Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@dkwingsmt
Copy link
Contributor

@dkwingsmt dkwingsmt commented Sep 16, 2025

This PR adds the engine support for a new iOS-style blur. It works by adding parameters to the blur filter that specify its blurring bounds.

This is the engine-side implementation. The corresponding framework changes that expose this to developers are in:

Related issues:

Design doc & previous discussions: flutter.dev/go/ios-style-blur-support

The Visual (Before & After)

This new mode, which I'm calling "bounded blur," is different from the traditional (global) gaussian blur in that blurs would not sample transparent pixels from outside the provided area.

The demo below shows the old blur (left) and the new bounded blur (right). Both are blurring a black triangle.

image image

Notice the new version on the right no longer has the bright "lining" at the top and left edges. This is because the blur algorithm now knows its own bounds and correctly stops sampling pixels from outside that area.

Technical details

API Change

To pass the bounds information down, I've added new parameters to _initBlur:

  // painting.dart
  external void _initBlur(
    double sigmaX,
    double sigmaY,
    int tileMode,
    bool bounded,  // Start of new parameters
    double boundsLeft,
    double boundsTop,
    double boundsRight,
    double boundsBottom,
  );

How the Bounds Are Used

These bounds are passed all the way down to GaussianBlurFilterContents and affect two key parts of the process:

  • Downsampling Pass: The shader is instructed not to sample any pixels outside the provided bounds.
  • Blurring Passes: The final blurred result is divided by the resulting opacity. This normalizes the varying alpha (due to varying sum of weights) across the pixels near the edge.

Notable Engine Changes

To handle the downsampling logic, I created a new downsampling shader texture_downsample_bounded.

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 framework flutter/packages/flutter repository. See also f: labels. engine flutter/engine related. See also e: labels. f: cupertino flutter/packages/flutter/cupertino repository e: impeller Impeller rendering backend issues and features requests platform-ios iOS applications specifically team-ios Owned by iOS platform team labels Sep 16, 2025
@dkwingsmt dkwingsmt changed the title iOS style blurring [Engine] iOS style blurring Sep 17, 2025
@dkwingsmt dkwingsmt marked this pull request as ready for review September 17, 2025 07:15
@dkwingsmt dkwingsmt requested a review from a team as a code owner September 17, 2025 07:15
@github-actions github-actions bot removed the f: cupertino flutter/packages/flutter/cupertino repository label Sep 17, 2025
@github-actions github-actions bot removed the framework flutter/packages/flutter repository. See also f: labels. label Sep 17, 2025
Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

Generally I'm good with the approach. It's on a good trajectory. You need to add a golden test to aiks_dl_blur_unittests.

pass.SetPipeline(
renderer.GetDownsampleSoftwareDecalPipeline(pipeline_options));
Rect bounds_rect =
pass_args.bounds_uv.value_or(Rect::MakeLTRB(0, 0, 1, 1));
Copy link
Member

Choose a reason for hiding this comment

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

Does this match old behavior when setting the bounds to (0,0,1,1)? Seems like that is potentially doing clipping that wasn't happening before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should match the old behavior.

The if branch works in the following way: (I've added this new commment above this statement)

      // If the blur is unbounded, then the pipeline is selected based on
      // whether the platform supports decal tile mode: The GLES backend
      // conditionally supports decal tile mode, while decal is always supported
      // for Vulkan and Metal.
      //
      // If the blur is bounded, software decal is always needed.

So there are two ways to enter the else branch. The first way is that the blur is unbounded, and the else branch is chosen because of GLES, corresponding to GetDownsampleTextureGlesPipeline before the PR, whose behavior was defined by the Sample as follows:

vec4 Sample(vec2 uv) {
#ifdef SUPPORTS_DECAL   // 0 for GLES
  return texture(texture_sampler, uv, float16_t(kDefaultMipBias));
#else
  if ((uv.x < 0 || uv.y < 0 || uv.x > 1 || uv.y > 1)) {
    return vec4(0);
  } else {
    return texture(texture_sampler, uv, float16_t(kDefaultMipBias));
  }
#endif
}

The Sample function always checks the coord against (0,0,1,1).

The second way is that the blur is bounded. In this case the coord is checked against the bounds.

case 0:
blur = std::make_shared<GaussianBlurFilterContents>(
blur_sigma_x.sigma, blur_sigma_y.sigma,
blur_sigma_x.sigma, blur_sigma_y.sigma, std::nullopt,
Copy link
Member

Choose a reason for hiding this comment

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

This would be a good place to expand the arguments to be able to control this. It's not an automated test though so it's optional. It would be helpful for you probably to mess around with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've attempted to mess around it but it's kind of complicated considering all the transform matrices and texture stuff. I think it'd be better done in a separate PR.

Copy link
Member

Choose a reason for hiding this comment

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

Did you get a chance to test how the bounded blur reacts to transforms?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. As shown in the playground demo (in the latest comment) and the golden test, the blur bound respects the current transform matrix.

@gaaclarke
Copy link
Member

I don't know if you are ready yet, but I'm going to hold off reviewing again until we get this PR green through CI so we can see the goldens. Let me know if you need any help getting it through.

@github-actions github-actions bot added the platform-web Web applications specifically label Sep 24, 2025
@mdebbar
Copy link
Contributor

mdebbar commented Sep 24, 2025

web part looks good!

@flutter-dashboard
Copy link

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 #175458 at sha 1d1f2a9

@flutter-dashboard flutter-dashboard bot added the will affect goldens Changes to golden files label Sep 25, 2025
@dkwingsmt
Copy link
Contributor Author

dkwingsmt commented Sep 25, 2025

gaaclarke : I'm aware that the golden test isn't representative. Actually some maths of the shaders aren't right, and I've been trying to figure them out for days. I'll let you know when I made progress.

@gaaclarke
Copy link
Member

I'm aware that the golden test isn't representative.

Thanks for the heads up. Let me know when you need me to look again.

@flutter-dashboard
Copy link

Golden file changes are available for triage from new commit, Click here to view.

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 #175458 at sha 4735a79

@flutter-dashboard
Copy link

Golden file changes are available for triage from new commit, Click here to view.

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 #175458 at sha b7b2d9f

@flutter-dashboard
Copy link

Golden file changes are available for triage from new commit, Click here to view.

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 #175458 at sha 005c218

@dkwingsmt
Copy link
Contributor Author

dkwingsmt commented Oct 2, 2025

I've completed the PR and it should be ready to review. Here are notable changes:

  1. gaussian.frag is now controlled by a specialized constant bounded_blur.
  2. Both branches of CalculateDownsamplePassArgs have been implemented and each tested by a golden test. Each golden test verifies two subcases: One has a non-zero offset and unclipped, and the other has a zero offset and clipped. (The latter one is possibly the only case that matters in practice.)
  3. Regarding the two branches of MakeDownsampleSubpass, I've decided that the first branch, which reuses existing mipmap renderers, does not suit our case, since a bounded blur requires special treatment during downsampling.
  4. I've added an interactive playground for bounded blurs.
Screen.Recording.2025-10-02.at.1.00.36.PM.mp4
  1. I disabled the kernel lerp hack for bounded blurs. This was to reduce the wave-like artifacts close to edges (see video below).
Screen.Recording.2025-10-02.at.1.06.09.PM.mp4
  • Known issue: These artifacts are greatly reduced, although not completely gone, without the lerp hack. It becomes much more noticeable (but still better than with the lerp hack) if the texture is drawn with a non-zero offset. Fortunately in practice we should almost only draw texture with a zero-artifacts. I've investigated this for days in vain, and I've decided that the result is good enough for zero offset texture anyway.

@flutter-dashboard
Copy link

Golden file changes are available for triage from new commit, Click here to view.

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 #175458 at sha d1cbd6a

@gaaclarke
Copy link
Member

It's been a second since I looked at this. I think you may have answered this in the past but it wasn't captured. Can you remind me why we couldn't implement this by tweaking the downsample pass to conditionally the crop the gutter and set the tilemode to clamp?

So for example, in the case where we are blurring something that is running out of content it looks like this:

input:

[####]
[####]
[####]
[####]

downsample output:

[.....]
[.###.]
[.###.]
[.###.]
[.....]

Then the blur is executed on it. If instead the downsample output was similar to the input with a tilemode set to clamp, wouldn't that give you the same result?

@dkwingsmt
Copy link
Contributor Author

@gaaclarke This PR is ready for a (hopefully final) review. For your convenience, here are the changes since the last review:

Commit c9e56b1 rewrote some documents to reflect the fact that tile_mode is always effective whether bounds is null or not.

It also adds another option "Tile mode" to the playground. As we analyzed the last time, tile mode can still affect bounded blur if the blur bound crosses texture bound. Check out the following video while paying attention to the top left corner of the window.

Screen.Recording.2025-12-22.at.11.04.56.AM.mov

Commit 8f59f39 reorders the parameters by placing bounds after tile_mode with a default value of std::nullopt if applicable. This is done for two reasons:

  • Since tile modes always apply while bounds don't, bounds feels like a lesser parameter and is more suitable being placed behind.
  • Placing bounds the last allows omitting this parameter when the blur is not bounded, reducing the lines of changes.

Copy link
Member

@gaaclarke gaaclarke left a comment

Choose a reason for hiding this comment

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

lgtm, thanks for all the following up and clarifying. I'm happy with were we ended up.

Comment on lines 4283 to 4285
/// The `bounds` rectangle is specified in the canvas's current coordinate
/// space (it is affected by the current transform). Consequently, the bounds
/// may not be axis-aligned in canvas coordinates.
/// space and is affected by the current transform; consequently, the bounds
/// may not be axis-aligned in the final canvas coordinates.
Copy link
Member

Choose a reason for hiding this comment

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

No action required: I wish we had a canonical word for this coordinate space. I typically call it local coordinate space.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah honestly I would call it "local coordinate" but I don't think it's clear enough for many people, which matters more.

@dkwingsmt
Copy link
Contributor Author

dkwingsmt commented Dec 22, 2025

@gaaclarke Thank you so much for patiently working through this PR with me!

@flutter-dashboard
Copy link

Golden file changes are available for triage from new commit, Click here to view.

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 #175458 at sha 8f59f39

@dkwingsmt dkwingsmt added the autosubmit Merge PR when tree becomes green via auto submit App label Dec 22, 2025
@auto-submit auto-submit bot added this pull request to the merge queue Dec 23, 2025
Merged via the queue into flutter:master with commit 906d520 Dec 23, 2025
181 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Dec 23, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Dec 23, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Dec 23, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Dec 23, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Dec 23, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Dec 23, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Dec 23, 2025
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Dec 23, 2025
…10674)

Manual roll Flutter from 57c3f8b66525 to 6ff7f300473f (83 revisions)

Manual roll requested by [email protected]

flutter/flutter@57c3f8b...6ff7f30

2025-12-23 [email protected] Roll Packages from f28cf2e to 5e3a766 (3 revisions) (flutter/flutter#180232)
2025-12-23 [email protected] Roll Fuchsia Linux SDK from CmFPyvSc-K8_WDd5p... to 5EgkVbjGVZmCFPdtR... (flutter/flutter#180230)
2025-12-23 [email protected] Roll Skia from db7ec9a14905 to bdb147ae3040 (2 revisions) (flutter/flutter#180222)
2025-12-23 [email protected] Add SnackBarTheme (flutter/flutter#180001)
2025-12-23 [email protected] Roll Skia from 0b1ba3920f1c to db7ec9a14905 (1 revision) (flutter/flutter#180219)
2025-12-23 [email protected] Roll Dart SDK from 31e9f619e18a to 94b05f717ba3 (1 revision) (flutter/flutter#180216)
2025-12-23 [email protected] Roll Skia from a3e4f7b9d5f3 to 0b1ba3920f1c (1 revision) (flutter/flutter#180214)
2025-12-23 [email protected] Roll Skia from b8517d1e25f7 to a3e4f7b9d5f3 (2 revisions) (flutter/flutter#180211)
2025-12-23 [email protected] [Engine] iOS style blurring (flutter/flutter#175458)
2025-12-23 [email protected] Roll Dart SDK from 2243e91acaf2 to 31e9f619e18a (1 revision) (flutter/flutter#180210)
2025-12-22 [email protected] Add error description for nbsp character(\u202f) (flutter/flutter#178895)
2025-12-22 [email protected] Roll Skia from 98c01ea504d7 to b8517d1e25f7 (1 revision) (flutter/flutter#180207)
2025-12-22 [email protected] Small clean up in `LocalizationPlugin` (flutter/flutter#180053)
2025-12-22 [email protected] Roll Skia from c5beca8fa90b to 98c01ea504d7 (2 revisions) (flutter/flutter#180202)
2025-12-22 [email protected] Roll Dart SDK from cff33b09b24d to 2243e91acaf2 (1 revision) (flutter/flutter#180199)
2025-12-22 [email protected] Remove usages of Android's `AsyncTask` in favor of `java.util.concurrent` (flutter/flutter#180050)
2025-12-22 [email protected] Roll Fuchsia Linux SDK from 18ZvfJB61p7Z8HAaC... to CmFPyvSc-K8_WDd5p... (flutter/flutter#180193)
2025-12-22 [email protected] Roll Skia from 7b7083ed9d57 to c5beca8fa90b (5 revisions) (flutter/flutter#180187)
2025-12-22 [email protected] Roll Dart SDK from 38812d17127d to cff33b09b24d (1 revision) (flutter/flutter#180185)
2025-12-22 [email protected] Roll Skia from 0eef18a0e2e6 to 7b7083ed9d57 (1 revision) (flutter/flutter#180184)
2025-12-22 [email protected] Roll Dart SDK from 66c8013acbff to 38812d17127d (1 revision) (flutter/flutter#180179)
2025-12-21 [email protected] Roll Skia from 6fbc6c75b9bb to 0eef18a0e2e6 (2 revisions) (flutter/flutter#180176)
2025-12-21 [email protected] Roll Fuchsia Linux SDK from kGnnY1-fTWwYAnk8e... to 18ZvfJB61p7Z8HAaC... (flutter/flutter#180173)
2025-12-21 [email protected] Roll Skia from 1a4ca755288a to 6fbc6c75b9bb (1 revision) (flutter/flutter#180167)
2025-12-20 [email protected] Roll Skia from 2ad7452bd9d1 to 1a4ca755288a (1 revision) (flutter/flutter#180160)
2025-12-20 [email protected] Roll Fuchsia Linux SDK from oe10epXkqGnv21AbZ... to kGnnY1-fTWwYAnk8e... (flutter/flutter#180158)
2025-12-20 [email protected] Roll Skia from b01ad49ea807 to 2ad7452bd9d1 (1 revision) (flutter/flutter#180155)
2025-12-20 [email protected] Roll Dart SDK from 8fb1c0c0a8ae to 66c8013acbff (1 revision) (flutter/flutter#180154)
2025-12-20 [email protected] Remove unnecessary RadioGroup migration TODOs (flutter/flutter#180105)
2025-12-20 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[reland] Unify canvas creation and Surface code in Skwasm and CanvasKit (#179473)" (flutter/flutter#180152)
2025-12-20 [email protected] Roll Skia from 3cc7e81928f0 to b01ad49ea807 (1 revision) (flutter/flutter#180151)
2025-12-20 [email protected] Roll Dart SDK from ac95c6e8a31d to 8fb1c0c0a8ae (1 revision) (flutter/flutter#180148)
2025-12-19 [email protected] Roll pub packages (flutter/flutter#180146)
2025-12-19 [email protected] Roll Skia from fa4434632ce6 to 3cc7e81928f0 (4 revisions) (flutter/flutter#180142)
2025-12-19 [email protected] [reland] Unify canvas creation and Surface code in Skwasm and CanvasKit (flutter/flutter#179473)
2025-12-19 [email protected] Roll Skia from ae5dd72b3591 to fa4434632ce6 (2 revisions) (flutter/flutter#180136)
2025-12-19 [email protected] Semantics headingLeveldoc update (flutter/flutter#179999)
2025-12-19 [email protected] Fix an issue where the semantics announce event may be encoded as either an int32_t or an int64_t depending on its value (flutter/flutter#180071)
2025-12-19 [email protected] [ Web ] Pass `--enable-experimental-ffi` when compiling WASM tests (flutter/flutter#180127)
2025-12-19 [email protected] Roll Dart SDK from cfc117d10d36 to ac95c6e8a31d (1 revision) (flutter/flutter#180130)
2025-12-19 [email protected] Pass canaryFeatures to BuildSettings (flutter/flutter#180108)
2025-12-19 [email protected] Roll Skia from fe2be289c9fe to ae5dd72b3591 (1 revision) (flutter/flutter#180129)
2025-12-19 [email protected] Roll Packages from 6f392aa to f28cf2e (1 revision) (flutter/flutter#180124)
2025-12-19 [email protected] Set text input purpose and hints on Linux platform (flutter/flutter#180013)
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e: impeller Impeller rendering backend issues and features requests engine flutter/engine related. See also e: labels. platform-ios iOS applications specifically platform-web Web applications specifically team-ios Owned by iOS platform team triaged-web Triaged by Web platform team will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants