UberSDFContent refactoring and handle stroke miter limit for stroked rects#184603
Conversation
f9b2e5a to
45c8c25
Compare
flar
left a comment
There was a problem hiding this comment.
Minor changes needed and a couple of nits.
engine/src/flutter/impeller/entity/contents/uber_sdf_contents.cc
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/contents/uber_sdf_contents.cc
Outdated
Show resolved
Hide resolved
| case Join::kRound: | ||
| frag_info.stroke_join = 2.0f; | ||
| break; | ||
| FS::FragInfo frag_info = {}; |
There was a problem hiding this comment.
It was needed to zero out the struct because the logic below would only optionally populate certain values (e.g. frag_info.stroke_width is not populated if params_.stroke is null). I changed the logic to just always populate every value, with 0s for default values.
| frag_info.stroked = params_.stroke ? 1.0f : 0.0f; | ||
| if (params_.stroke) { | ||
| frag_info.stroke_width = params_.stroke->width; | ||
| switch (params_.stroke->join) { |
There was a problem hiding this comment.
Maybe have a static or anonymous static ToShaderJoin method?
(And the same for .type even though it only has 2 values right now.
And if the type conversion function uses a switch statement on an enum class then we can be sure that we update it when we add new types.
There was a problem hiding this comment.
Can the shader define constants for the joins and types that appear in the generated C++ header file along side the FragInfo so we can ensure consistency?
There was a problem hiding this comment.
I added anonymous static methods as you suggested.
I don't know of a way to define these constants in the shader's generated C++.
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
| FillRectGeometry geometry(rect.Expand(1.0f)); | ||
| auto contents = UberSDFContents::MakeRect(Color::Red(), 0.0f, Join::kMiter, | ||
| false, &geometry); | ||
| auto params = UberSDFParameters::MakeRect(Color::Red(), rect, std::nullopt); |
There was a problem hiding this comment.
Do we have one that tests a circle as well?
There was a problem hiding this comment.
Added more tests here, for stroked rects and filled/stroked circles
There was a problem hiding this comment.
Code Review
This pull request refactors the Impeller engine's SDF rendering by introducing a dedicated UberSDFParameters struct and UberSDFGeometry class to centralize parameter management and geometry calculations. The changes simplify the UberSDFContents interface and improve code maintainability. I have identified a potential division-by-zero issue in UberSDFGeometry::GetPositionBuffer that should be addressed, and I recommend adding documentation to public members to comply with the project's style guide.
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
walley892
left a comment
There was a problem hiding this comment.
Great change, glad we came up with a good foundation to build on! A couple of comments
engine/src/flutter/impeller/entity/contents/uber_sdf_parameters.h
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.h
Outdated
Show resolved
Hide resolved
| Scalar aa_padding); | ||
| static std::unique_ptr<UberSDFContents> Make( | ||
| const UberSDFParameters& params, | ||
| std::unique_ptr<Geometry> geometry); |
There was a problem hiding this comment.
I don't know enough about the surrounding code or the lifetime of this object relative to the surrounding objects where it's used , but it looks like right after this object is created in Canvas a raw pointer to it is grabbed. Is this safe to be a unique_ptr managed by the Contents?
There was a problem hiding this comment.
The contents doesn't manage the unique_ptr. Whoever calls this to create a contents manages the unique_ptr.
I believe UberSDFContents was based on CircleContents. So this constructor returns a unique_ptr in the same way as CircleContents
and a couple other similar Contents classes.There was a problem hiding this comment.
Oh wait, I think I misinterpreted your comment. You were referring to the geometry unique_ptr, not the constructor's returned unique_ptr.
I think this should be correct with how it's currently used. UberSDFContents is only ever used in canvas.cc, and in both of the use cases it seems correct to me to hand over ownership of the geometry to the Contents.
Edit: And looking at CircleContents, that also takes a unique_ptr for its geometry. So my first comment is still perfectly applicable, accidentally.
There was a problem hiding this comment.
Sorry, I wasn't clear. The pointer to the geometry is owned by the Contents, then a raw pointer to the geometry is retrieved in the Canvas.
There was a problem hiding this comment.
Maybe the Contents owning the geometry is conceptually correct. The existing code in canvas.cc that uses CircleContents that uses this ownership model doesn't then grab a raw pointer to the geometry, because it doesn't use AddRenderSDFEntityToCurrentPass.
I'm not sure if there will be any issues with giving ownership of the geometry to this Contents class, then later passing around a raw pointer to the geometry - I don't know enough about the surrounding code.
Code pointers (no pun intended) to clarify:
This implementation (which grabs a raw pointer to the geometry owned by the Contents): https://github.com/flutter/flutter/pull/184603/changes#diff-66371f190607b925575505f2a8250f7738c9c444328cb6f453575652c4bde57dL1053
CircleContents (which doesn't grab a raw pointer right away): https://github.com/flutter/flutter/blob/master/engine/src/flutter/impeller/display_list/canvas.cc#L525
There was a problem hiding this comment.
All of this is behind a flag so users won't be affected if there are any resulting memory access issues. Can you merge ci.yaml from HEAD so we can test this in CICD?
There was a problem hiding this comment.
Merged with HEAD.
I see your point about canvas.cc grabbing and using the raw pointer. Following the code, I do think this is being used in a safe way. But it's non obvious and kind of fragile. I think there's a lot of unnecessary passing around of geometry pointers happening which could be cleaned up. I'm going to make a separate PR to do this so it doesn't clutter this PR.
There was a problem hiding this comment.
We have quite a history of using stack allocated objects with raw pointers in Canvas for things like this. The eventual call to AddFooToCurrentPass consumes these objects and is finished with them when it returns.
There was a problem hiding this comment.
But unique pointers are safe. We can revisit whether we think stack allocation is important later.
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
flar
left a comment
There was a problem hiding this comment.
Submitting a few responses, but not done reviewing everything.
| UberSDFGeometry::~UberSDFGeometry() = default; | ||
|
|
||
| Rect UberSDFGeometry::GetBaseBounds() const { | ||
| Rect bounds = Rect::MakeOriginSize(params_.center - params_.size, |
There was a problem hiding this comment.
thought - I'm questioning whether MakeOriginSize buys us any readability here. Perhaps if Rect had a MakeCenterRadii(Point, Size) factory, but at this point is it more readable to just do the LTRB with center.xy +/- size.xy?
We might consider having a MakeDiagonal that takes 2 points and does a more optimized init than MakePointBounds. Then this could be MakeDiagonal(c-s, c+s)
There was a problem hiding this comment.
Done. I changed to to LTRB, which I think is a little more straightforward.
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.h
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/contents/uber_sdf_parameters_unittests.cc
Show resolved
Hide resolved
walley892
left a comment
There was a problem hiding this comment.
CI is passing, so I guess the weird memory handling is good for now. LGTM
|
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. |
flar
left a comment
There was a problem hiding this comment.
LGTM, some comments about some potential future work mainly.
| Scalar aa_padding); | ||
| static std::unique_ptr<UberSDFContents> Make( | ||
| const UberSDFParameters& params, | ||
| std::unique_ptr<Geometry> geometry); |
There was a problem hiding this comment.
We have quite a history of using stack allocated objects with raw pointers in Canvas for things like this. The eventual call to AddFooToCurrentPass consumes these objects and is finished with them when it returns.
| Scalar aa_padding); | ||
| static std::unique_ptr<UberSDFContents> Make( | ||
| const UberSDFParameters& params, | ||
| std::unique_ptr<Geometry> geometry); |
There was a problem hiding this comment.
But unique pointers are safe. We can revisit whether we think stack allocation is important later.
engine/src/flutter/impeller/entity/contents/uber_sdf_parameters.h
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
engine/src/flutter/impeller/entity/geometry/uber_sdf_geometry.cc
Outdated
Show resolved
Hide resolved
|
As recommended by walley892, I'm just going to get this submitted to unblock other work that needs to go on top of this. I filed #184828 to investigate the golden issues. |
flutter/flutter@81c87ea...bf18e39 2026-04-10 25263018+trizin@users.noreply.github.com [flutter_tools] Fix arm64e incorrectly matching arm64 in regex check (flutter/flutter#184057) 2026-04-10 jacksongardner@google.com Specify GitHub Repo in GH CLI calls for revert workflow. (flutter/flutter#184878) 2026-04-10 jacksongardner@google.com Don't use `git add -N` in the sync engine workflow. (flutter/flutter#184882) 2026-04-10 engine-flutter-autoroll@skia.org Roll Skia from af67d5555e35 to 25b01e5f4ea0 (14 revisions) (flutter/flutter#184865) 2026-04-10 737941+loic-sharma@users.noreply.github.com [Dot shorthands] Finish examples/api migration (flutter/flutter#183967) 2026-04-10 engine-flutter-autoroll@skia.org Roll Dart SDK from 98a143f8873e to e715805ddbd3 (1 revision) (flutter/flutter#184864) 2026-04-10 98614782+auto-submit[bot]@users.noreply.github.com Reverts "Disable async mode with LLDB (#184768)" (flutter/flutter#184868) 2026-04-09 katelovett@google.com Skip freeze check in the merge queue (flutter/flutter#184854) 2026-04-09 116356835+AbdeMohlbi@users.noreply.github.com Remove unused variable in `ProcessTextPlugin.java` (flutter/flutter#184161) 2026-04-09 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from pDXMXRIjEHTw7B0sk... to lZcRfPoCLnDttrf9P... (flutter/flutter#184842) 2026-04-09 engine-flutter-autoroll@skia.org Roll Dart SDK from bd6280c3e8e9 to 98a143f8873e (5 revisions) (flutter/flutter#184824) 2026-04-09 34871572+gmackall@users.noreply.github.com Remove `linux_android_emu_unstable android_engine_vulkan_tests` (flutter/flutter#184787) 2026-04-09 engine-flutter-autoroll@skia.org Roll Packages from 0e0a032 to 1aa892c (9 revisions) (flutter/flutter#184829) 2026-04-09 dacoharkes@google.com [record_use] Add experimental flag and test project (flutter/flutter#184719) 2026-04-09 15619084+vashworth@users.noreply.github.com Disable async mode with LLDB (flutter/flutter#184768) 2026-04-09 1063596+reidbaker@users.noreply.github.com Update link for rolling forward to aligned Dart hash (flutter/flutter#184780) 2026-04-09 engine-flutter-autoroll@skia.org Roll Skia from 4d0f5389e131 to af67d5555e35 (3 revisions) (flutter/flutter#184825) 2026-04-09 97480502+b-luk@users.noreply.github.com UberSDFContent refactoring and handle stroke miter limit for stroked rects (flutter/flutter#184603) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC bmparr@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md

Creates a new UberSDFParameters struct, which is encapsulates all the state needed for a UberSDF FragInfo. It has shape-specific constructors to populate this state for different shapes.
Creates a new UberSDFGeometry class to be used as the Geometry for UberSDFContents. It contains all the AA padding logic for UberSDF, so the AA padding can be removed from canvas and from FillRectGeometry. UberSDFGeometry's GetPositionBuffer leverages a FillRectGeometry to return a quad that properly accounts for AA padding.
UberSDFContents is updated to be constructed with UberSDFParameters rather than having shape-specific constructors. It becomes agnostic to the specific shape being drawn, and now has no shape- or geometry-aware logic. It simply pipes through UberSDFParameters values to the UberSDF shader's FragInfo.
This is mostly a no-op refactoring. The exception is for UberSDF stroke rects with a miter limit. UberSDFParameters properly handles miter limit for these, so now stroked rects with low miter limits properly become beveled.
Part of #184402
Fixes #184404
Part of #184352
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
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.