Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Impeller] match rrect_blur math to the gaussian math #53130

Merged
merged 14 commits into from
Jun 3, 2024

Conversation

gaaclarke
Copy link
Member

@gaaclarke gaaclarke commented May 30, 2024

fixes flutter/flutter#149276

before

Screenshot 2024-05-30 at 1 29 11 PM

after

Screenshot 2024-05-31 at 11 07 58 AM

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or the PR is test-exempt. See testing the engine for instructions on writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@gaaclarke gaaclarke requested review from bdero and flar May 30, 2024 20:39
@gaaclarke
Copy link
Member Author

gaaclarke commented May 30, 2024

When we land this we should revisit the math for the rrect_blur halo since we should be able to make it even more constricted.

Copy link
Member

@jonahwilliams jonahwilliams left a comment

Choose a reason for hiding this comment

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

Beautiful!

@flar
Copy link
Contributor

flar commented May 30, 2024

How do either of those 2 results compare to our legacy behavior on these shapes?

@gaaclarke
Copy link
Member Author

How do either of those 2 results compare to our legacy behavior on these shapes?

The guassian blur matches skia's blur. That work happened here: #47405

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

Changes reported for pull request #53130 at sha 2466518

@gaaclarke
Copy link
Member Author

Oh awesome, the results on CI show that the scalar is related to DPI. I guess my golden images were running at 2x. I thought they were all 1x. I'll make that change. That wraps everything up in a nice bow.

e7d50f32d8147dfc0d68136ab1f80b78

@flar
Copy link
Contributor

flar commented May 30, 2024

How do either of those 2 results compare to our legacy behavior on these shapes?

The guassian blur matches skia's blur. That work happened here: #47405

(Hang on, I was running this on a May 11 version of the framework repo, need to double check...)
(Reconfirmed on To(framework)T)

That's not what I'm seeing on the current ToT - the fast blur matches Skia, not the other way around:
(Note, the RRect fast blur should be on the left, the right is a Path that simulates a round rect and isn't optimized to an RRect)

test program
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                CustomPaint(
                  painter: _RRectPainter(
                    cornerSize: 10,
                    blurSize: 10,
                  ),
                  size: const Size(150, 150),
                ),
                CustomPaint(
                  painter: _RRectPathPainter(
                    cornerSize: 10,
                    blurSize: 10,
                  ),
                  size: const Size(150, 150),
                ),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                CustomPaint(
                  painter: _RRectPainter(
                    cornerSize: 10,
                    blurSize: 30,
                  ),
                  size: const Size(150, 150),
                ),
                CustomPaint(
                  painter: _RRectPathPainter(
                    cornerSize: 10,
                    blurSize: 30,
                  ),
                  size: const Size(150, 150),
                ),
              ],
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                CustomPaint(
                  painter: _RRectPainter(
                    cornerSize: 10,
                    blurSize: 50,
                  ),
                  size: const Size(150, 150),
                ),
                CustomPaint(
                  painter: _RRectPathPainter(
                    cornerSize: 10,
                    blurSize: 50,
                  ),
                  size: const Size(150, 150),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class _RRectPainter extends CustomPainter {
  _RRectPainter({required this.cornerSize, required this.blurSize});

  final double cornerSize;
  final double blurSize;

  @override
  void paint(Canvas canvas, Size size) {
    Rect rect = Rect.fromLTRB(0, 0, size.width, size.height);
    RRect rrect = RRect.fromRectXY(rect, cornerSize, cornerSize);

    Paint paint = Paint()
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSize)
      ..color = Colors.blue;
    canvas.drawRRect(rrect, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

class _RRectPathPainter extends CustomPainter {
  _RRectPathPainter({required this.cornerSize, required this.blurSize});

  final double cornerSize;
  final double blurSize;

  @override
  void paint(Canvas canvas, Size size) {
    Path path = Path();
    path.addRect(Rect.fromLTRB(
        cornerSize, 0,
        size.width - cornerSize, size.height));
    path.addRect(Rect.fromLTRB(
        0, cornerSize,
        size.width, size.height - cornerSize));
    path.addOval(Rect.fromLTRB(
      0, 0, cornerSize * 2, cornerSize * 2,
    ));
    path.addOval(Rect.fromLTRB(
      size.width - cornerSize * 2, 0, size.width, cornerSize * 2,
    ));
    path.addOval(Rect.fromLTRB(
      0, size.height - cornerSize * 2, cornerSize * 2, size.height,
    ));
    path.addOval(Rect.fromLTRB(
      size.width - cornerSize * 2, size.height - cornerSize * 2, size.width, size.height,
    ));

    Paint paint = Paint()
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSize)
      ..color = Colors.blue;
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Skia output Skia Screenshot 2024-05-30 at 2 47 17 PM
Impeller output Impeller Screenshot 2024-05-30 at 2 48 47 PM

@gaaclarke
Copy link
Member Author

That's not what I'm seeing on the current ToT

Oh interesting, #53130 was working with larger sigmas. Yet sigma 50 is super blurry.
Did you check the reproduction for the bug that fixed?

There is obviously some issue with dpi that needs to be resolved. I'm going to move this back to a draft as we sort through this.

@gaaclarke gaaclarke marked this pull request as draft May 30, 2024 22:01
@flar
Copy link
Contributor

flar commented May 30, 2024

That's not what I'm seeing on the current ToT

Oh interesting, #53130 was working with larger sigmas. Yet sigma 50 is super blurry. Did you check the reproduction for the bug that fixed?

There is obviously some issue with dpi that needs to be resolved. I'm going to move this back to a draft as we sort through this.

This is the framework commit that I was running against:

commit 488fb0958269a8553e400ac8724525ecf98ea88f (HEAD -> main, upstream/master, upstream/main)
Author: Kostia Sokolovskyi <[email protected]>
Date:   Thu May 30 22:17:04 2024 +0200

@flutter-dashboard
Copy link

This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again.

@flar
Copy link
Contributor

flar commented May 30, 2024

In Paint::MaskBlurDescriptor::CreateMaskBlur I don't see anything that applies the transform to the sigmas...?

@gaaclarke
Copy link
Member Author

I verified that the gaussian blur still looks correct for the reproduction code for issue flutter/flutter#127770. That has 164 for a sigma. But setting the sigma to 50 we see the same thing jim saw

skia sigma = 164

impeller sigma = 164

skia sigma = 50

impeller sigma = 50

doctor
[!] Flutter (Channel [user-branch], 3.23.0-13.0.pre.27, on macOS 14.5 23F79 darwin-arm64, locale en)
    ! Flutter version 3.23.0-13.0.pre.27 on channel [user-branch] at /Users/aaclarke/dev/flutter
      Currently on an unknown channel. Run `flutter channel` to switch to an official channel.
      If that doesn't fix the issue, reinstall Flutter by following instructions at https://flutter.dev/docs/get-started/install.
    ! Upstream repository unknown source is not a standard remote.
      Set environment variable "FLUTTER_GIT_URL" to unknown source to dismiss this error.
    • Framework revision 472a29d3f3 (6 hours ago), 2024-05-30 09:24:14 -0700
    • Engine revision fb64b9a4e6
    • Dart version 3.5.0 (build 3.5.0-203.0.dev)
    • DevTools version 2.36.0-dev.10
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.

@flar
Copy link
Contributor

flar commented May 30, 2024

I modified my test case to do 4 columns

  • RRect with 0.5 scale
  • Path with 0.5 scale
  • RRect with 1.0 scale
  • Path with 1.0 scale

Note that I adjust the RRect and Path geometry to counter-act the scale so that they will be the same size shape in both cases, but the blur will be scaled. The left pair should look the same with about 1/2 the blurring compared to the right pair. As you can see, the blurring changes size for the RRect fast path version, but not for the Path (gaussian) version, confirming that the gaussian path is ignoring the transform for the sigma...

(Updated the test case - I forgot to adjust the corner size for the fast RRect case - fixed now)

New test code
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Widget makeRow(double cornerSize, double blurSize) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        CustomPaint(
          painter: _RRectPainter(
            cornerSize: cornerSize,
            blurSize: blurSize,
            scale: 0.5,
          ),
          size: const Size(150, 150),
        ),
        CustomPaint(
          painter: _RRectPathPainter(
            cornerSize: cornerSize,
            blurSize: blurSize,
            scale: 0.5,
          ),
          size: const Size(150, 150),
        ),
        CustomPaint(
          painter: _RRectPainter(
            cornerSize: cornerSize,
            blurSize: blurSize,
            scale: 1.0,
          ),
          size: const Size(150, 150),
        ),
        CustomPaint(
          painter: _RRectPathPainter(
            cornerSize: cornerSize,
            blurSize: blurSize,
            scale: 1.0,
          ),
          size: const Size(150, 150),
        ),
      ],
    );
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            makeRow(10, 10),
            makeRow(10, 30),
            makeRow(10, 50),
          ],
        ),
      ),
    );
  }
}

class _RRectPainter extends CustomPainter {
  _RRectPainter({required this.cornerSize, required this.blurSize, required this.scale});

  final double cornerSize;
  final double blurSize;
  final double scale;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.scale(scale, scale);
    double w = size.width / scale;
    double h = size.height / scale;
    double c = cornerSize / scale;

    Rect rect = Rect.fromLTRB(0, 0, w, h);
    RRect rrect = RRect.fromRectXY(rect, c, c);

    Paint paint = Paint()
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSize)
      ..color = Colors.blue;
    canvas.drawRRect(rrect, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

class _RRectPathPainter extends CustomPainter {
  _RRectPathPainter({required this.cornerSize, required this.blurSize, required this.scale});

  final double cornerSize;
  final double blurSize;
  final double scale;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.scale(scale, scale);
    double w = size.width / scale;
    double h = size.height / scale;
    double c = cornerSize / scale;

    Path path = Path();
    path.addRect(Rect.fromLTRB(c, 0, w - c, h));
    path.addRect(Rect.fromLTRB(0, c, w, h - c));
    path.addOval(Rect.fromLTRB(0, 0, c * 2, c * 2));
    path.addOval(Rect.fromLTRB(w - c * 2, 0, w, c * 2));
    path.addOval(Rect.fromLTRB(0, h - c * 2, c * 2, h));
    path.addOval(Rect.fromLTRB(w - c * 2, h - c * 2, w, h));

    Paint paint = Paint()
      ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSize)
      ..color = Colors.blue;
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Results on Impeller Screenshot 2024-05-30 at 3 34 51 PM

@flar
Copy link
Contributor

flar commented May 30, 2024

These 2 do not look the same to me. The Skia version is much blurrier than the Impeller version. I can still see the basic approximate shape in the Impeller version, but the Skia version is much more obscured.

I verified that the gaussian blur still looks correct for the reproduction code for issue flutter/flutter#127770. That has 164 for a sigma. But setting the sigma to 50 we see the same thing jim saw

skia sigma = 164

impeller sigma = 164

@gaaclarke
Copy link
Member Author

These 2 do not look the same to me. The Skia version is much blurrier than the Impeller version. I can still see the basic approximate shape in the Impeller version, but the Skia version is much more obscured.

Yea, they're not going to be the same. Check out flutter/flutter#127770 to see what incorrect looks like.

@gaaclarke
Copy link
Member Author

Okay, I found the bug in the gaussian blur. We were scaling the sigma by the effect transform, but not the entity's transform. That made the rrect_blur and gaussian blur match so I could remove that 0.5 scalar from rrect_blur. I've also added a new golden that shows they are the same when scaled too.

I still need to make sure that we are still good for that issue against 164 sigma and also make sure that the golden images are correct.

@gaaclarke
Copy link
Member Author

With the most recent change, here is skia vs impeller with flutter/flutter#127770. Although they appear to agree they seem much more dissipated than in the expected result in the original bug.

skia

skia_IMG_6998

impeller

impeller_IMG_6999

@gaaclarke gaaclarke marked this pull request as ready for review May 31, 2024 18:33
@gaaclarke
Copy link
Member Author

Okay, the golden results look as expected. This is ready for review again. The bug was missing the entity transform when scaling the blur sigma. PTAL.

@flutter-dashboard
Copy link

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

Changes reported for pull request #53130 at sha 713670f

@flar
Copy link
Contributor

flar commented May 31, 2024

The 2 new images for 164 look similar, but they are so big it's hard to eyeball them due to having to scroll back and forth...

@flutter-dashboard
Copy link

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

Changes reported for pull request #53130 at sha 3a178f7

@gaaclarke
Copy link
Member Author

This PR makes the halo the match the rrect_blur size, but it seems to also be diluting the blur as seen in flutter/flutter#149431 (comment)

@gaaclarke
Copy link
Member Author

gaaclarke commented Jun 3, 2024

This PR makes the halo the match the rrect_blur size, but it seems to also be diluting the blur as seen in flutter/flutter#149431 (comment)

I mistakenly took out the effect transform from the calculation of blur radius. I thought that was included in the entity transform, it turns out it wasn't.

@flutter-dashboard
Copy link

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

Changes reported for pull request #53130 at sha 82a120b

@gaaclarke gaaclarke added the autosubmit Merge PR when tree becomes green via auto submit App label Jun 3, 2024
@flutter-dashboard
Copy link

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

Changes reported for pull request #53130 at sha 985fb30

@auto-submit auto-submit bot merged commit ad31a25 into flutter:main Jun 3, 2024
26 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Jun 4, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Jun 4, 2024
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Jun 4, 2024
…149658)

flutter/engine@a6aa5d8...e211c43

2024-06-04 [email protected] Roll Fuchsia Linux SDK from lKBLxel8iBaHpT5q6... to pagJoGS4kQ8Efa_if... (flutter/engine#53192)
2024-06-04 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[display_list] allow applying opacity peephole to single glyph. (#53160)" (flutter/engine#53189)
2024-06-04 [email protected] Roll Dart SDK from 49b5590c8d80 to 9dce57c343ec (1 revision) (flutter/engine#53187)
2024-06-04 [email protected] [Impeller] Use varying interpolation to compute some linear gradients. (flutter/engine#53166)
2024-06-03 [email protected] [Impeller] match rrect_blur math to the gaussian math (flutter/engine#53130)
2024-06-03 [email protected] [web] Add Ethiopic font fallback. (flutter/engine#53180)
2024-06-03 [email protected] Fix rendering corruption by Flutter and GDK sharing the same OpenGL context (flutter/engine#53103)
2024-06-03 [email protected] [display_list] allow applying opacity peephole to single glyph. (flutter/engine#53160)

Also rolling transitive DEPS:
  fuchsia/sdk/core/linux-amd64 from lKBLxel8iBaH to pagJoGS4kQ8E

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: 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
auto-submit bot pushed a commit that referenced this pull request Jun 8, 2024
…3261)

fixes flutter/flutter#149781
fixes flutter/flutter#149458
fixes flutter/flutter#140890

This works by performing the blur in the axis aligned "source space" (as opposed to "global space").  The rotation and scaling then is applied to the result of the gaussian blur.  Previously the differences between rrect_blur and gaussian blur were "fixed" in #53130 which worked for blurring content that had no signal.  This addresses that same problem but in a more correct way that is less prone to artifacts when translating a blur since the blur happens in "source space".

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App e: impeller will affect goldens
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Impeller] The 2-pass Gaussian Blur and the RRect blur deviate significantly.
3 participants