Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable property functions for {text,icon}-{color,halo-color} #4186

Merged
merged 4 commits into from
Feb 7, 2017

Conversation

anandthakker
Copy link
Contributor

@anandthakker anandthakker commented Feb 2, 2017

Adds property function support for:

  • {text,icon}-color
  • {text,icon}-halo-color

Refs #2730

Checklist

  • briefly describe the changes in this PR
  • write tests for all new functionality
  • post benchmark scores
  • manually test the debug page

@Scarysize
Copy link

Nice! We implemented this earlier this week in our fork (for sdf icons).

Just a thought going through the Symbol Bucket code: Would it make sense to split up text (glyph) and icon rendering completely? The bucket code contains a lot of if(isText/isIcon) statements, bucket methods receive a gazillion parameters because they handle both types and even draw_symbols needs to differentiate between the two types.

@anandthakker
Copy link
Contributor Author

This is working for the simple text-color case, but not yet working for icon-color for some reason (in the added render test, both icons are coming up red) -- troubleshooting now.

Also, the existing approach to halo rendering causes a complication here. We currently re-use the same program to render the text/icon halo, and then the text/icon itself. Introducing property functions for paint props complicates this, because now we need a way to switch which property--e.g. text-color or text-halo-color--is being used for color. I'm really not sure where/how this ought to happen. @jfirebaugh @lucaswoj thoughts?

@anandthakker
Copy link
Contributor Author

@Scarysize good question! I'm 💯 with you on those crazy long parameter lists. I definitely think this part of the codebase could use a solid refactor, and splitting up text/icon might be a good route to take.... I'm personally not very familiar w/ the history of the symbol stuff, though, so there might be lurking subtleties that I'm not aware of.

@lucaswoj
Copy link
Contributor

lucaswoj commented Feb 2, 2017

I'm really not sure where/how this ought to happen.

The simplest design I can think of is adding a new parameter to VertexArrayObject#bind and Buffer#setVertexAttribPointers.

This method is responsible for setting up the connections between shader attributes and struct array members.

By default there is a 1:1 mapping of shader attributes to struct array members. We could solve this problem by making that relationship a little looser, allowing callers of Buffer#setVertexAttribPointers to define some kind of aliasing.

segment.vaos[layer.id].bind(gl, program, buffers.layoutVertexBuffer, buffers.elementBuffer, null, segment.vertexOffset, {
    attributeAliases: {
        'color': 'halo-color'
    }
});

Happy to voice on this if you have any follow-up questions. 😄

@jfirebaugh
Copy link
Contributor

Other options:

  • Bind both colors, add a uniform boolean that selects between them and an if statement in the shader.
  • Create separate shaders for halo and fill. Factor the common parts into reusable GLSL functions.
  • Figure out how to draw halo and fill in a single draw call.

@jfirebaugh
Copy link
Contributor

Figure out how to draw halo and fill in a single draw call.

Previously: #596. @lbud, how conclusive was the outcome there? Is there no way to overcome the overlap issue?

@anandthakker
Copy link
Contributor Author

Update: ruled out consolidating the halo and fill in a single call (see #596 ). Because @jfirebaugh indicated in chat that the attributeAliases approach would likely be difficult to carry over to -native, going to try binding both sets of attributes, along with a uniform switch so the shader can decide.

@anandthakker
Copy link
Contributor Author

benchmark master fac6358 dds-symbol-paint 7872482
map-load 215 ms 135 ms
style-load 115 ms 109 ms
buffer 1,163 ms 1,187 ms
fps 60 fps 60 fps
frame-duration 5.1 ms, 0% > 16ms 5.3 ms, 0% > 16ms
query-point 0.97 ms 1.05 ms
query-box 70.54 ms 64.17 ms
geojson-setdata-small 6 ms 5 ms
geojson-setdata-large 145 ms 174 ms

@@ -358,7 +366,7 @@ class SymbolBucket {
// the buffers for both tiles and clipped to tile boundaries at draw time.
const addToBuffers = inside || mayOverlap;
this.addSymbolInstance(anchor, line, shapedTextOrientations, shapedIcon, this.layers[0],
addToBuffers, this.symbolInstancesArray.length, this.collisionBoxArray, feature.index, feature.sourceLayerIndex, this.index,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per conversation w @mollymerp, I replaced the (unused) index field with a featureIndex, which provides a mapping back to from the symbol instance to the feature it came from. This allows us to retrieve the feature's properties down in place().

This will likely need a bit of reworking when we return to the cross-source symbol placement quest, but since we don't know exactly what that'll look like, I think we should cross that bridge when we come to it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any context on how the index field became unused?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucaswoj I believe it never was used, but rather was there on the hypothesis that it might be needed during cross-source placement. @mollymerp does that sound right to you?

@anandthakker
Copy link
Contributor Author

@jfirebaugh @lucaswoj k -- this is still a subset of the symbol paint properties (list is here #2730 ), but it'd be good to go ahead and get some 👀 on it, esp. since this is my first foray into this area of the codebase.

lowp float dist = texture2D(u_texture, v_tex).a;
lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a;
lowp float gamma = u_gamma * v_gamma_scale;
lowp float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * fade_alpha;

gl_FragColor = u_color * (alpha * u_opacity);
gl_FragColor = color_to_use * (alpha * u_opacity);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of color, halo_color, and color_to_use how about fill_color, halo_color, and color?

Copy link
Contributor Author

@anandthakker anandthakker Feb 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. I'd foolishly assumed I needed to stick with the attribute-name <=> paint-property-name-sans-layer-type-prefix mapping, but doing #4186 (review) will free us up to improve the naming here.

@@ -210,7 +210,8 @@ function getPaintAttributeValue(attribute, layer, globalProperties, featurePrope
}

function normalizePaintAttribute(attribute, layer) {
const name = attribute.property.replace(`${layer.type}-`, '').replace(/-/g, '_');
const layerTypePrefix = layer.type === 'symbol' ? /text-|icon-/ : `${layer.type}-`;
const name = attribute.property.replace(layerTypePrefix, '').replace(/-/g, '_');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you think of any cases where this magic behaviour might bite us later? I'd be interested to see if there was a way to specify this behaviour explicitly within SymbolBucket instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you think of any cases where this magic behaviour might bite us later?

@lucaswoj not offhand, but I'm 💯 with you in being uneasy / less than satisfied with this magic.

I'd be interested to see if there was a way to specify this behaviour explicitly within SymbolBucket instead.

👍 I'm thinking we can allow the bucket to explicitly define attribute.name, and then have normalizePaintAttribute just use that if it's there, instead of computing the attribute name from the paint property name.

@anandthakker
Copy link
Contributor Author

@lucaswoj addressed your points re: bad magic and naming by explicitly setting the attribute names in paintAttributes.

When attribute.name isn't set explicitly, I'm personally okay with keeping the pre-existing, slightly-less-magical convenience where we construct attribute name based on the property name. It seems harmless, particularly since there's now a way to override it.

@anandthakker anandthakker changed the title Enable property functions for {text,icon}-color Enable property functions for {text,icon}-{color,halo-color} Feb 3, 2017
@@ -184,6 +192,12 @@ class SymbolBucket {
// It's better to place labels on one long line than on many short segments.
this.features = mergeLines(this.features);
}

// set up this mapping to allow for constant-time lookups in place()
this.featuresByIndex = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than create a separate index, can we attach a reference to the feature properties to the symbolInstance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfirebaugh the symbolInstance is being stored (at populate() time) in a StructArray (this.symbolInstancesArray), so we can't attach a reference.

Granted, (a) we're not yet using symbolInstancesArray on a different thread, and (b) once we do (for cross-source placement), we'll have to find a different way to handle the paint attributes, since presumably whichever thread gets the array won't have the property data from the source.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's adjust/replace symbolInstancesArray based on the requirements here for data-driven properties. We can revisit its use in cross-source symbol placement when we're ready to work on that feature again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure; if we're doing that I'd say we should go with 'replace' over 'adjust': adjusting it to these requirements would mean changing it from a StructArray, thus making it non-transferrable and no longer suitable for the cross-source placement project... if that's going to be the case, I don't see much reason to hold on to this state (symbolInstancesArray, symbolQuadsArray) at the WorkerTile level.

I'm good to go ahead with this, but I just want to flag that dropping symbolInstancesArray will make the changes in this PR a notch more invasive, removing some stuff that we may end up having to partially redo later when we tackle cross-source placement -- but it's probably true that even if we left it in place, making it work with data-driven styling would anyway have caused significant changes to what's there now.

Copy link
Contributor Author

@anandthakker anandthakker Feb 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prep work for this is done in #4217. Tomorrow AM I'll rebase on that, which will allow us to just place a direct reference to featureProperties onto each symbolInstance as you suggested.

Copy link
Contributor

@lucaswoj lucaswoj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with this! Deferring to @jfirebaugh for the final 👍

@anandthakker
Copy link
Contributor Author

@lucaswoj @jfirebaugh rebased onto the refactor in #4217, so that one should get merged first, but after that's in I think this is ready. Compare this PR's changes only at refactor-symbol-instances...dds-symbol-paint

@anandthakker anandthakker merged commit 8f58879 into master Feb 7, 2017
@anandthakker anandthakker deleted the dds-symbol-paint branch February 7, 2017 20:21
mapsam pushed a commit that referenced this pull request Feb 8, 2017
* Enable property functions for {text,icon}-color

* Update expected images for {icon,text}-color render tests

* Add render tests for {icon,text}-halo-color property functions

* Drop unneeded feature map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants