-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
The overlay minimization fails in the situation described in the test below. It could do better.
Scenario
Consider the following scene:
The scene is structured into the following layers, listed in paint order:
- Picture 1 (big green square on the left)
- Platform view 1 (red square on the left)
- Picture 2 (small green square on the left)
- Picture 3 (big green square on the right)
- Platform view 2 (red square on the right)
- Picture 4 (small green square on the right)
Layers 1-3 form one cluster of layers, and 4-6 form a second cluster of layers.
Compositing this naively, you'd need the following stack of overlays:
- Picture 1
- Platform view 1
- Picture 2 and picture 3
- Platform view 2
- Picture 4
This is exactly what the web engine does today.
However, notice that the two clusters do not overlap. This means, that the paint order can be changed as follows:
- Picture 1 (big green square on the left)
- Picture 3 (big green square on the right)
- Platform view 1 (red square on the left)
- Platform view 2 (red square on the right)
- Picture 2 (small green square on the left)
- Picture 4 (small green square on the right)
Essentially, you "sink" picture 3 as far back as possible, below platform view 1 and picture 2 (safe to do since they don't overlap). You do the same for platform view 2. Now, to render this, you need fewer overlays:
- Picture 1 and picture 3
- Platform view 1 and platform view 2
- Picture 2 and picture 4
In this contrived example, the overlay count only goes from 5 to 3, but imagine 10 non-overlapping clusters like that. That will blow up the number of overlays today. However, you never need more than 3 overlays.
To reproduce, add the following test to the web engine's embedded_views_test.dart:
test('sinks platform view under the canvas if it does not overlap with the picture',
() async {
ui_web.platformViewRegistry.registerViewFactory(
'test-view',
(int viewId) => createDomHTMLDivElement()..className = 'platform-view',
);
CkPicture rectPicture(double l, double t, double w, double h) {
final ui.Rect rect = ui.Rect.fromLTWH(l, t, w, h);
return paintPicture(rect, (CkCanvas canvas) {
canvas.drawRect(
rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0));
});
}
await createPlatformView(0, 'test-view');
await createPlatformView(1, 'test-view');
expect(PlatformViewManager.instance.isVisible(0), isTrue);
expect(PlatformViewManager.instance.isVisible(1), isTrue);
final LayerSceneBuilder sb = LayerSceneBuilder();
// First picture-view-picture stack.
{
sb.pushOffset(0, 0);
sb.addPicture(
ui.Offset.zero,
rectPicture(0, 0, 10, 10),
);
sb.addPlatformView(
0,
width: 10,
height: 10,
);
sb.addPicture(
ui.Offset.zero,
rectPicture(2, 2, 5, 5),
);
sb.pop();
}
// Second picture-view-picture stack that does not overlap with the first one.
{
sb.pushOffset(20, 0);
sb.addPicture(
ui.Offset.zero,
rectPicture(0, 0, 10, 10),
);
sb.addPlatformView(
1,
width: 10,
height: 10,
);
sb.addPicture(
ui.Offset.zero,
rectPicture(2, 2, 5, 5),
);
sb.pop();
}
final LayerScene scene1 = sb.build();
await renderScene(scene1);
// Observe inefficient overlay allocation
_expectSceneMatches(<_EmbeddedViewMarker>[
_overlay,
_platformView,
_overlay,
_platformView,
_overlay,
]);
});