-
Notifications
You must be signed in to change notification settings - Fork 27.3k
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
BackdropFilter performance issue #32804
Comments
applying blur filters are very intensive operations (or so i've heard) - are you perhaps rebuilding the FrostedBox's parent widgets with many setState calls? |
Nope, I'm just dragging the cells. |
Hi, I have the same problem, I have a list of items, and there are only 2 widgets that have a freeze effect, this is the search string and the bottom panel, when scrolling down, these widgets are hidden and the list rotates smoothly. As soon as we turn over the list and two widgets appear on the screen, the FPS immediately drops and the list starts to lag + sometimes elements with this effect blink (disappear suddenly and redraw) The build of the search, the bottom is only called when they appear, so I wrapped the whole widget with a freeze effect in the RepaintBoundary, but that did not help me in any way. |
I have similar problem - blurred widget put into ListView significantly reduces scroll speed and the blur effect sometimes disappears while scrolling. Tested on Android and iPhone simulators. UPD: Checked on iPhone Xr (debug) - works fast, but wrong. Blur effect looses during scrolling. Definitely a bug! |
This is indeed an issue that we're aware of. There will be an easy solution to it once #32662 is fixed. Before that, @flar may be able provide you some sample code (he just showed me 9x performance improvements) of how to snapshot the BackdropFilter once manully so Flutter won't draw it in every frame. |
(Ignore the close, I missed the button.) @liyuqian is mistaken about what I was showing him. I have no solution yet, I was just writing a test app to demonstrate the performance cost of BackdropFilter before starting to look for a solution (and 9x was specific to my simple test case and not indicative of the cost in every situation...) |
Possible workaround:
|
Here's an example of 9x speedup (from ~50ms to ~5ms per frame on my Moto G4): Note that it assumes a static and opaque background. Otherwise the snapshot cache won't render the same. BTW, the example above also shows two problems that Flutter currently has:
|
I have the same problem, |
We've identified a BackdropFilter performance regression (#36064) that started from May (71d370f). In my local test, it reduces the rendering speed by ~3x (from 9ms to 30ms). Before that regression is fixed, please try something like #32804 (comment) to mitigate the issue. The solution there is likely to benefit your app's performance after the fix if the content of your BackdropFilter doesn't change. |
I have an animation on my blur radius, |
@liyuqian any ETA for this issue? Your solution not working because of the |
@sooxiaotong : The BackdropFilter performance regression in #36064 has already been fixed, and it should be now 2-3x faster. Can you please try the master branch and see if that satisfies your need? |
@liyuqian yep did notice some diff, but any ideas how to fix this issue? maybe can refer to UIVisualEffectView approach or GPU acceleration? (pure guessing |
@imaNNeoFighT the layer caching won't help if your blur radius changes, but the recent regression in #36064 that was fixed should help a bit. |
After discussions with @liyuqian I'm going to look at the effect of caching the background (to avoid the readback) but applying the filter on the fly (which is GPU accelerated and should be pretty quick if we can avoid a rendertarget context switch). If that shows significant performance improvement then it will help even if you animate the blur radius as in @imaNNeoFighT |
Any progress on the fix? I am experiencing the same performance issue. |
We have a workaround that @liyuqian posted above in this comment but any solution to reduce the frequency of automatically applying a filter to the background is going to face a number of issues that we are actively investigating. Right now there are a number of elements of the Flutter layered design that need to be improved before we can track the stability of the background behind the BackdropFilter and our investigations are underway both to identify all of the issues and to see what possible solutions we might have. I will hopefully submit some related issues over the next week or two to outline some of the improvements which won't immediately impact elimination of the blur operations per frame until most or all of them are addressed, but they will then be independently trackable. For example:
|
We have recently implemented the ImageFiltered widget which may be a better match for some of the use cases of BackdropFilter. It is a very simply widget which applies an ImageFilter to its child rather than to "everything rendered in the scene behind it" - which lets us focus our attempts to cache the results better. See #47489 for more information. |
I've created an alternate version of this example using an ImageFiltered widget. The new example has a checkbox to switch back and forth between the existing workaround using a snapshot and using the new ImageFiltered widget instead (see the https://gist.github.com/flar/efe8f4aadeb41f587a8d86a07c483fe4 |
I have a return FlexibleSpaceBar.createSettings(
minExtent: minExtent,
maxExtent: maxExtent,
currentExtent: maxExtent,
child: Stack(
children: <Widget>[
ClipRect(
child: BackdropFilter(
filter: ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
color: Theme.of(context).scaffoldBackgroundColor.withOpacity(0.8),
),
),
),
AppBar(
leading: leading,
title: title,
bottom: bottom,
actions: actions,
backgroundColor: Colors.transparent,
),
],
),
); |
I can subscribe to this issue, using the following code:
The result is "debug mode" level of performance in release mode for low-end devices while ok in mid-range and up on Android. |
You might try:
|
Can confirm that using ImageFiltered instead of BackdropFilter solved it for me |
Can anyone make a Blur Background "under" the child (not covering the child) with ImageFiltered? seems like Imagefiltered blurs the child, it can't make a transparent blur background... |
You could make a custom Widget which stack 2 containers, using Stack class. |
I know what you mean
Problem is the "child", i can't just simply put color container, it won't blur, you need to put actual image or other thing to make it blur... |
Yeah, sadly that's true :( . |
@vadrian89 It's ok, thanks for the suggestion, ImageFiltered truely better than BackdropFilter in performance comparison, just wanna know the final approach of blur background, not just blurring the child, then it will be perfect, although i got my alternative fix for this situation, but i am seeking the right answer~ |
@dvlj can i know your alternative fix and is there any progress or something i can follow |
If frosted container effect is needed and performance is an issue, could this problem now be resolved with 3.7 using fragment shaders? |
any updates for this issue ? |
After playing around with Impeller, the performance degradation is even worse. Using more then two ImageFilter.blur widgets in a ListView results in a noticeable frame rate drop (iPhone 12, release mode) |
@vjamrich Could you provide some more details, ideally in a new GitHub issue (so we can track any Impeller-specific aspects of it)? Please include the version of Flutter you used, and if possible a snippet of code that reproduces the performance issue. Thanks! |
Is there any update on solution yet? I've tried to use the workaround, but the snapshot I get is without any blur effect. That does solve the performance issues, however the solution is basically just removing the blur effect I need to display. I've rewritten the code a bit so that it works with latest dart. I also added a little customization, because I use the blur effect as a background of end drawer, which is where I encountered debug mode like performance of scroll animation in release mode. Is there any other workaround or update on solution? I am making an Android TV app, and the optimization should be maximized, as the TVs don't really have as much power as phones do. Here's how I am using the
|
Reproduced this issue on the latest Flutter stable/master channels using this sample code (source from #32804 (comment) but updated it to be compatible with new Flutter/Dart changes). Demo on Realme 6, Android 11 in profile mode on Flutter stable 3.19.0Screen.Recording.2024-02-16.at.19.08.01.movThe Timeline trace Json: dart_devtools_2024-02-16_19_08_09.947.json Sample codeimport 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
CachedFrostedBox(
opaqueBackground: Container(
color: Colors.white,
child: Text('0' * 10000),
),
sigmaX: 2.0,
sigmaY: 2.0,
child: Container(
alignment: Alignment.center,
child: Text('Hello'),
),
),
Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: LinearProgressIndicator(),
),
),
],
),
),
showPerformanceOverlay: true,
);
}
}
class Foo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
fit: StackFit.expand,
children: <Widget>[
CachedFrostedBox(
opaqueBackground: Container(
color: Colors.white,
child: Text('0' * 10000),
),
sigmaX: 2.0,
sigmaY: 2.0,
child: Container(
alignment: Alignment.center,
child: Text('Hello'),
),
),
Center(
child: Padding(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: LinearProgressIndicator(),
),
),
],
);
}
}
class CachedFrostedBox extends StatefulWidget {
CachedFrostedBox({required this.child, this.sigmaX = 8, this.sigmaY = 8, required this.opaqueBackground})
: this.frostBackground = Stack(
children: <Widget>[
opaqueBackground,
ClipRect(
child: BackdropFilter(
filter: ui.ImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY),
child: new Container(
decoration: new BoxDecoration(
color: Colors.white.withOpacity(0.1),
)),
)),
],
);
final Widget child;
final double sigmaY;
final double sigmaX;
/// This must be opaque so the backdrop filter won't access any colors beneath this background.
final Widget opaqueBackground;
/// Blur applied to the opaqueBackground. See the constructor.
final Widget frostBackground;
@override
State<StatefulWidget> createState() {
return CachedFrostedBoxState();
}
}
class CachedFrostedBoxState extends State<CachedFrostedBox> {
final GlobalKey _snapshotKey = GlobalKey();
Image? _backgroundSnapshot;
bool _snapshotLoaded = false;
bool _skipSnapshot = false;
void _snapshot(Duration _) async {
final RenderRepaintBoundary renderBackground = _snapshotKey.currentContext?.findRenderObject() as RenderRepaintBoundary;
final ui.Image image = await renderBackground.toImage(
pixelRatio: WidgetsBinding.instance.window.devicePixelRatio,
);
// !!! The default encoding rawRgba will throw exceptions. This bug is introducing a lot
// of encoding/decoding work.
final ByteData? imageByteData = await image.toByteData(format: ui.ImageByteFormat.png);
setState(() {
_backgroundSnapshot = Image.memory(imageByteData!.buffer.asUint8List());
});
}
@override
Widget build(BuildContext context) {
Widget frostedBackground;
if (_backgroundSnapshot == null || _skipSnapshot) {
frostedBackground = RepaintBoundary(
key: _snapshotKey,
child: widget.frostBackground,
);
if (!_skipSnapshot) {
SchedulerBinding.instance.addPostFrameCallback(_snapshot);
}
} else {
// !!! We don't seem to have a way to know when IO thread
// decoded the image.
if (!_snapshotLoaded) {
frostedBackground = widget.frostBackground;
Future.delayed(Duration(seconds: 1), () {
setState(() {
_snapshotLoaded = true;
});
});
} else {
frostedBackground = Offstage();
}
}
return Stack(
children: <Widget>[
frostedBackground,
if (_backgroundSnapshot != null) _backgroundSnapshot!,
widget.child,
GestureDetector(onTap: () {
setState(() {
_skipSnapshot = !_skipSnapshot;
});
}),
],
);
}
}
flutter doctor -v (stable and master)[✓] Flutter (Channel stable, 3.19.0, on macOS 14.1 23B74 darwin-x64, locale en-VN)
• Flutter version 3.19.0 on channel stable at /Users/huynq/Documents/GitHub/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision bae5e49bc2 (2 days ago), 2024-02-13 17:46:18 -0800
• Engine revision 04817c99c9
• Dart version 3.3.0
• DevTools version 2.31.1
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
• Android SDK at /Users/huynq/Library/Android/sdk
• Platform android-34, build-tools 34.0.0
• ANDROID_HOME = /Users/huynq/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15A507
• CocoaPods version 1.14.3
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2023.1)
• Android Studio at /Applications/Android Studio Hedgehog.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314)
[✓] Android Studio (version 2022.2)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• android-studio-dir = /Applications/Android Studio.app/
• Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
[✓] VS Code (version 1.86.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.82.0
[✓] Connected device (3 available)
• RMX2001 (mobile) • EUYTFEUSQSRGDA6D • android-arm64 • Android 11 (API 30)
• macOS (desktop) • macos • darwin-x64 • macOS 14.1 23B74 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 121.0.6167.160
! Error: Browsing on the local area network for iPad. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources
• All expected network resources are available.
• No issues found! [!] Flutter (Channel master, 3.20.0-7.0.pre.79, on macOS 14.1 23B74 darwin-x64, locale en-VN)
• Flutter version 3.20.0-7.0.pre.79 on channel master at /Users/huynq/Documents/GitHub/flutter_master
! Warning: `flutter` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path.
! Warning: `dart` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path.
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 7b9f7be7f6 (38 minutes ago), 2024-02-15 18:13:21 -0800
• Engine revision bc4dd534a0
• Dart version 3.4.0 (build 3.4.0-143.0.dev)
• DevTools version 2.33.0-dev.6
• If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
• Android SDK at /Users/huynq/Library/Android/sdk
• Platform android-34, build-tools 34.0.0
• ANDROID_HOME = /Users/huynq/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15A507
• CocoaPods version 1.14.3
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2023.1)
• Android Studio at /Applications/Android Studio Hedgehog.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b1000.6-10550314)
[✓] Android Studio (version 2022.2)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• android-studio-dir = /Applications/Android Studio.app/
• Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
[✓] VS Code (version 1.86.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.82.0
[✓] Connected device (4 available)
• RMX2001 (mobile) • EUYTFEUSQSRGDA6D • android-arm64 • Android 11 (API 30)
• iPad (mobile) • 00008103-000A1464346A201E • ios • iOS 17.2 21C62
• macOS (desktop) • macos • darwin-x64 • macOS 14.1 23B74 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 121.0.6167.160
[✓] Network resources
• All expected network resources are available.
! Doctor found issues in 1 category. |
We have a number of blur optimizations in progress in the context of Impeller, and expect to see improvements to blurs over the coming stable releases this year. Even with these optimizations, screens containing multiple backdrop filter blurs will take us more time to optimize well. Having said that though, we expect that you will continue to observe that backdrop filter blurs are one of the most expensive operations you can perform in a Flutter app. There is likely very little that can be done to avoid low-to-mid range Android phones struggling with these kinds of blurs. Android TVs are less likely to provide good results for this widget. I believe this guidance is essentially the same as the guidance here for native Android app development: https://source.android.com/docs/core/display/window-blurs. Our observation has been that iPhones are better able to cope with blurs in both Flutter and native development. The workarounds suggested above are good suggestions. Additionally, I'd suggest poking around in the Wonderous app (code here), to see how it handles differences between iOS and Android regarding blurring. |
## Overview - Fix lagging event detail issues Reference issue: flutter/flutter#32804
Hi, I'm using a BackdropFilter widget as a cell background in a list.
But this widget totally cripples the application, the performances are very bad and the UI lags very much.
This is my custom widget:
It seems that the BackdropFilter constantly repaints itself. I've tried to use a stack to reduce the amount of things it rebuilds, but still, the lag is very high.
I think that the problem is that every time I scroll the list, the blur effect on the single cell it's rebuilded costing a lot of performances
Any tip? Am'I using the wrong widget?
The text was updated successfully, but these errors were encountered: