Skip to content

UberSDFContent refactoring and handle stroke miter limit for stroked rects#184603

Merged
auto-submit[bot] merged 12 commits intoflutter:masterfrom
b-luk:ubersdfalwaysfilledrectgeometry
Apr 9, 2026
Merged

UberSDFContent refactoring and handle stroke miter limit for stroked rects#184603
auto-submit[bot] merged 12 commits intoflutter:masterfrom
b-luk:ubersdfalwaysfilledrectgeometry

Conversation

@b-luk
Copy link
Copy Markdown
Contributor

@b-luk b-luk commented Apr 4, 2026

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-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.

@github-actions github-actions bot added engine flutter/engine related. See also e: labels. e: impeller Impeller rendering backend issues and features requests labels Apr 4, 2026
@b-luk b-luk force-pushed the ubersdfalwaysfilledrectgeometry branch from f9b2e5a to 45c8c25 Compare April 7, 2026 19:08
Copy link
Copy Markdown
Contributor

@flar flar left a comment

Choose a reason for hiding this comment

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

Minor changes needed and a couple of nits.

case Join::kRound:
frag_info.stroke_join = 2.0f;
break;
FS::FragInfo frag_info = {};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need the = {}?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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++.

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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we have one that tests a circle as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added more tests here, for stroked rects and filled/stroked circles

@b-luk b-luk changed the title Quick UberSDFContent and CircleContent refactoring prototype UberSDFContent refactoring Apr 8, 2026
@b-luk b-luk marked this pull request as ready for review April 8, 2026 21:26
@b-luk b-luk requested review from flar and walley892 April 8, 2026 21:27
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

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.

@b-luk b-luk changed the title UberSDFContent refactoring UberSDFContent refactoring and adding support for stroked rects Apr 8, 2026
@b-luk b-luk changed the title UberSDFContent refactoring and adding support for stroked rects UberSDFContent refactoring Apr 8, 2026
@b-luk b-luk added the CICD Run CI/CD label Apr 8, 2026
@b-luk b-luk changed the title UberSDFContent refactoring UberSDFContent refactoring and handle stroke miter limit for stroked rects Apr 8, 2026
Copy link
Copy Markdown
Contributor

@walley892 walley892 left a comment

Choose a reason for hiding this comment

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

Great change, glad we came up with a good foundation to build on! A couple of comments

Scalar aa_padding);
static std::unique_ptr<UberSDFContents> Make(
const UberSDFParameters& params,
std::unique_ptr<Geometry> geometry);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

Make(std::unique_ptr<CircleGeometry> geometry, Color color, bool stroked);
and a couple other similar Contents classes.

Copy link
Copy Markdown
Contributor Author

@b-luk b-luk Apr 8, 2026

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

@walley892 walley892 Apr 8, 2026

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But unique pointers are safe. We can revisit whether we think stack allocation is important later.

Copy link
Copy Markdown
Contributor

@flar flar left a comment

Choose a reason for hiding this comment

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

Submitting a few responses, but not done reviewing everything.

UberSDFGeometry::~UberSDFGeometry() = default;

Rect UberSDFGeometry::GetBaseBounds() const {
Rect bounds = Rect::MakeOriginSize(params_.center - params_.size,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done. I changed to to LTRB, which I think is a little more straightforward.

@github-actions github-actions bot removed the CICD Run CI/CD label Apr 8, 2026
@b-luk b-luk requested a review from flar April 8, 2026 23:23
@github-actions github-actions bot removed the CICD Run CI/CD label Apr 9, 2026
@walley892 walley892 added the CICD Run CI/CD label Apr 9, 2026
Copy link
Copy Markdown
Contributor

@walley892 walley892 left a comment

Choose a reason for hiding this comment

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

CI is passing, so I guess the weird memory handling is good for now. LGTM

@flutter-dashboard
Copy link
Copy Markdown

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 package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #184603 at sha bf0d218

@flutter-dashboard flutter-dashboard bot added the will affect goldens Changes to golden files label Apr 9, 2026
Copy link
Copy Markdown
Contributor

@flar flar left a comment

Choose a reason for hiding this comment

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

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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But unique pointers are safe. We can revisit whether we think stack allocation is important later.

@b-luk
Copy link
Copy Markdown
Contributor Author

b-luk commented Apr 9, 2026

There is one golden change which is significantly different than expected:
image

And a few others which are slightly weird. Maybe related to blending. I will investigate.

@b-luk b-luk added the autosubmit Merge PR when tree becomes green via auto submit App label Apr 9, 2026
@b-luk
Copy link
Copy Markdown
Contributor Author

b-luk commented Apr 9, 2026

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.

@auto-submit auto-submit bot added this pull request to the merge queue Apr 9, 2026
Merged via the queue into flutter:master with commit 47b5032 Apr 9, 2026
201 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Apr 9, 2026
@b-luk b-luk deleted the ubersdfalwaysfilledrectgeometry branch April 9, 2026 22:58
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 10, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Apr 10, 2026
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Apr 10, 2026
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD e: impeller Impeller rendering backend issues and features requests engine flutter/engine related. See also e: labels. will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Handle miter limit for stroked Rect SDFs

3 participants