Skip to content

Commit

Permalink
feat: optimize raster performance on Animated images. (#513)
Browse files Browse the repository at this point in the history
The I/O bottleneck can impact the performance of the raster thread in
Flutter.

When WebF pages contain multiple GIF images, each frame of these GIFs
requires the I/O thread to fetch its next frame, potentially turning the
I/O thread into a bottleneck.

This PR introduces an intersectionObserverLayer for each GIF image,
monitoring the visibility of each animated image. If an ImageElement
scrolls outside the viewport, the imageFrame event listeners will be
detached from the imageStreamListener, and the next frame request for
that image will be paused.

With this update, only the animated images visible in the viewport will
request the next frame, significantly reducing the load on the I/O
thread.

Related issue for Flutter:
flutter/flutter#135443
  • Loading branch information
andycall authored Nov 7, 2023
1 parent f907dab commit fd690df
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 11 deletions.
2 changes: 1 addition & 1 deletion webf/example/macos/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "The Flutter Authors";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
26 changes: 17 additions & 9 deletions webf/lib/src/html/img.dart
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,23 @@ class ImageElement extends Element {
_isListeningStream = true;
}

bool _didWatchAnimationImage = false;
void _watchAnimatedImageWhenVisible() {
RenderReplaced? renderReplaced = renderBoxModel as RenderReplaced?;
if (_isListeningStream && !_didWatchAnimationImage) {
_stopListeningStream();
renderReplaced?.addIntersectionChangeListener(_handleIntersectionChange);
_didWatchAnimationImage = true;
}
}

@override
void dispose() async {
super.dispose();

RenderReplaced? renderReplaced = renderBoxModel as RenderReplaced?;
renderReplaced?.removeIntersectionChangeListener(_handleIntersectionChange);

// Stop and remove image stream reference.
_stopListeningStream();
_cachedImageStream = null;
Expand Down Expand Up @@ -338,6 +351,9 @@ class ImageElement extends Element {
// When appear
if (entry.isIntersecting) {
_updateImageDataLazyCompleter?.complete();
_listenToStream();
} else {
_stopListeningStream();
}
}

Expand Down Expand Up @@ -485,6 +501,7 @@ class ImageElement extends Element {
// Multi frame image should wrap a repaint boundary for better composite performance.
if (_frameCount > 2) {
forceToRepaintBoundary = true;
_watchAnimatedImageWhenVisible();
}

_updateRenderObject(image: imageInfo.image);
Expand Down Expand Up @@ -525,15 +542,8 @@ class ImageElement extends Element {
_updateImageDataLazyCompleter = completer;

RenderReplaced? renderReplaced = renderBoxModel as RenderReplaced?;
FlutterView ownerFlutterView = ownerDocument.controller.ownerFlutterView;
renderReplaced
?..isInLazyRendering = true
// Expand the intersecting area to preload images before they become visible to users.
..intersectPadding = Rect.fromLTRB(
ownerFlutterView.physicalSize.width,
ownerFlutterView.physicalSize.height,
ownerFlutterView.physicalSize.width,
ownerFlutterView.physicalSize.height)
// When detach renderer, all listeners will be cleared.
..addIntersectionChangeListener(_handleIntersectionChange);

Expand All @@ -548,8 +558,6 @@ class ImageElement extends Element {

renderReplaced = renderBoxModel as RenderReplaced?;
renderReplaced?.isInLazyRendering = false;
renderReplaced
?.removeIntersectionChangeListener(_handleIntersectionChange);
}

SchedulerBinding.instance.addPostFrameCallback((timeStamp) {
Expand Down

0 comments on commit fd690df

Please sign in to comment.