Skip to content

Commit a59c8d3

Browse files
Revert "Revert "Use a single OffscreenCanvas for rendering in CanvasKit" (flutter#45744)"
This reverts commit 153edff.
1 parent f29e1f6 commit a59c8d3

20 files changed

+813
-850
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2619,10 +2619,11 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.da
26192619
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/platform_message.dart + ../../../flutter/LICENSE
26202620
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart + ../../../flutter/LICENSE
26212621
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart + ../../../flutter/LICENSE
2622+
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart + ../../../flutter/LICENSE
2623+
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/render_canvas_factory.dart + ../../../flutter/LICENSE
26222624
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/renderer.dart + ../../../flutter/LICENSE
26232625
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/shader.dart + ../../../flutter/LICENSE
26242626
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface.dart + ../../../flutter/LICENSE
2625-
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart + ../../../flutter/LICENSE
26262627
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart + ../../../flutter/LICENSE
26272628
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart + ../../../flutter/LICENSE
26282629
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/util.dart + ../../../flutter/LICENSE
@@ -5396,10 +5397,11 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart
53965397
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/platform_message.dart
53975398
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart
53985399
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart
5400+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart
5401+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/render_canvas_factory.dart
53995402
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/renderer.dart
54005403
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/shader.dart
54015404
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface.dart
5402-
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/surface_factory.dart
54035405
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text.dart
54045406
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart
54055407
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/util.dart

lib/web_ui/lib/src/engine.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ export 'engine/canvaskit/picture.dart';
4343
export 'engine/canvaskit/picture_recorder.dart';
4444
export 'engine/canvaskit/raster_cache.dart';
4545
export 'engine/canvaskit/rasterizer.dart';
46+
export 'engine/canvaskit/render_canvas.dart';
47+
export 'engine/canvaskit/render_canvas_factory.dart';
4648
export 'engine/canvaskit/renderer.dart';
4749
export 'engine/canvaskit/shader.dart';
4850
export 'engine/canvaskit/surface.dart';
49-
export 'engine/canvaskit/surface_factory.dart';
5051
export 'engine/canvaskit/text.dart';
5152
export 'engine/canvaskit/text_fragmenter.dart';
5253
export 'engine/canvaskit/util.dart';

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@ extension CanvasKitExtension on CanvasKit {
161161
DomCanvasElement canvas, SkWebGLContextOptions options) =>
162162
_GetWebGLContext(canvas, options).toDartDouble;
163163

164+
@JS('GetWebGLContext')
165+
external JSNumber _GetOffscreenWebGLContext(
166+
DomOffscreenCanvas canvas, SkWebGLContextOptions options);
167+
double GetOffscreenWebGLContext(
168+
DomOffscreenCanvas canvas, SkWebGLContextOptions options) =>
169+
_GetOffscreenWebGLContext(canvas, options).toDartDouble;
170+
164171
@JS('MakeGrContext')
165172
external SkGrContext _MakeGrContext(JSNumber glContext);
166173
SkGrContext MakeGrContext(double glContext) =>
@@ -199,6 +206,9 @@ extension CanvasKitExtension on CanvasKit {
199206

200207
external SkSurface MakeSWCanvasSurface(DomCanvasElement canvas);
201208

209+
@JS('MakeSWCanvasSurface')
210+
external SkSurface MakeOffscreenSWCanvasSurface(DomOffscreenCanvas canvas);
211+
202212
/// Creates an image from decoded pixels represented as a list of bytes.
203213
///
204214
/// The pixel data must be encoded according to the image info in [info].

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

Lines changed: 56 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ import 'embedded_views_diff.dart';
1818
import 'path.dart';
1919
import 'picture.dart';
2020
import 'picture_recorder.dart';
21+
import 'render_canvas.dart';
22+
import 'render_canvas_factory.dart';
2123
import 'renderer.dart';
22-
import 'surface.dart';
23-
import 'surface_factory.dart';
2424

2525
/// This composites HTML views into the [ui.Scene].
2626
class HtmlViewEmbedder {
@@ -31,42 +31,6 @@ class HtmlViewEmbedder {
3131

3232
DomElement get skiaSceneHost => CanvasKitRenderer.instance.sceneHost!;
3333

34-
/// Force the view embedder to disable overlays.
35-
///
36-
/// This should never be used outside of tests.
37-
static set debugDisableOverlays(bool disable) {
38-
// Short circuit if the value is the same as what we already have.
39-
if (disable == _debugOverlaysDisabled) {
40-
return;
41-
}
42-
_debugOverlaysDisabled = disable;
43-
final SurfaceFactory? instance = SurfaceFactory.debugUninitializedInstance;
44-
if (instance != null) {
45-
instance.releaseSurfaces();
46-
instance.removeSurfacesFromDom();
47-
instance.debugClear();
48-
}
49-
if (disable) {
50-
// If we are disabling overlays then get the current [SurfaceFactory]
51-
// instance, clear it, and overwrite it with a new instance with only
52-
// one surface for the base surface.
53-
SurfaceFactory.debugSetInstance(SurfaceFactory(1));
54-
} else {
55-
// If we are re-enabling overlays then replace the current
56-
// [SurfaceFactory]instance with one with
57-
// [configuration.canvasKitMaximumSurfaces] overlays.
58-
SurfaceFactory.debugSetInstance(
59-
SurfaceFactory(configuration.canvasKitMaximumSurfaces));
60-
}
61-
}
62-
63-
static bool _debugOverlaysDisabled = false;
64-
65-
/// Whether or not we have issues a warning to the user about having too many
66-
/// surfaces on screen at once. This is so we only warn once, instead of every
67-
/// frame.
68-
bool _warnedAboutTooManySurfaces = false;
69-
7034
/// The context for the current frame.
7135
EmbedderFrameContext _context = EmbedderFrameContext();
7236

@@ -86,10 +50,12 @@ class HtmlViewEmbedder {
8650
/// * The number of clipping elements used last time the view was composited.
8751
final Map<int, ViewClipChain> _viewClipChains = <int, ViewClipChain>{};
8852

89-
/// Surfaces used to draw on top of platform views, keyed by platform view ID.
90-
///
91-
/// These surfaces are cached in the [OverlayCache] and reused.
92-
final Map<int, Surface> _overlays = <int, Surface>{};
53+
/// The maximum number of overlays to create. Too many overlays can cause a
54+
/// performance burden.
55+
static const int maximumOverlays = 7;
56+
57+
/// Canvases used to draw on top of platform views, keyed by platform view ID.
58+
final Map<int, RenderCanvas> _overlays = <int, RenderCanvas>{};
9359

9460
/// The views that need to be recomposited into the scene on the next frame.
9561
final Set<int> _viewsToRecomposite = <int>{};
@@ -100,6 +66,9 @@ class HtmlViewEmbedder {
10066
/// The most recent composition order.
10167
final List<int> _activeCompositionOrder = <int>[];
10268

69+
/// The most recent overlay groups.
70+
List<OverlayGroup> _activeOverlayGroups = <OverlayGroup>[];
71+
10372
/// The size of the frame, in physical pixels.
10473
ui.Size _frameSize = ui.window.physicalSize;
10574

@@ -124,20 +93,10 @@ class HtmlViewEmbedder {
12493
}
12594

12695
void prerollCompositeEmbeddedView(int viewId, EmbeddedViewParams params) {
127-
final bool hasAvailableOverlay =
128-
_context.pictureRecordersCreatedDuringPreroll.length <
129-
SurfaceFactory.instance.maximumOverlays;
130-
if (!hasAvailableOverlay && !_warnedAboutTooManySurfaces) {
131-
_warnedAboutTooManySurfaces = true;
132-
printWarning('Flutter was unable to create enough overlay surfaces. '
133-
'This is usually caused by too many platform views being '
134-
'displayed at once. '
135-
'You may experience incorrect rendering.');
136-
}
13796
// We need an overlay for each visible platform view. Invisible platform
13897
// views will be grouped with (at most) one visible platform view later.
13998
final bool needNewOverlay = PlatformViewManager.instance.isVisible(viewId);
140-
if (needNewOverlay && hasAvailableOverlay) {
99+
if (needNewOverlay) {
141100
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
142101
pictureRecorder.beginRecording(ui.Offset.zero & _frameSize);
143102
_context.pictureRecordersCreatedDuringPreroll.add(pictureRecorder);
@@ -409,26 +368,27 @@ class HtmlViewEmbedder {
409368
(_activeCompositionOrder.isEmpty || _compositionOrder.isEmpty)
410369
? null
411370
: diffViewList(_activeCompositionOrder, _compositionOrder);
412-
_updateOverlays(diffResult);
371+
final List<OverlayGroup>? overlayGroups = _updateOverlays(diffResult);
372+
if (overlayGroups != null) {
373+
_activeOverlayGroups = overlayGroups;
374+
}
413375
assert(
414-
_context.pictureRecorders.length == _overlays.length,
415-
'There should be the same number of picture recorders '
376+
_context.pictureRecorders.length >= _overlays.length,
377+
'There should be at least as many picture recorders '
416378
'(${_context.pictureRecorders.length}) as overlays (${_overlays.length}).',
417379
);
418-
int pictureRecorderIndex = 0;
419380

420-
for (int i = 0; i < _compositionOrder.length; i++) {
421-
final int viewId = _compositionOrder[i];
422-
if (_overlays[viewId] != null) {
423-
final SurfaceFrame frame = _overlays[viewId]!.acquireFrame(_frameSize);
424-
final CkCanvas canvas = frame.skiaCanvas;
425-
final CkPicture ckPicture =
426-
_context.pictureRecorders[pictureRecorderIndex].endRecording();
427-
canvas.clear(const ui.Color(0x00000000));
428-
canvas.drawPicture(ckPicture);
381+
int pictureRecorderIndex = 0;
382+
for (final OverlayGroup overlayGroup in _activeOverlayGroups) {
383+
final RenderCanvas overlay = _overlays[overlayGroup.last]!;
384+
final List<CkPicture> pictures = <CkPicture>[];
385+
for (int i = 0; i < overlayGroup.visibleCount; i++) {
386+
pictures.add(
387+
_context.pictureRecorders[pictureRecorderIndex].endRecording());
429388
pictureRecorderIndex++;
430-
frame.submit();
431389
}
390+
CanvasKitRenderer.instance.rasterizer
391+
.rasterizeToCanvas(overlay, pictures);
432392
}
433393
for (final CkPictureRecorder recorder
434394
in _context.pictureRecordersCreatedDuringPreroll) {
@@ -481,15 +441,15 @@ class HtmlViewEmbedder {
481441
if (diffResult.addToBeginning) {
482442
final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
483443
skiaSceneHost.insertBefore(platformViewRoot, elementToInsertBefore);
484-
final Surface? overlay = _overlays[viewId];
444+
final RenderCanvas? overlay = _overlays[viewId];
485445
if (overlay != null) {
486446
skiaSceneHost.insertBefore(
487447
overlay.htmlElement, elementToInsertBefore);
488448
}
489449
} else {
490450
final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
491451
skiaSceneHost.append(platformViewRoot);
492-
final Surface? overlay = _overlays[viewId];
452+
final RenderCanvas? overlay = _overlays[viewId];
493453
if (overlay != null) {
494454
skiaSceneHost.append(overlay.htmlElement);
495455
}
@@ -514,7 +474,7 @@ class HtmlViewEmbedder {
514474
}
515475
}
516476
} else {
517-
SurfaceFactory.instance.removeSurfacesFromDom();
477+
RenderCanvasFactory.instance.removeSurfacesFromDom();
518478
for (int i = 0; i < _compositionOrder.length; i++) {
519479
final int viewId = _compositionOrder[i];
520480

@@ -532,7 +492,7 @@ class HtmlViewEmbedder {
532492
}
533493

534494
final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
535-
final Surface? overlay = _overlays[viewId];
495+
final RenderCanvas? overlay = _overlays[viewId];
536496
skiaSceneHost.append(platformViewRoot);
537497
if (overlay != null) {
538498
skiaSceneHost.append(overlay.htmlElement);
@@ -568,8 +528,8 @@ class HtmlViewEmbedder {
568528

569529
void _releaseOverlay(int viewId) {
570530
if (_overlays[viewId] != null) {
571-
final Surface overlay = _overlays[viewId]!;
572-
SurfaceFactory.instance.releaseSurface(overlay);
531+
final RenderCanvas overlay = _overlays[viewId]!;
532+
RenderCanvasFactory.instance.releaseCanvas(overlay);
573533
_overlays.remove(viewId);
574534
}
575535
}
@@ -591,13 +551,13 @@ class HtmlViewEmbedder {
591551
// composition order of the current and previous frame, respectively.
592552
//
593553
// TODO(hterkelsen): Test this more thoroughly.
594-
void _updateOverlays(ViewListDiffResult? diffResult) {
554+
List<OverlayGroup>? _updateOverlays(ViewListDiffResult? diffResult) {
595555
if (diffResult != null &&
596556
diffResult.viewsToAdd.isEmpty &&
597557
diffResult.viewsToRemove.isEmpty) {
598558
// The composition order has not changed, continue using the assigned
599559
// overlays.
600-
return;
560+
return null;
601561
}
602562
// Group platform views from their composition order.
603563
// Each group contains one visible view, and any number of invisible views
@@ -606,17 +566,10 @@ class HtmlViewEmbedder {
606566
getOverlayGroups(_compositionOrder);
607567
final List<int> viewsNeedingOverlays =
608568
overlayGroups.map((OverlayGroup group) => group.last).toList();
609-
// If there were more visible views than overlays, then the last group
610-
// doesn't have an overlay.
611-
if (viewsNeedingOverlays.length > SurfaceFactory.instance.maximumOverlays) {
612-
assert(viewsNeedingOverlays.length ==
613-
SurfaceFactory.instance.maximumOverlays + 1);
614-
viewsNeedingOverlays.removeLast();
615-
}
616569
if (diffResult == null) {
617570
// Everything is going to be explicitly recomposited anyway. Release all
618571
// the surfaces and assign an overlay to all the surfaces needing one.
619-
SurfaceFactory.instance.releaseSurfaces();
572+
RenderCanvasFactory.instance.releaseCanvases();
620573
_overlays.clear();
621574
viewsNeedingOverlays.forEach(_initializeOverlay);
622575
} else {
@@ -635,6 +588,7 @@ class HtmlViewEmbedder {
635588
.forEach(_initializeOverlay);
636589
}
637590
assert(_overlays.length == viewsNeedingOverlays.length);
591+
return overlayGroups;
638592
}
639593

640594
// Group the platform views into "overlay groups". These are sublists
@@ -646,12 +600,8 @@ class HtmlViewEmbedder {
646600
// be assigned an overlay are grouped together and will be rendered on top of
647601
// the rest of the scene.
648602
List<OverlayGroup> getOverlayGroups(List<int> views) {
649-
final int maxOverlays = SurfaceFactory.instance.maximumOverlays;
650-
if (maxOverlays == 0) {
651-
return const <OverlayGroup>[];
652-
}
653603
final List<OverlayGroup> result = <OverlayGroup>[];
654-
OverlayGroup currentGroup = OverlayGroup(<int>[]);
604+
OverlayGroup currentGroup = OverlayGroup();
655605

656606
for (int i = 0; i < views.length; i++) {
657607
final int view = views[i];
@@ -660,8 +610,10 @@ class HtmlViewEmbedder {
660610
currentGroup.add(view);
661611
} else {
662612
// `view` is visible.
663-
if (!currentGroup.hasVisibleView) {
664-
// If `view` is the first visible one of the group, add it.
613+
if (!currentGroup.hasVisibleView ||
614+
result.length + 1 >= HtmlViewEmbedder.maximumOverlays) {
615+
// If `view` is the first visible one of the group or we've reached
616+
// the maximum number of overlays, add it.
665617
currentGroup.add(view, visible: true);
666618
} else {
667619
// There's already a visible `view` in `currentGroup`, so a new
@@ -671,17 +623,8 @@ class HtmlViewEmbedder {
671623
// We only care about groups that have one visible view.
672624
result.add(currentGroup);
673625
}
674-
// If there are overlays still available.
675-
if (result.length < maxOverlays) {
676-
// Create a new group, starting with `view`.
677-
currentGroup = OverlayGroup(<int>[view], visible: true);
678-
} else {
679-
// Add the rest of the views to a final group that will be rendered
680-
// on top of the scene.
681-
currentGroup = OverlayGroup(views.sublist(i), visible: true);
682-
// And break out of the loop!
683-
break;
684-
}
626+
currentGroup = OverlayGroup();
627+
currentGroup.add(view, visible: true);
685628
}
686629
}
687630
}
@@ -696,8 +639,7 @@ class HtmlViewEmbedder {
696639
assert(!_overlays.containsKey(viewId));
697640

698641
// Try reusing a cached overlay created for another platform view.
699-
final Surface overlay = SurfaceFactory.instance.getSurface()!;
700-
overlay.createOrUpdateSurface(_frameSize);
642+
final RenderCanvas overlay = RenderCanvasFactory.instance.getCanvas();
701643
_overlays[viewId] = overlay;
702644
}
703645

@@ -742,29 +684,30 @@ class HtmlViewEmbedder {
742684
/// Every overlay group is a list containing a visible view preceded or followed
743685
/// by zero or more invisible views.
744686
class OverlayGroup {
745-
/// Constructor
746-
OverlayGroup(
747-
List<int> viewGroup, {
748-
bool visible = false,
749-
}) : _group = viewGroup,
750-
_containsVisibleView = visible;
687+
OverlayGroup() : _group = <int>[];
751688

752689
// The internal list of ints.
753690
final List<int> _group;
754-
// A boolean flag to mark if any visible view has been added to the list.
755-
bool _containsVisibleView;
691+
692+
/// The number of visible views in this group.
693+
int _visibleCount = 0;
756694

757695
/// Add a [view] (maybe [visible]) to this group.
758696
void add(int view, {bool visible = false}) {
759697
_group.add(view);
760-
_containsVisibleView |= visible;
698+
if (visible) {
699+
_visibleCount++;
700+
}
761701
}
762702

763703
/// Get the "last" view added to this group.
764704
int get last => _group.last;
765705

766706
/// Returns true if this group contains any visible view.
767-
bool get hasVisibleView => _group.isNotEmpty && _containsVisibleView;
707+
bool get hasVisibleView => _visibleCount > 0;
708+
709+
/// Returns the number of visible views in this overlay group.
710+
int get visibleCount => _visibleCount;
768711
}
769712

770713
/// Represents a Clip Chain (for a view).

0 commit comments

Comments
 (0)