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

Commit 8b5b3be

Browse files
Reverts "Reland "[canvaskit] Further improve overlay optimization by splitting pictures" (#55402)" (#55456)
Reverts: #55402 Initiated by: chingjun Reason for reverting: caused internal tests to fail. See b/369740500 for more details. Original PR Author: harryterkelsen Reviewed By: {yjbanov} This change reverts the following previous change: This enhances the overlay optimization by delaying combining pictures to get tighter bounds for the pictures that make up the scene, enabling more sophisticated optimization since we can determine if they intersect with platform views on a per-picture basis. Fixes flutter/flutter#149863 On a Macbook in Chrome in an example app with an infinite scrolling grid of platform views, this brings the ratio of dropped frames from 93% to 55% (roughly 4 fps to 30 fps). This is a reland of #54878 with a fix for scenes with pictures that are eventually entirely clipped out. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent fc6c852 commit 8b5b3be

File tree

10 files changed

+656
-1149
lines changed

10 files changed

+656
-1149
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43589,7 +43589,6 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.da
4358943589
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart + ../../../flutter/LICENSE
4359043590
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart + ../../../flutter/LICENSE
4359143591
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart + ../../../flutter/LICENSE
43592-
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart + ../../../flutter/LICENSE
4359343592
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart + ../../../flutter/LICENSE
4359443593
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/multi_surface_rasterizer.dart + ../../../flutter/LICENSE
4359543594
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart + ../../../flutter/LICENSE
@@ -46470,7 +46469,6 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart
4647046469
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer.dart
4647146470
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart
4647246471
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart
46473-
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart
4647446472
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart
4647546473
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/multi_surface_rasterizer.dart
4647646474
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart

lib/web_ui/lib/src/engine.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export 'engine/canvaskit/image_web_codecs.dart';
3333
export 'engine/canvaskit/layer.dart';
3434
export 'engine/canvaskit/layer_scene_builder.dart';
3535
export 'engine/canvaskit/layer_tree.dart';
36-
export 'engine/canvaskit/layer_visitor.dart';
3736
export 'engine/canvaskit/mask_filter.dart';
3837
export 'engine/canvaskit/multi_surface_rasterizer.dart';
3938
export 'engine/canvaskit/n_way_canvas.dart';

lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart

Lines changed: 58 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import '../svg.dart';
1414
import '../util.dart';
1515
import '../vector_math.dart';
1616
import 'canvas.dart';
17-
import 'layer.dart';
1817
import 'overlay_scene_optimizer.dart';
1918
import 'painting.dart';
2019
import 'path.dart';
@@ -67,9 +66,6 @@ class HtmlViewEmbedder {
6766
/// Returns the most recent rendering. Only used in tests.
6867
Rendering get debugActiveRendering => _activeRendering;
6968

70-
/// If [debugOverlayOptimizationBounds] is true, this canvas will draw
71-
/// semitransparent rectangles showing the computed bounds of the platform
72-
/// views and pictures in the scene.
7369
DisplayCanvas? debugBoundsCanvas;
7470

7571
/// The size of the frame, in physical pixels.
@@ -79,23 +75,27 @@ class HtmlViewEmbedder {
7975
_frameSize = size;
8076
}
8177

82-
/// Returns a list of recording canvases which the pictures in the upcoming
83-
/// paint step will be drawn into. These recording canvases are combined into
84-
/// an N-way canvas for the rasterizer to record clip and transform operations
85-
/// during the measure step.
86-
Iterable<CkCanvas> getPictureCanvases() {
87-
return _context.measuringPictureRecorders.values
88-
.map((CkPictureRecorder r) => r.recordingCanvas!);
89-
}
90-
91-
/// Returns a list of canvases for the optimized rendering. These are used in
92-
/// the paint step.
93-
Iterable<CkCanvas> getOptimizedCanvases() {
94-
return _context.optimizedCanvasRecorders!
78+
/// Returns a list of canvases which will be overlaid on top of the "base"
79+
/// canvas after a platform view is composited into the scene.
80+
///
81+
/// The engine asks for the overlay canvases immediately before the paint
82+
/// phase, after the preroll phase. In the preroll phase we must be
83+
/// conservative and assume that every platform view which is prerolled is
84+
/// also composited, and therefore requires an overlay canvas. However, not
85+
/// every platform view which is prerolled ends up being composited (it may be
86+
/// clipped out and not actually drawn). This means that we may end up
87+
/// overallocating canvases. This isn't a problem in practice, however, as
88+
/// unused recording canvases are simply deleted at the end of the frame.
89+
Iterable<CkCanvas> getOverlayCanvases() {
90+
return _context.pictureRecordersCreatedDuringPreroll
9591
.map((CkPictureRecorder r) => r.recordingCanvas!);
9692
}
9793

9894
void prerollCompositeEmbeddedView(int viewId, EmbeddedViewParams params) {
95+
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
96+
pictureRecorder.beginRecording(ui.Offset.zero & _frameSize.toSize());
97+
_context.pictureRecordersCreatedDuringPreroll.add(pictureRecorder);
98+
9999
// Do nothing if the params didn't change.
100100
if (_currentCompositionParams[viewId] == params) {
101101
// If the view was prerolled but not composited, then it needs to be
@@ -109,38 +109,30 @@ class HtmlViewEmbedder {
109109
_viewsToRecomposite.add(viewId);
110110
}
111111

112-
/// Record that a picture recorder is needed for [picture] to be measured.
113-
void prerollPicture(PictureLayer picture) {
114-
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
115-
pictureRecorder.beginRecording(ui.Offset.zero & _frameSize.toSize());
116-
_context.measuringPictureRecorders[picture] = pictureRecorder;
117-
}
118-
119-
/// Returns the canvas that was created to measure [picture].
120-
CkCanvas getMeasuringCanvasFor(PictureLayer picture) {
121-
return _context.measuringPictureRecorders[picture]!.recordingCanvas!;
122-
}
123-
124-
/// Adds the picture recorder associated with [picture] to the unoptimized
125-
/// scene.
126-
void addPictureToUnoptimizedScene(PictureLayer picture) {
127-
final CkPictureRecorder recorder =
128-
_context.measuringPictureRecorders[picture]!;
129-
_context.sceneElements.add(PictureSceneElement(picture, recorder));
130-
}
131-
132112
/// Prepares to composite [viewId].
133-
void compositeEmbeddedView(int viewId) {
113+
///
114+
/// If this returns a [CkCanvas], then that canvas should be the new leaf
115+
/// node. Otherwise, keep the same leaf node.
116+
CkCanvas? compositeEmbeddedView(int viewId) {
134117
// Ensure platform view with `viewId` is injected into the `rasterizer.view`.
135118
rasterizer.view.dom.injectPlatformView(viewId);
136119

120+
final int overlayIndex = _context.viewCount;
137121
_compositionOrder.add(viewId);
138-
_context.sceneElements.add(PlatformViewSceneElement(viewId));
122+
_context.viewCount++;
123+
124+
CkPictureRecorder? recorderToUseForRendering;
125+
if (overlayIndex < _context.pictureRecordersCreatedDuringPreroll.length) {
126+
recorderToUseForRendering =
127+
_context.pictureRecordersCreatedDuringPreroll[overlayIndex];
128+
_context.pictureRecorders.add(recorderToUseForRendering);
129+
}
139130

140131
if (_viewsToRecomposite.contains(viewId)) {
141132
_compositeWithParams(viewId, _currentCompositionParams[viewId]!);
142133
_viewsToRecomposite.remove(viewId);
143134
}
135+
return recorderToUseForRendering?.recordingCanvas;
144136
}
145137

146138
void _compositeWithParams(int platformViewId, EmbeddedViewParams params) {
@@ -363,57 +355,14 @@ class HtmlViewEmbedder {
363355
sceneHost.append(_svgPathDefs!);
364356
}
365357

366-
/// Optimizes the scene to use the fewest possible canvases. This sets up
367-
/// the final paint pass to paint the pictures into the optimized canvases.
368-
void optimizeRendering() {
369-
final Map<CkPicture, PictureLayer> scenePictureToRawPicture =
370-
<CkPicture, PictureLayer>{};
371-
final Iterable<SceneElement> unoptimizedRendering =
372-
_context.sceneElements.map<SceneElement>((SceneElement element) {
373-
if (element is PictureSceneElement) {
374-
final CkPicture scenePicture = element.pictureRecorder.endRecording();
375-
if (scenePicture.cullRect.isEmpty) {
376-
element.picture.isCulled = true;
377-
}
378-
element.scenePicture = scenePicture;
379-
scenePictureToRawPicture[scenePicture] = element.picture;
380-
return element;
381-
} else {
382-
return element;
383-
}
384-
});
358+
Future<void> submitFrame(CkPicture basePicture) async {
359+
final List<CkPicture> pictures = <CkPicture>[basePicture];
360+
for (final CkPictureRecorder recorder in _context.pictureRecorders) {
361+
pictures.add(recorder.endRecording());
362+
}
385363
Rendering rendering = createOptimizedRendering(
386-
unoptimizedRendering, _currentCompositionParams);
364+
pictures, _compositionOrder, _currentCompositionParams);
387365
rendering = _modifyRenderingForMaxCanvases(rendering);
388-
_context.optimizedRendering = rendering;
389-
// Create new picture recorders for the optimized render canvases and record
390-
// which pictures go in which canvas.
391-
final List<CkPictureRecorder> optimizedCanvasRecorders =
392-
<CkPictureRecorder>[];
393-
final Map<PictureLayer, CkPictureRecorder> pictureToOptimizedCanvasMap =
394-
<PictureLayer, CkPictureRecorder>{};
395-
for (final RenderingRenderCanvas renderCanvas in rendering.canvases) {
396-
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
397-
pictureRecorder.beginRecording(ui.Offset.zero & _frameSize.toSize());
398-
optimizedCanvasRecorders.add(pictureRecorder);
399-
for (final CkPicture picture in renderCanvas.pictures) {
400-
pictureToOptimizedCanvasMap[scenePictureToRawPicture[picture]!] =
401-
pictureRecorder;
402-
}
403-
}
404-
_context.optimizedCanvasRecorders = optimizedCanvasRecorders;
405-
_context.pictureToOptimizedCanvasMap = pictureToOptimizedCanvasMap;
406-
}
407-
408-
/// Returns the canvas that this picture layer should draw into in the
409-
/// optimized scene.
410-
CkCanvas getOptimizedCanvasFor(PictureLayer picture) {
411-
assert(_context.optimizedRendering != null);
412-
return _context.pictureToOptimizedCanvasMap![picture]!.recordingCanvas!;
413-
}
414-
415-
Future<void> submitFrame() async {
416-
final Rendering rendering = _context.optimizedRendering!;
417366
_updateDomForNewRendering(rendering);
418367
if (rendering.equalsForRendering(_activeRendering)) {
419368
// Copy the display canvases to the new rendering.
@@ -426,17 +375,13 @@ class HtmlViewEmbedder {
426375
_activeRendering = rendering;
427376

428377
final List<RenderingRenderCanvas> renderCanvases = rendering.canvases;
429-
int renderCanvasIndex = 0;
430378
for (final RenderingRenderCanvas renderCanvas in renderCanvases) {
431-
final CkPicture renderPicture = _context
432-
.optimizedCanvasRecorders![renderCanvasIndex++]
433-
.endRecording();
434379
await rasterizer.rasterizeToCanvas(
435-
renderCanvas.displayCanvas!, <CkPicture>[renderPicture]);
380+
renderCanvas.displayCanvas!, renderCanvas.pictures);
436381
}
437382

438383
for (final CkPictureRecorder recorder
439-
in _context.measuringPictureRecorders.values) {
384+
in _context.pictureRecordersCreatedDuringPreroll) {
440385
if (recorder.isRecording) {
441386
recorder.endRecording();
442387
}
@@ -448,11 +393,11 @@ class HtmlViewEmbedder {
448393
debugBoundsCanvas ??= rasterizer.displayFactory.getCanvas();
449394
final CkPictureRecorder boundsRecorder = CkPictureRecorder();
450395
final CkCanvas boundsCanvas = boundsRecorder.beginRecording(
451-
ui.Rect.fromLTWH(
452-
0,
453-
0,
454-
_frameSize.width.toDouble(),
455-
_frameSize.height.toDouble(),
396+
ui.Rect.fromLTWH(
397+
0,
398+
0,
399+
_frameSize.width.toDouble(),
400+
_frameSize.height.toDouble(),
456401
),
457402
);
458403
final CkPaint platformViewBoundsPaint = CkPaint()
@@ -958,45 +903,20 @@ class MutatorsStack extends Iterable<Mutator> {
958903
Iterable<Mutator> get reversed => _mutators;
959904
}
960905

961-
sealed class SceneElement {}
962-
963-
class PictureSceneElement extends SceneElement {
964-
PictureSceneElement(this.picture, this.pictureRecorder);
965-
966-
final PictureLayer picture;
967-
final CkPictureRecorder pictureRecorder;
968-
969-
/// The picture as it would be painted in the final scene, with clips and
970-
/// transforms applied. This is set by [optimizeRendering].
971-
CkPicture? scenePicture;
972-
}
973-
974-
class PlatformViewSceneElement extends SceneElement {
975-
PlatformViewSceneElement(this.viewId);
976-
977-
final int viewId;
978-
}
979-
980906
/// The state for the current frame.
981907
class EmbedderFrameContext {
982-
/// Picture recorders which were created d the final bounds of the picture in the scene.
983-
final Map<PictureLayer, CkPictureRecorder> measuringPictureRecorders =
984-
<PictureLayer, CkPictureRecorder>{};
985-
986-
/// List of picture recorders and platform view ids in the order they were
987-
/// painted.
988-
final List<SceneElement> sceneElements = <SceneElement>[];
989-
990-
/// The optimized rendering for this frame. This is set by calling
991-
/// [optimizeRendering].
992-
Rendering? optimizedRendering;
993-
994-
/// The picture recorders for the optimized rendering. This is set by calling
995-
/// [optimizeRendering].
996-
List<CkPictureRecorder>? optimizedCanvasRecorders;
997-
998-
/// A map from the original PictureLayer to the picture recorder it should go
999-
/// into in the optimized rendering. This is set by calling
1000-
/// [optimizedRendering].
1001-
Map<PictureLayer, CkPictureRecorder>? pictureToOptimizedCanvasMap;
908+
/// Picture recorders which were created during the preroll phase.
909+
///
910+
/// These picture recorders will be "claimed" in the paint phase by platform
911+
/// views being composited into the scene.
912+
final List<CkPictureRecorder> pictureRecordersCreatedDuringPreroll =
913+
<CkPictureRecorder>[];
914+
915+
/// Picture recorders which were actually used in the paint phase.
916+
///
917+
/// This is a subset of [_pictureRecordersCreatedDuringPreroll].
918+
final List<CkPictureRecorder> pictureRecorders = <CkPictureRecorder>[];
919+
920+
/// The number of platform views in this frame.
921+
int viewCount = 0;
1002922
}

0 commit comments

Comments
 (0)