Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@jonahwilliams
Copy link
Contributor

@jonahwilliams jonahwilliams commented Oct 7, 2024

Introduces a mechanism to allow backdrop filters to 1) share backdrop inputs and 2) fuse filter applications for faster blurs.

This is a proposed solution to flutter/flutter#131568

Implemented:

  • Developer can specify a "backdrop id" which indicates that a backdrop layer should share the input texture and potentially cached filter for a layer.
  • Removes second save layer for each backdrop filter
  • Removes save layer trace event for backdrop filter
  • Can fuse backdrop filters if there is more than one identical filter

TBD:

  • Adjust heruristic to avoid applying bdf filter to entire screen

Suggestions: applying a bdf should be a distinct operation from a save layer in the DL builder/dispatcher. The saveLayer implmenentation in the impeller dispatcher is super convoluted because it needs to handle both.

Video

Video starts with normal bdf then I hot reload to specify that the bdfs share inputs/filters. This is running on a pixel 8 pro

Change to the macrobenchmark app is just:

  Widget build(BuildContext context) {
    Widget addBlur(Widget child, bool shouldBlur) {
      if (shouldBlur) {
        return ClipRect(
          child: BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
            backdropId: 1, // Added ID
            child: child,
          ),
        );
      } else {
        return child;
      }
    }
demo.mp4

Requires framework changes in https://github.com/jonahwilliams/flutter/pull/new/backdrop_id

@jonahwilliams
Copy link
Contributor Author

This code isn't ready for review yet, but adding @flar and @gaaclarke for high level input. This is the doc that I said I was going to write that I dragged my feet on...

@jonahwilliams
Copy link
Contributor Author

Also I notice in the video that there is a slight pixel shift when switching modes...

@jonahwilliams jonahwilliams marked this pull request as ready for review October 10, 2024 22:22
@jonahwilliams
Copy link
Contributor Author

Currently the transforms are incorrect if the same bdf id is used in different save layers.

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

Just reviewed the first half - the front end - on this pass.

One thought - why 64-bit signed integer for ID? Do we anticipate having this many BDFs? An optional value feels more appropriate at the Dart level than a magic value (or declaring that valid IDs must be positive). Making the parameter nullable at the Dart level seems simple. std::optional seems like overkill at the C++ level, but these don't get used all that much compared to all of the other work going on.

uint32_t total_content_depth,
DlBlendMode max_content_blend_mode,
const DlImageFilter* backdrop = nullptr) {
const DlImageFilter* backdrop = nullptr,
Copy link
Contributor

Choose a reason for hiding this comment

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

These are implementations, aren't they? Do they need the default values?

Copy link
Contributor

Choose a reason for hiding this comment

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

(Or all of these virtual tags?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are not overrides so I think they need the default values. overrides of a virtual method don't need to re-specify them.

const DlPaint* paint = nullptr,
const DlImageFilter* backdrop = nullptr) override;
const DlImageFilter* backdrop = nullptr,
int64_t backdrop_id = -1) override;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto - default values probably unnecessary.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, in this case there is a lot of code calling directly to a DlBuilder not through the DlCanvas interface so perhaps it needs them for ease of use. That need would disappear if we did something more like Flutter and Skia and have the Builder simply represent the recording process and provide an actual DlCanvas implementation via a getter rather than via direct implementation...?

const SaveLayerOptions options,
const DlImageFilter* backdrop = nullptr) = 0;
const DlImageFilter* backdrop = nullptr,
int64_t backdrop_id = -1) = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

This used to be the front end and back end for DLs. Now that it is just the backend I think all default values throughout this interface are obsolete. But, maybe there are a couple of unit tests that still call these directly? I'll watch for this as I de-skiafy...

Copy link
Contributor

Choose a reason for hiding this comment

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

(The DlOp records used during dispatch shouldn't need any defaults for the calls they make.)

Copy link
Contributor

Choose a reason for hiding this comment

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

I filed flutter/flutter#156617 to remind me to clean this up.

const DlPaint* paint = nullptr,
const DlImageFilter* backdrop = nullptr) override;
const DlImageFilter* backdrop = nullptr,
int64_t backdrop_id = -1) override;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we use this directly (as in on an instance of this class as opposed to casting it to a DlCanvas where the defaults already exist)? Actually we might in the embedders or other platform code.

@jonahwilliams
Copy link
Contributor Author

Re: the 64 bit integer. Dart ints are 64 bits and signed, so using a smaller int means we'd need to do a runtime check or throw an error (folks could reasonably use Dart max int has an input). We could remove the -1 and make it nullable instead, I need to look into how to pass that through dart::ffi.

@gaaclarke
Copy link
Member

Since we didn't end up writing a design doc, I know we talked about it that one day over gvc but can you write down (or link to someplace where it is already written) why we needed to have users specify this explicitly and we couldn't come up with a heuristic to do this automatically?

@jonahwilliams
Copy link
Contributor Author

Since we didn't end up writing a design doc..

  1. The invalidation heuristics for backdrop filters would be expensive to track. We'd need to measure dirty regions between any two backdrops and that means recording/intersecting the bounds for all draws.
  2. The huerstics fail if filters like blurs are too close as the sample region will go far out of the output region (and this cannot be controlled).

@gaaclarke
Copy link
Member

Did you consider making the Dart API look like this instead of specifying an integer? This captures a scope more succinctly and doesn't require managing ids. The downside is that it is impossible to mix and match the blur layer across the code (though that makes the code easier to follow). Under the covers you could keep the implementation the same if you wanted.

Widget build(BuildContext context) {
  return BackdropGroup(
    children:[
      BackdropFilter(...),
      BackdropFilter(...),
    ]
  );
}

@jonahwilliams
Copy link
Contributor Author

I discussed this a bit with @flar and we both like the idea of separating the Backdrop widget from a backdrop-reading widget. So something like:

Widget build(BuildContext context) {
  return BackdropGroup(
    children:[
      BackdropFilter(parent: BackdropGroup.of(context)),
      BackdropFilter(...),
    ]
  );
}

But that is all Dart API that would be constructed at the framework layer. The implementation should look almost identical.

@jonahwilliams
Copy link
Contributor Author

I modified the backdrop filter blur benchmark application to place a single blur in the top left and in the bottom right corner. This should in theory be the worst possible case for backdrop id. This test was on an iPhone and I made sure to sync after the BDF fix from @gaaclarke and with partial repaint disabled. GPU numbers are performance state medium

No backdrop Id:

2.13 ms GPU Time / ~2.5 ms Raster time.

Backdrop Id w/ shared filter (current design)

1.55 ms GPU Time / ~2.5 ms Raster time

Backdrop id w/ shared input but not shared filter*:

2.0 ms GPU Time / ~2.5 ms Raster time

  • can be tested by setting the sigmas to be slightly different

So for the two small filter case, there really isn't any hit to CPU time. But computing the blur once is still faster than doing it twice, even over a larger area. These numbers will likely be diffrent across devices though.

@flutter-dashboard
Copy link

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

Changes reported for pull request #55701 at sha 95abc95

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!

@gaaclarke

This comment was marked as outdated.

// layer once.
if (backdrop_data->all_filters_equal &&
!backdrop_data->shared_filter_snapshot.has_value()) {
// TODO(jonahwilliams): compute minimum input hint.
Copy link
Member

Choose a reason for hiding this comment

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

This needs an issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ahh forgot to update it, I had filled flutter/flutter#157110

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@jonahwilliams jonahwilliams added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 18, 2024
@auto-submit auto-submit bot merged commit ad9e4fe into flutter:main Oct 18, 2024
35 checks passed
@jonahwilliams jonahwilliams deleted the dirty_bdf branch October 18, 2024 15:38
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Oct 18, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Oct 18, 2024
@jonahwilliams
Copy link
Contributor Author

reason for revert: unexpected framework golden.

@jonahwilliams jonahwilliams added the revert Label used to revert changes in a closed and merged pull request. label Oct 18, 2024
@auto-submit
Copy link
Contributor

auto-submit bot commented Oct 18, 2024

Unable to create the revert pull request due to ProcessException: Standard out
Auto-merging impeller/display_list/canvas.cc
CONFLICT (content): Merge conflict in impeller/display_list/canvas.cc
Auto-merging impeller/display_list/canvas.h
Auto-merging impeller/entity/entity_pass_target.cc
Auto-merging impeller/entity/entity_pass_target.h
Auto-merging testing/impeller_golden_tests_output.txt
Standard error
error: could not revert ad9e4fe... [Impeller] add mechanism for sharing bdf inputs. (#55701)
hint: After resolving the conflicts, mark them with
hint: "git add/rm ", then run
hint: "git revert --continue".
hint: You can instead skip this commit with "git revert --skip".
hint: To abort and get back to the state before "git revert",
hint: run "git revert --abort".
Command: git revert --no-edit -m 1 ad9e4fe

@auto-submit auto-submit bot removed the revert Label used to revert changes in a closed and merged pull request. label Oct 18, 2024
jonahwilliams pushed a commit to jonahwilliams/engine that referenced this pull request Oct 18, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Oct 19, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Oct 19, 2024
jonahwilliams pushed a commit to flutter/flutter that referenced this pull request Oct 19, 2024
…157215)

flutter/engine@76d310e...dd37446

2024-10-19 [email protected] Move keymap from
FlKeyboardViewDelegate to FlKeyboardManager (flutter/engine#55942)
2024-10-19 [email protected] [UI] fix scene builder parameter
naming. (flutter/engine#55969)
2024-10-19 [email protected] iOS: Improve thread safety of first frame
callback (flutter/engine#55966)
2024-10-18 [email protected] iOS: Fix flaky tests (remove timeouts)
(flutter/engine#55961)
2024-10-18 [email protected] [Impeller] allocate the impeller
onscreen texture from the render target cache. (flutter/engine#55943)
2024-10-18 [email protected] Roll Fuchsia Linux SDK from
9F_NaKPd2twhbPwP7... to tNQZ8d5mRYpe3--lk... (flutter/engine#55963)
2024-10-18 [email protected] Started filtering
out close line segments in rrect polylines. (flutter/engine#55929)
2024-10-18 [email protected] [Impeller] libImpeller: Allow custom
font registrations. (flutter/engine#55934)
2024-10-18 [email protected] Re-reland "iOS: Migrate FlutterEngine to
ARC" (flutter/engine#55962)
2024-10-18 [email protected] [Impeller] libImpeller: Add a README.
(flutter/engine#55940)
2024-10-18 [email protected] iOS: Eliminate needless profiler metrics
ivar (flutter/engine#55957)
2024-10-18 98614782+auto-submit[bot]@users.noreply.github.com Reverts
"[Impeller] one descriptor pool per frame. (#55939)"
(flutter/engine#55959)
2024-10-18 [email protected] Revert "Reland "iOS: Migrate FlutterEngine
to ARC" (#55937)" (flutter/engine#55954)
2024-10-18 [email protected] Roll Dart SDK from
993d3069f42e to a51df90298ca (7 revisions) (flutter/engine#55951)
2024-10-18 [email protected] [engine] add back opt out for merged
threads. (flutter/engine#55952)
2024-10-18 [email protected] [Impeller] one descriptor pool per
frame. (flutter/engine#55939)
2024-10-18 [email protected] [Impeller] add mechanism for sharing
bdf inputs. (flutter/engine#55701)

Also rolling transitive DEPS:
  fuchsia/sdk/core/linux-amd64 from 9F_NaKPd2twh to tNQZ8d5mRYpe

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] 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
nick9822 pushed a commit to nick9822/flutter that referenced this pull request Dec 18, 2024
Introduces a mechanism to allow backdrop filters to 1) share backdrop inputs and 2) fuse filter applications for faster blurs.

This is a proposed solution to flutter#131568

Implemented:
* Developer can specify a "backdrop id" which indicates that a backdrop layer should share the input texture and potentially cached filter for a layer.
* Removes second save layer for each backdrop filter
* Removes save layer trace event for backdrop filter
* Can fuse backdrop filters if there is more than one identical filter

TBD:
* Adjust heruristic to avoid applying bdf filter to entire screen

Suggestions: applying a bdf should be a distinct operation from a save layer in the DL builder/dispatcher. The saveLayer implmenentation in the impeller dispatcher is super convoluted because it needs to handle both.

### Video

Video starts with normal bdf then I hot reload to specify that the bdfs share inputs/filters. This is running on a pixel 8 pro

Change to the macrobenchmark app is just:
```dart
  Widget build(BuildContext context) {
    Widget addBlur(Widget child, bool shouldBlur) {
      if (shouldBlur) {
        return ClipRect(
          child: BackdropFilter(
            filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
            backdropId: 1, // Added ID
            child: child,
          ),
        );
      } else {
        return child;
      }
    }
```

https://github.com/user-attachments/assets/22707f97-5825-43f1-91b4-1a02a43437f5

Requires framework changes in https://github.com/jonahwilliams/flutter/pull/new/backdrop_id
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

autosubmit Merge PR when tree becomes green via auto submit App e: impeller platform-web Code specifically for the web engine will affect goldens

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants