-
Notifications
You must be signed in to change notification settings - Fork 26.8k
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
[Impeller] use varying interpolation to compute linear gradients. #148651
Comments
For conical gradients we can do triangle wedges divided from a covering circle. Two point conical still 🤷 |
If using varying interpolation for mapping gradient colors, the effect transform would need to be applied to the cover rectangle geometry in order to map the colors properly, which would involve handling some corner cases with fallback.
But I think this can be a great approach when limited to effect transforms that are translation+rotation only (probably the overwhelmingly common case). For the initial pass of this, I would strongly recommend limiting scope for the initial implementation to:
To implement this, you won't be able to use the We're already a little bit callback crazy, but one possibility would be to add an optional callback to |
#53166) Create a fast gradient variant that assumes vertex interpolation is sufficient to compute the gradient color. This follows the approximate design from flutter/flutter#148651 This is only implemented for draws that are exactly horizontal or vertical gradients on a rectangle with an identity effect transform. This could be easily extended to all horizontal or vertical gradients by extending the drawGeometry call to make the cover rect customizable. Handling diagonal gradients and effect transforms is more work but feasible. this is sufficient to make flutter/flutter#148496 about 2x as fast, at least for me. Applications with different gradients will not yet benefit.
Part of flutter/flutter#148651 allows the fast gradient to apply to non-rect shapes where the start/end are still on the coverage rect. Goldens are slightly different but seem close enough?
See #148496 for a motivating example.
Background
Impeller's current gradient shader is quite slow (I know I wrote it), due to accomidating an extremely flexible gradient API into a single shader. Flutter has approximately the same API as https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient , which supports an arbitrary number of colors, color stops, potentially inset in the rendered geometry. Skia generates new gradient shaders at runtime based on the number of colors and tile mode, but this isn't a viable option with Impeller. We can do better than this anyway.
Overview
Use varying interpolation to compute the gradient colors for us. This requires us to potentially change the vertices associated with a draw so that there is sufficient information to perform the interpolation.
For example, suppose we are applying a linear gradient with 2 colors to a rectangle (red and blue), with the start and end at the top center and bottom center (respectively).
It is apparent that assigning red to varyings for vertices 1, 2 and blue to vertices 3, 4 will lead to exactly the interpolation needed by the gradient. Thus the fragment stage can be simplified to computing the premultiplied color (our gradients interpolate in unpremul space) and applying the ordered dithering.
What if we have more than two colors?
When there are more than two colors, we must insert additional vertices for our interpolation. In the case of 3 colors (red, green, blue) and the same rectangle, we need to insert new vertices to assign the green color 2, at the correct location determined by the color stop value (if unspecified 0.5). This is equivalent to dividing the rectangle in half
In this case we have red at vertices 1, 2, green at 3, 4, and blue at 5, 6. This process can be repeated for an arbitrary number of divisions, provided that S/E are located on the coverage rect. The division point can be computed by using the stop value to lerp start and end (
point_i = (1.0 - t_i) * start + t_i * end
).What if the S/E are located elsewhere?
IF the start/end are horizontal, then I believe we could handle this by rotating the shape such that it was vertical, computing the triangles, and then rotating back. Likewise if the directions are reversed - these are all 90, 180, 270 degree rotations.
If The start and end are diagonal in some way, things get more interesting. We have a few possibilities here, but I haven't thought through all of the options.
What if the S/E are inside the rectangle
Then we need to handle the tile mode, which computes a value of t similar to a texture sampler. For example, t may clamp, or repeat, or be mirrored. I think the best option would be to expand the geometry and compute t for each stop. If its decal though, we can avoid this.
What if the S/E are outside the rectangle
Assuming the existing conditions are met, we treat the starting point on the rectangle as whatever the value of
t
would be.What if the shape isn't a rectangle
With StC, all shapes can be rectangles so this isn't a problem. Draw the actual shape with the stencil buffer and then the coverage rect can be substituted for the geometry rect.
What about other gradient types?
These are less common. I think we could do concentric circles for the radial gradient. Conical/Sweep 🤷
The text was updated successfully, but these errors were encountered: