SSBO-based gradients in UberSDF - #192962
Conversation
9aaea2a to
7d04d7b
Compare
7d04d7b to
bae52c5
Compare
There was a problem hiding this comment.
github PR review UI is bad at detecting that this is mostly copied from uber_sdf.frag.
Here's the full diff reported by git diff -C --find-copies-harder master..HEAD:
diff --git a/engine/src/flutter/impeller/entity/shaders/uber_sdf.frag b/engine/src/flutter/impeller/entity/shaders/uber_sdf_common.glsl
similarity index 93%
copy from engine/src/flutter/impeller/entity/shaders/uber_sdf.frag
copy to engine/src/flutter/impeller/entity/shaders/uber_sdf_common.glsl
index 648b72ef18c..d24cd9ae881 100644
--- a/engine/src/flutter/impeller/entity/shaders/uber_sdf.frag
+++ b/engine/src/flutter/impeller/entity/shaders/uber_sdf_common.glsl
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// Shared body of the UberSDF shaders, included by `uber_sdf.frag` and
+// `uber_sdf_ssbo.frag`. Each entrypoint supplies only `getGradientColor()`.
+
precision mediump float;
#include <impeller/color.glsl>
@@ -14,8 +17,6 @@ precision mediump float;
#include "sdf_functions.glsl"
#include "sdf_utils.glsl"
-uniform sampler2D color_source_sampler;
-
uniform FragInfo {
// FragInfo fields are sorted by size (vec4 -> vec2 -> float) to optimize
// uniform register usage.
@@ -102,13 +103,23 @@ uniform FragInfo {
/// 2: Mirror
/// 3: Decal
float tile_mode;
- /// Half the size of a single gradient texel in normalized texture
- /// coordinates along the gradient ramp (x axis).
- float half_texel;
/// Inverse gradient length:
/// - Linear gradient: 1.0 / dot(delta, delta)
/// - Radial gradient: 1.0 / radius
float inv_gradient_length;
+
+ // --- Variant-Specific Gradient Parameters ---
+ // Exactly one of these is used by any given entrypoint; the other is unset.
+
+ /// Half the size of a single gradient texel in normalized texture
+ /// coordinates along the gradient ramp (x axis).
+ ///
+ /// Only used by the gradient ramp texture variant.
+ float half_texel;
+ /// The number of gradient stops in the color data storage buffer.
+ ///
+ /// Only used by the storage buffer variant.
+ float colors_length;
}
frag_info;
@@ -116,6 +127,13 @@ out vec4 frag_color;
highp in vec2 v_position;
+// Looks up the gradient color at gradient position `t`.
+//
+// `t` is untiled, so implementations apply frag_info.tile_mode themselves.
+// Returns an unpremultiplied color, ignoring frag_info.color.a, which the
+// calling getColor() applies below.
+vec4 getGradientColor(float t);
+
// Gets the color to use at v_position based on frag_info properties.
vec4 getColor() {
vec4 color;
@@ -134,9 +152,7 @@ vec4 getColor() {
t = IPComputeRadialGradientT(v_position, frag_info.gradient_coords.xy,
frag_info.inv_gradient_length);
}
- vec4 gradient_color = IPSampleLinearWithTileMode(
- color_source_sampler, vec2(t, 0.5), vec2(frag_info.half_texel, 0.5),
- frag_info.tile_mode, vec4(0.0));
+ vec4 gradient_color = getGradientColor(t);
color = vec4(gradient_color.rgb, gradient_color.a * frag_info.color.a);
}
return color;bae52c5 to
4720fb8
Compare
|
Golden changes: https://flutter-gold.skia.org/search?issue=192962&crs=github&patchsets=1&corpus=flutter Many of the gradient goldens have small changes. All the changes seem visually imperceptible. |
There was a problem hiding this comment.
Code Review
This pull request adds support for rendering UberSDF shaders using Shader Storage Buffer Objects (SSBO) instead of pre-baked gradient ramp textures when supported by the device. It refactors the UberSDF fragment shader into a shared common file, introduces an SSBO-specific fragment shader, and updates pipeline creation, canvas setup, and rendering logic to conditionally use the SSBO path. The review feedback recommends querying device capabilities consistently, encapsulating the new rendering helper methods as private in UberSDFContents, and defensively ensuring the gradient color stops vector is never empty before binding it to prevent potential graphics backend crashes.
|
Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change). If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
|
Golden file changes are available for triage from new commit, Click here to view. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
gaaclarke
left a comment
There was a problem hiding this comment.
I'd like to see a golden test for this. I suspect what will happen is that the golden tests will be swapped over to the SSBO version. We should clone some of the gradient tests to artificially sets the capabilities to say we don't have SSBO.
Otherwise looking good.
We have SSBO vs texture goldens based on the backend that runs the golden test. The golden test running on the MetalSDF backend supports SSBO and exercises the SSBO rendering path. The golden test running on the OpenGLESSDF backend does not support SSBO and exercises the texture-based rendering path. (If we do update OpenGLES to support SSBO in the future, we'll need another approach to ensure we have golden tests for the texture based path.) This approach of using the different backends to test different supported capabilities is the same as what the existing non-uberSDF gradient shaders do to golden test their different variants. |
I decided to do this and clone some of SSBO tests to non-SSBO versions. This way the tests are more explicit about SSBO vs non-SSBO, instead of implicitly relying on backend capabilities which is harder to follow and may change. I artificially set the capability in a similar way to how the existing |
|
I'm going to merge main since the other PR experiencing that cipd problem seems resolved now. If this passes we can close it out and keep an eye open for it in the future. |
walley892
left a comment
There was a problem hiding this comment.
Mostly good. A few notes
| return RenderTexture(renderer, entity, pass); | ||
| } | ||
|
|
||
| bool UberSDFContents::RenderTexture(const ContentContext& renderer, |
There was a problem hiding this comment.
Nit: Can we rename this function to RenderWithTextureGradient and the other to RenderWithSSBOGradient?
The way it's named right now, it sounds like this could be a thing that renders to a texture or renders the contents of a texture to a rect or something
There was a problem hiding this comment.
Follows the existing gradient shaders. e.g. for linear:
| @@ -160,6 +161,7 @@ using TiledTexturePipeline = RenderPipelineHandle<TextureUvFillVertexShader, Til | |||
| using VerticesUber1Shader = RenderPipelineHandle<PorterDuffBlendVertexShader, VerticesUber1FragmentShader>; | |||
| using VerticesUber2Shader = RenderPipelineHandle<PorterDuffBlendVertexShader, VerticesUber2FragmentShader>; | |||
| using UberSDFPipeline = RenderPipelineHandle<CircleVertexShader, UberSdfFragmentShader>; | |||
There was a problem hiding this comment.
Nit: should this be renamed to UberSDFTexturePipeline or something similar?
There was a problem hiding this comment.
Follows the naming of the existing gradient shaders. e.g. for linear:
flutter/engine/src/flutter/impeller/entity/contents/pipelines.h
Lines 138 to 140 in 7a0b1cc
| FS::BindColorSourceSampler(pass, sampler_binding.texture, | ||
| sampler_binding.sampler); | ||
| FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info)); | ||
| pass.SetCommandLabel("UberSDF"); |
There was a problem hiding this comment.
Can we change this label to UberSDFTexture?
There was a problem hiding this comment.
Follows the naming of the existing gradient shaders. e.g. for linear:
Texture variant:
SSBO variant:
Uniform variant:
|
|
||
| /// The normalized position of each entry in `colors`, in ascending order. | ||
| /// | ||
| /// Always the same length as `colors`. |
There was a problem hiding this comment.
Always the same length as
colors.
I don't think there's an assert for this anywhere, I could be missing it. Should we add one?
There was a problem hiding this comment.
These fields are used here https://github.com/flutter/flutter/pull/192962/changes#diff-985034ed2ba76391d344554774ec0419b277508cfbd9979d7256a0045d7c9f57R207-R210, passed to CreateGradientColors.
CreateGradientColors does a check on their lengths:
There was a problem hiding this comment.
Could we do the following rename:
uber_sdf -> uber_sdf_texture or uber_sdf_texture_gradient. contains the implementation of the gradient value fetch based on texture.
uber_sdf_common -> uber_sdf. Contains the bulk of the logic.
There was a problem hiding this comment.
Follows the naming of the existing gradient shaders. e.g. for linear:
Texture variant: linear_gradient_fill.frag
SSBO variant: linear_gradient_ssbo_fill.frag
Uniform variant: linear_gradient_uniform_fill.frag
b-luk
left a comment
There was a problem hiding this comment.
Regarding the naming: I did consider whether the existing version should be named with a "texture" suffix. But I based this on the existing gradient shaders, where the texture version is the "base" version with no suffix, and the ssbo and uniform variants get a suffix. I replied to each comment with the existing naming patterns that I followed.
I put a high value consistency and following existing conventions in the codebase, so I followed the existing patterns even though I do agree with you that if I was naming them from scratch I would have given the texture variant a "texture" suffix.
I think I like the RenderTexture to RenderWithTextureGradient change, because that's private to uber_sdf_contents so consistency matters a little less And unlike the gradient-specific shaders which only deal with gradients, inside uber_sdf_contents a name like RenderWithTextureGradient gives helpful context on what the texture is specifically for.
The other renamings suggestions are public across our codebase though, so I think being consistent with the naming matters more than for the RenderTexture case.
Keeping in mind the existing naming patterns in the codebase, if you still think some or all of these should be renamed let me know and I'll rename them.
I think my preference is:
- Do the
RenderTexturetoRenderWithTextureGradient(andRenderSSBOtoRenderWithSSBOGradient) renaming - Keep the other names, for consistency with the existing naming conventions of the codebase
- File a cleanup issue to rename both uber_sdf and the existing gradient shaders to have a "texture" suffix for the texture variants. That way the naming stays consistent. Although I feel like this issue would be low priority so who knows if anyone will ever get to it.
|
|
||
| /// The normalized position of each entry in `colors`, in ascending order. | ||
| /// | ||
| /// Always the same length as `colors`. |
There was a problem hiding this comment.
These fields are used here https://github.com/flutter/flutter/pull/192962/changes#diff-985034ed2ba76391d344554774ec0419b277508cfbd9979d7256a0045d7c9f57R207-R210, passed to CreateGradientColors.
CreateGradientColors does a check on their lengths:
| return RenderTexture(renderer, entity, pass); | ||
| } | ||
|
|
||
| bool UberSDFContents::RenderTexture(const ContentContext& renderer, |
There was a problem hiding this comment.
Follows the existing gradient shaders. e.g. for linear:
| @@ -160,6 +161,7 @@ using TiledTexturePipeline = RenderPipelineHandle<TextureUvFillVertexShader, Til | |||
| using VerticesUber1Shader = RenderPipelineHandle<PorterDuffBlendVertexShader, VerticesUber1FragmentShader>; | |||
| using VerticesUber2Shader = RenderPipelineHandle<PorterDuffBlendVertexShader, VerticesUber2FragmentShader>; | |||
| using UberSDFPipeline = RenderPipelineHandle<CircleVertexShader, UberSdfFragmentShader>; | |||
There was a problem hiding this comment.
Follows the naming of the existing gradient shaders. e.g. for linear:
flutter/engine/src/flutter/impeller/entity/contents/pipelines.h
Lines 138 to 140 in 7a0b1cc
| FS::BindColorSourceSampler(pass, sampler_binding.texture, | ||
| sampler_binding.sampler); | ||
| FS::BindFragInfo(pass, data_host_buffer.EmplaceUniform(frag_info)); | ||
| pass.SetCommandLabel("UberSDF"); |
There was a problem hiding this comment.
Follows the naming of the existing gradient shaders. e.g. for linear:
Texture variant:
SSBO variant:
Uniform variant:
There was a problem hiding this comment.
Follows the naming of the existing gradient shaders. e.g. for linear:
Texture variant: linear_gradient_fill.frag
SSBO variant: linear_gradient_ssbo_fill.frag
Uniform variant: linear_gradient_uniform_fill.frag
gaaclarke
left a comment
There was a problem hiding this comment.
LGTM. I think @walley892 raises some good points on naming. It's best to keep the names consistent for now. We can reevaluate them as a group if we want to change it to make it more clear. The current system is more an artifact of how the code evolved, not really a conscious design decision.
| /// | ||
| /// Only the Metal playground implements `SetCapabilities`, so `*NoSSBO` tests | ||
| /// must skip every other backend. | ||
| class ScopedForceNoSSBO { |
There was a problem hiding this comment.
nit: I think a more robust way may be to have different test suites for this with this set in the SetUp and TearDown methods. I'm not sure if that's something easy to setup with our current system though.
|
Golden file changes are available for triage from new commit, Click here to view. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
Moves most of the logic of uber_sdf.frag to uber_sdf_common.glsl. New uber_sdf.frag and uber_sdf_ssbo.frag shaders import uber_sdf_common.glsl and implement `getGradientColor()` with a texture-based or SSBO-based gradient implementation. Gradient SSBO logic based off the existing SSBO gradient shaders like https://github.com/flutter/flutter/blob/b138043fc75c1270e1bd9f1fa0a5c1299e86e638/engine/src/flutter/impeller/entity/shaders/gradients/linear_gradient_ssbo_fill.frag Note that for GLES, SSBO is currently not supported. https://github.com/flutter/flutter/blob/b138043fc75c1270e1bd9f1fa0a5c1299e86e638/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.cc#L277-L279 This means that for Windows and Linux, which currently always have a GLES backend, this change does not do anything. This is a prerequisite for addressing flutter#190401. This improves UberSDF gradient rendering when SSBO is supported, but not on Windows. The next step to address flutter#190401 is to make Windows support SSBO: flutter#193162 Tested this with the "Scrolling basic LinearGradient" benchmark app from flutter#190401. I ran it 3 times, on my Mac. The raster time goes down significantly, so that it is on par with the solid color time. ## On Mac (my local dev machine, M3 MacBook Pro): ### Before: | | Avg raster p50 | |--|--| | Solid | 1.250ms | | Gradient | 2.596ms | <details> <summary> Full results </summary> ``` Note: This says "Windows" because that's hard-coded in the benchmark app. But this was run on my Mac. flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=1.085/1.661ms | raster p50/p90/p95/p99/max=1.278/1.701/1.764/1.857/1.999ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.460/2.101ms | raster p50/p90/p95/p99/max=2.639/3.182/3.325/3.727/4.102ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=0.904/1.400ms | raster p50/p90/p95/p99/max=1.232/1.635/1.752/1.918/2.125ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.490/2.262ms | raster p50/p90/p95/p99/max=2.686/3.368/3.615/3.863/4.256ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=0.903/1.426ms | raster p50/p90/p95/p99/max=1.240/1.655/1.727/1.879/2.097ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.176/2.259ms | raster p50/p90/p95/p99/max=2.463/3.069/3.270/3.706/3.934ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ ``` </details> ### After: | | Avg raster p50 | |--|--| | Solid | 1.271ms | | Gradient | 1.286ms | <details> <summary> Full results </summary> ``` Note: This says "Windows" because that's hard-coded in the benchmark app. But this was run on my Mac. flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=1.290/1.816ms | raster p50/p90/p95/p99/max=1.365/1.748/1.823/1.953/1.990ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.523/2.281ms | raster p50/p90/p95/p99/max=1.286/1.457/1.528/1.722/1.812ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=0.957/1.108ms | raster p50/p90/p95/p99/max=1.192/1.354/1.385/1.462/1.854ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.572/3.112ms | raster p50/p90/p95/p99/max=1.263/1.455/1.542/1.702/1.859ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=1.044/1.657ms | raster p50/p90/p95/p99/max=1.256/1.721/1.770/1.892/1.919ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.925/3.021ms | raster p50/p90/p95/p99/max=1.308/1.536/1.589/1.722/1.905ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ ``` </details> ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant in-code documentation (doc comments with `///`). - [x] If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [flutter/website]: https://github.com/flutter/website [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: gaaclarke <30870216+gaaclarke@users.noreply.github.com>
Moves most of the logic of uber_sdf.frag to uber_sdf_common.glsl. New uber_sdf.frag and uber_sdf_ssbo.frag shaders import uber_sdf_common.glsl and implement `getGradientColor()` with a texture-based or SSBO-based gradient implementation. Gradient SSBO logic based off the existing SSBO gradient shaders like https://github.com/flutter/flutter/blob/b138043fc75c1270e1bd9f1fa0a5c1299e86e638/engine/src/flutter/impeller/entity/shaders/gradients/linear_gradient_ssbo_fill.frag Note that for GLES, SSBO is currently not supported. https://github.com/flutter/flutter/blob/b138043fc75c1270e1bd9f1fa0a5c1299e86e638/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.cc#L277-L279 This means that for Windows and Linux, which currently always have a GLES backend, this change does not do anything. This is a prerequisite for addressing flutter#190401. This improves UberSDF gradient rendering when SSBO is supported, but not on Windows. The next step to address flutter#190401 is to make Windows support SSBO: flutter#193162 Tested this with the "Scrolling basic LinearGradient" benchmark app from flutter#190401. I ran it 3 times, on my Mac. The raster time goes down significantly, so that it is on par with the solid color time. ## On Mac (my local dev machine, M3 MacBook Pro): ### Before: | | Avg raster p50 | |--|--| | Solid | 1.250ms | | Gradient | 2.596ms | <details> <summary> Full results </summary> ``` Note: This says "Windows" because that's hard-coded in the benchmark app. But this was run on my Mac. flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=1.085/1.661ms | raster p50/p90/p95/p99/max=1.278/1.701/1.764/1.857/1.999ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.460/2.101ms | raster p50/p90/p95/p99/max=2.639/3.182/3.325/3.727/4.102ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=0.904/1.400ms | raster p50/p90/p95/p99/max=1.232/1.635/1.752/1.918/2.125ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.490/2.262ms | raster p50/p90/p95/p99/max=2.686/3.368/3.615/3.863/4.256ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=0.903/1.426ms | raster p50/p90/p95/p99/max=1.240/1.655/1.727/1.879/2.097ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.176/2.259ms | raster p50/p90/p95/p99/max=2.463/3.069/3.270/3.706/3.934ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ ``` </details> ### After: | | Avg raster p50 | |--|--| | Solid | 1.271ms | | Gradient | 1.286ms | <details> <summary> Full results </summary> ``` Note: This says "Windows" because that's hard-coded in the benchmark app. But this was run on my Mac. flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=1.290/1.816ms | raster p50/p90/p95/p99/max=1.365/1.748/1.823/1.953/1.990ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.523/2.281ms | raster p50/p90/p95/p99/max=1.286/1.457/1.528/1.722/1.812ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=0.957/1.108ms | raster p50/p90/p95/p99/max=1.192/1.354/1.385/1.462/1.854ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.572/3.112ms | raster p50/p90/p95/p99/max=1.263/1.455/1.542/1.702/1.859ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ flutter: === Windows linear-gradient comparison === flutter: mode=profile speed=1400px/s flutter: Solid color | frames=240 fps=59.75 | ui p50/p95=1.044/1.657ms | raster p50/p90/p95/p99/max=1.256/1.721/1.770/1.892/1.919ms | raster_over_budget=0/240 (0.0%) flutter: Linear gradient | frames=240 fps=59.75 | ui p50/p95=1.925/3.021ms | raster p50/p90/p95/p99/max=1.308/1.536/1.589/1.722/1.905ms | raster_over_budget=0/240 (0.0%) flutter: ================================================ ``` </details> ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant in-code documentation (doc comments with `///`). - [x] If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [flutter/website]: https://github.com/flutter/website [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: gaaclarke <30870216+gaaclarke@users.noreply.github.com>
…#13060) Manual roll Flutter from 4fcd90be0045 to c5a061b18fa2 (222 revisions) Manual roll requested by quncheng@google.com flutter/flutter@4fcd90b...c5a061b 2026-09-28 brackenavaron@gmail.com [cross imports] navigator_test.dart (flutter/flutter#190954) 2026-09-28 154381524+flutteractionsbot@users.noreply.github.com Revert: Reland: Only render views that need to be rendered (flutter/flutter#193360) 2026-09-28 codefu@google.com ci(bringup): android_intent_security_test is green (flutter/flutter#193469) 2026-09-28 154381524+flutteractionsbot@users.noreply.github.com Sync CHANGELOG.md from stable (flutter/flutter#193020) 2026-09-28 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from ukukV5lEkKabtOATk... to QdgqP02_cYpRQQQNN... (flutter/flutter#193454) 2026-09-28 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#193460) 2026-09-28 victorsanniay@gmail.com Un-nest sceneBuildDuration and windowRenderDuration in web SceneBuilderRecorder (flutter/flutter#193260) 2026-09-28 zarah@google.com Fix unresolved doc comment references in dev/integration_tests and dev/benchmarks (flutter/flutter#193433) 2026-09-28 engine-flutter-autoroll@skia.org Roll Packages from e55e7ac to ba0364a (9 revisions) (flutter/flutter#193450) 2026-09-28 bkonyi@google.com [tool] Require explicit dependency injection for FlutterDevice and FlutterDevice.create (flutter/flutter#192830) 2026-09-28 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#193442) 2026-09-28 dacoharkes@google.com [flutter_tools] Include data assets from hooks when pubspec.yaml is empty (flutter/flutter#193434) 2026-09-28 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#193438) 2026-09-28 zarah@google.com Fix unresolved doc comment references in Material and Cupertino (flutter/flutter#193283) 2026-09-28 zarah@google.com Fix doc references to Material and Cupertino in the widgets library and its tests (flutter/flutter#193334) 2026-09-26 kevmoo@users.noreply.github.com wasm: enforce WasmGC opt-in capability checks and Firefox < 147 guard (flutter/flutter#193180) 2026-09-26 43054281+camsim99@users.noreply.github.com [Android] Refuse external setters of engine entrypoint and cached engine arguments via `Intent`s (flutter/flutter#190249) 2026-09-26 1961493+harryterkelsen@users.noreply.github.com [web] Unskip TextPainter.getWordBoundary test (flutter/flutter#193378) 2026-09-26 bkonyi@google.com Specify non-obvious types in pattern variable declarations (flutter/flutter#192632) 2026-09-26 30870216+gaaclarke@users.noreply.github.com Removes feedback loop from advanced filters without offscreen msaa and framebufferfetch (flutter/flutter#193306) 2026-09-26 1961493+harryterkelsen@users.noreply.github.com [web] Unskip 8 passing image tests in painting and rendering (flutter/flutter#193377) 2026-09-26 154381524+flutteractionsbot@users.noreply.github.com Revert: Allow resetting test invariants in `addTearDown` (flutter/flutter#193383) 2026-09-25 bkonyi@google.com [tool] Migrate CoverageCollector to modular dependency injection (flutter/flutter#192924) 2026-09-25 zarah@google.com Fix more unresolved doc comment references (flutter/flutter#193336) 2026-09-25 dkwingsmt@users.noreply.github.com Allow resetting test invariants in `addTearDown` (flutter/flutter#192082) 2026-09-25 jhy03261997@gmail.com [a11y] Add a semantics role for slider (flutter/flutter#193324) 2026-09-25 stuartmorgan@google.com Fix plugin tests after Pigeon plugin template changes (flutter/flutter#193302) 2026-09-25 jesswon@google.com [Android 17] Bump Standard Test Apps in flutter/flutter to AGP 9.3.1 (flutter/flutter#193263) 2026-09-25 97480502+b-luk@users.noreply.github.com SSBO-based gradients in UberSDF (flutter/flutter#192962) 2026-09-25 robert.ancell@canonical.com [Linux] Handle FlView being destroyed before rendering is complete. (flutter/flutter#193268) 2026-09-25 bkonyi@google.com [flutter_tools] Handle Windows reserved characters in test target path (flutter/flutter#191900) 2026-09-25 sneurlax@gmail.com docs(tools): nit: say hook/build.dart in CMake native assets comment (flutter/flutter#192205) 2026-09-25 kevmoo@users.noreply.github.com [web] Enable Firefox Skwasm UI CI suites, configure COI configs, and fail fast on loader rejections (flutter/flutter#193187) 2026-09-25 jesswon@google.com [Android 17] Bumped Engine Dependencies to 9.3.1 (flutter/flutter#193265) 2026-09-25 kevmoo@users.noreply.github.com [web] Omit group role on menu scrollables and assign region role to named routes (flutter/flutter#192965) 2026-09-25 engine-flutter-autoroll@skia.org Roll Skia from 8eedeed98e79 to f441ca223b2b (5 revisions) (flutter/flutter#193353) 2026-09-25 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from EbPpoJW-Lnsu-8dyZ... to ukukV5lEkKabtOATk... (flutter/flutter#193326) 2026-09-25 engine-flutter-autoroll@skia.org Roll Packages from 431ea69 to e55e7ac (6 revisions) (flutter/flutter#193351) 2026-09-25 139053348+shikharish@users.noreply.github.com Support DynamicLibrary.codeAsset in Flutter (flutter/flutter#188947) 2026-09-25 engine-flutter-autoroll@skia.org Roll Dart SDK from 75d9e87d4e3d to 0e7642b85457 (1 revision) (flutter/flutter#193346) 2026-09-25 15619084+vashworth@users.noreply.github.com Exclude iOS PRs from macOS link (flutter/flutter#193315) 2026-09-25 engine-flutter-autoroll@skia.org Roll Skia from 6357608543ec to 8eedeed98e79 (4 revisions) (flutter/flutter#193341) 2026-09-25 engine-flutter-autoroll@skia.org Roll Dart SDK from d3aedb186aab to 75d9e87d4e3d (3 revisions) (flutter/flutter#193335) 2026-09-25 nickolasdeluca@live.com Cache the paint offset adjusted line metrics in TextPainter (flutter/flutter#191223) ...
…lutter#192124, flutter#192962) UberSDF in this fork cannot shade a color source, so every antialiased gradient rect, rrect, oval or circle (Avio's Shell and greeter run with use_sdfs) was blended through a white SDF mask: two snapshots and a blend target per draw, plus a deferred coverage pass in externalLinearBackdrop mode. Upstream fixed this in the owning layer: fdb1d09 (flutter#192124) shades linear and radial gradients inside UberSDF and 419f9dd (flutter#192962) adds the storage-buffer variant used on Vulkan, so no ramp texture is made per draw. Both upstream commits sit on intermediate UberSDF changes this fork does not carry (uniform reordering, rect-path derivatives, stroke pixel size, which would change other SDF output), and Avio's UberSDF carries patch 45's coverage fields, so this is an adapted backport in one commit, not literal cherry-picks: - uber_sdf.frag is split into uber_sdf_common.glsl (shape SDFs, getColor, main) plus uber_sdf.frag (ramp texture) and uber_sdf_ssbo.frag (gradient stops in a storage buffer, the same math as the SSBO gradient shaders). Wherever the backend supports SSBOs (Vulkan), every UberSDF draw uses the SSBO variant, as upstream does. - UberSDFParameters gains GradientParameters; UberSDFContents renders both variants from one FragInfo setup; color filters wrap on the GPU for a gradient, and a gradient is never a background color. - Canvas::AddRenderSDFEntityToCurrentPass shades a linear or radial gradient whose local matrix (with the inverse shape transform) is a similarity. Conical and sweep gradients, images and runtime effects keep the masked kSrcIn path patch 52 slims. Patch 52's no-mask rect still comes first. - A gradient shaded in UberSDF takes the coverage transfer in the shader; defer_coverage no longer includes the color source in that case. Avio deltas against upstream (decision D3, accepted on the condition that Avio's crisp look stays): - Edges keep the old response: a gradient's SDF alpha uses the light-foreground gamma correction of the white mask it was blended through before, not the gradient's luma; the SSBO variant dithers like the SSBO gradient shaders, and like the masked composite it dithers the gradient color before coverage scales it, so a pixel the shape does not cover (the AA band's outside, the quad corners around a rounded shape) stays exactly transparent and srcOver adds nothing to what lies underneath. Upstream does not dither in UberSDF. - Linear gradients also require a similarity matrix (upstream checks only affinity); mapping end points through a shear or non-uniform scale does not map the gradient's field. - flutter#191925 and flutter#191980 are not brought in: the ramp-texture variant uses the fork's current GradientData, and Avio's SDF users take the SSBO variant. - malioc.json is not updated. Nothing else on the branch depends on this commit; its tests use patch 52's Playground::EnsureContextUsesSDFs. Reverting it alone restores the masked path exactly. Tests: - UberSDFContentsTest.ApplyColorFilterWithGradient and AsBackgroundColorGradientReturnsNullopt (from upstream; red as compile failures on the base: GradientParameters did not exist). - On every playground that renders with SDFs (Playground:: EnsureContextUsesSDFs: Vulkan with SDFs enabled, which runs the storage-buffer variant Avio ships, and the OpenGL ES and Metal SDF backends, which run the ramp-texture variant; plain backends skip): AiksTest.SdfLinearGradientRectAllocatesNoOffscreen (expected red on the base: three offscreens), SdfRadialGradientCircleUnderNonUniformScale- AllocatesNoOffscreen, UnsupportedColorSourceStillBlends (a sweep gradient keeps exactly three offscreens), and SdfGradientEdgesMatchMaskedComposite: the pixel guard the user asked for. It renders rect, rounded rect, oval and circle with an opaque dark-to-light gradient and a translucent glass sheen, in platformDefault and externalLinearBackdrop, once shaded in UberSDF and once through the old masked composite (the same horizontal gradient with a y-scale local matrix, which leaves its field unchanged but routes it through the masked path). Every pixel, corners and antialiased edges included, must hold alpha within 2/255 and color within 2/255 (ramp texture, no dither) or 5/255 (storage buffer: the composite dithered in its snapshot's grid, then quantized once more); every pixel the composite leaves fully transparent must stay exactly zero. Green by construction on the base (both renders take the masked path there); after this patch it fails if edges, corners, glass coverage or uncovered pixels drift. - SdfGradientLeavesUncoveredQuadPixelsTransparent: the quad corners around a gradient circle are exactly zero in both coverage modes, opaque and glass. Red on the storage-buffer variant if the dither runs after coverage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moves most of the logic of uber_sdf.frag to uber_sdf_common.glsl. New uber_sdf.frag and uber_sdf_ssbo.frag shaders import uber_sdf_common.glsl and implement
getGradientColor()with a texture-based or SSBO-based gradient implementation.Gradient SSBO logic based off the existing SSBO gradient shaders like https://github.com/flutter/flutter/blob/b138043fc75c1270e1bd9f1fa0a5c1299e86e638/engine/src/flutter/impeller/entity/shaders/gradients/linear_gradient_ssbo_fill.frag
Note that for GLES, SSBO is currently not supported.
flutter/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.cc
Lines 277 to 279 in b138043
This means that for Windows and Linux, which currently always have a GLES backend, this change does not do anything.
This is a prerequisite for addressing #190401. This improves UberSDF gradient rendering when SSBO is supported, but not on Windows. The next step to address #190401 is to make Windows support SSBO: #193162
Tested this with the "Scrolling basic LinearGradient" benchmark app from #190401. I ran it 3 times, on my Mac. The raster time goes down significantly, so that it is on par with the solid color time.
On Mac (my local dev machine, M3 MacBook Pro):
Before:
Full results
After:
Full results
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.