Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
2dacc82
WIP add multiview to renderer
harryterkelsen Nov 16, 2023
913ad52
Merge branch 'main' into canvaskit-renderer-multiview
harryterkelsen Nov 17, 2023
d0b625e
WIP
harryterkelsen Nov 20, 2023
241ed34
Merge branch 'main' into canvaskit-renderer-multiview
harryterkelsen Nov 20, 2023
9deb9c7
WIP multiview rendering
harryterkelsen Nov 22, 2023
29dd070
Merge branch 'main' into canvaskit-renderer-multiview
harryterkelsen Nov 22, 2023
edb64ec
Respond to comments
harryterkelsen Nov 22, 2023
bca01da
Fix tests
harryterkelsen Nov 22, 2023
373b740
Added basic unit test for multiview rendering
harryterkelsen Nov 22, 2023
fa27c2b
Merge branch 'main' into canvaskit-renderer-multiview
harryterkelsen Nov 22, 2023
28101a0
Remove unused import in test
harryterkelsen Nov 22, 2023
fe52616
Respond to comments
harryterkelsen Nov 23, 2023
225cd17
Add trailing commas so dartfmt doesn't explode
harryterkelsen Nov 23, 2023
0e5ec2c
Undo dartfmt
harryterkelsen Nov 23, 2023
71da65c
trailing whitespace
harryterkelsen Nov 23, 2023
ea3791a
trailing whitespace
harryterkelsen Nov 23, 2023
f7ddbac
Roll Skia from 3a79d7a618aa to 5606ef899116 (1 revision) (#48331)
skia-flutter-autoroll Nov 22, 2023
27279e7
[web] Hook the new JS API to the FlutterViewManager (#48283)
mdebbar Nov 22, 2023
bcfb4fa
Fix not being able to hide iOS status bar via setEnabledSystemUIMode …
LinXunFeng Nov 23, 2023
91b753a
Roll Fuchsia Linux SDK from IK4xyRtZkDwqT6pXA... to 4CZFCL5oL042nQihC…
skia-flutter-autoroll Nov 23, 2023
bdc563d
Roll Skia from 5606ef899116 to fc9c5337e568 (1 revision) (#48339)
skia-flutter-autoroll Nov 23, 2023
7bc18bc
Roll Skia from fc9c5337e568 to 094c3d963ba0 (6 revisions) (#48340)
skia-flutter-autoroll Nov 23, 2023
9e9b67c
Roll Skia from 094c3d963ba0 to 2c89bb28860b (1 revision) (#48341)
skia-flutter-autoroll Nov 23, 2023
95a5fb2
Roll Skia from 2c89bb28860b to b3d947126fd2 (1 revision) (#48349)
skia-flutter-autoroll Nov 24, 2023
bcc59ad
Roll Skia from b3d947126fd2 to de1673ed1618 (1 revision) (#48350)
skia-flutter-autoroll Nov 24, 2023
4d9bf77
Roll Skia from de1673ed1618 to 201d0c8d83f0 (1 revision) (#48352)
skia-flutter-autoroll Nov 24, 2023
a92c484
Roll Skia from 201d0c8d83f0 to b18b594b230d (1 revision) (#48356)
skia-flutter-autoroll Nov 24, 2023
e0bdff1
Roll Fuchsia Linux SDK from 4CZFCL5oL042nQihC... to P3HXI8K3eIeoBYhdX…
skia-flutter-autoroll Nov 24, 2023
b5da2ff
[Flutter GPU] Raster encoding. First triangle! (#48314)
bdero Nov 25, 2023
53cdf35
Roll Fuchsia Linux SDK from P3HXI8K3eIeoBYhdX... to zHKuNuXtMPLbovc0j…
skia-flutter-autoroll Nov 25, 2023
2f7f23e
Roll Skia from b18b594b230d to 0eea0b277d7d (7 revisions) (#48387)
skia-flutter-autoroll Nov 26, 2023
690ff8a
Roll Fuchsia Linux SDK from zHKuNuXtMPLbovc0j... to qyRH6zYfUEjo9L1x2…
skia-flutter-autoroll Nov 26, 2023
c38043a
[Impeller] OES extension does not apply to regular textures for decal…
Nov 27, 2023
84feadc
[Impeller] use spec constant for decal support in morph filter. (#48288)
Nov 27, 2023
6855b20
[Flutter GPU] Texture binding, index binding, attachments, depth stat…
bdero Nov 27, 2023
71d2ffb
Roll Skia from 0eea0b277d7d to b3e896c2f9bf (2 revisions) (#48391)
skia-flutter-autoroll Nov 27, 2023
ad428b2
Roll Skia from b3e896c2f9bf to 2bf9afca2bc1 (1 revision) (#48392)
skia-flutter-autoroll Nov 27, 2023
b0c6d87
Roll Skia from 2bf9afca2bc1 to 9fa62ccefe59 (1 revision) (#48393)
skia-flutter-autoroll Nov 27, 2023
ac1684c
Reduce number of surfaces required when presenting platform views (#4…
knopp Nov 27, 2023
4e455c2
Roll Skia from 9fa62ccefe59 to 5f0832787d0f (1 revision) (#48400)
skia-flutter-autoroll Nov 27, 2023
91c82d9
Roll Skia from 5f0832787d0f to b0d81aba3f78 (1 revision) (#48401)
skia-flutter-autoroll Nov 27, 2023
d73d7b2
Manual roll Dart SDK from f1fd14505782 to c9bdb5884670 (10 revisions)…
skia-flutter-autoroll Nov 27, 2023
cc43161
Roll Skia from b0d81aba3f78 to 743fc1cde518 (3 revisions) (#48406)
skia-flutter-autoroll Nov 27, 2023
eb13bc2
[Impeller] revert non-zero tessellation optimization. (#48234)
Nov 27, 2023
2593d1f
Roll Skia from 743fc1cde518 to 4c964f8c4738 (2 revisions) (#48410)
skia-flutter-autoroll Nov 27, 2023
a28b802
Respond to review comments
harryterkelsen Nov 27, 2023
0e59f27
Fix TODOs and changed EngineFlutterView constructor
harryterkelsen Nov 27, 2023
bcf62a7
Merge branch 'main' into canvaskit-renderer-multiview
harryterkelsen Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 20 additions & 22 deletions lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,17 @@ import 'embedded_views_diff.dart';
import 'path.dart';
import 'picture.dart';
import 'picture_recorder.dart';
import 'rasterizer.dart';
import 'render_canvas.dart';
import 'render_canvas_factory.dart';
import 'renderer.dart';

/// This composites HTML views into the [ui.Scene].
class HtmlViewEmbedder {
HtmlViewEmbedder._();
HtmlViewEmbedder(this.sceneHost, this.rasterizer, this.renderCanvasFactory);

/// The [HtmlViewEmbedder] singleton.
static HtmlViewEmbedder instance = HtmlViewEmbedder._();

DomElement get skiaSceneHost => CanvasKitRenderer.instance.sceneHost!;
final DomElement sceneHost;
final Rasterizer rasterizer;
final RenderCanvasFactory renderCanvasFactory;

/// The context for the current frame.
EmbedderFrameContext _context = EmbedderFrameContext();
Expand Down Expand Up @@ -219,7 +218,7 @@ class HtmlViewEmbedder {

// If the chain was previously attached, attach it to the same position.
if (headClipViewWasAttached) {
skiaSceneHost.insertBefore(head, headClipViewNextSibling);
sceneHost.insertBefore(head, headClipViewNextSibling);
}
return head;
}
Expand Down Expand Up @@ -359,7 +358,7 @@ class HtmlViewEmbedder {
}
_svgPathDefs = kSvgResourceHeader.cloneNode(false) as SVGElement;
_svgPathDefs!.append(createSVGDefsElement()..id = 'sk_path_defs');
skiaSceneHost.append(_svgPathDefs!);
sceneHost.append(_svgPathDefs!);
}

void submitFrame() {
Expand All @@ -386,8 +385,7 @@ class HtmlViewEmbedder {
_context.pictureRecorders[pictureRecorderIndex].endRecording());
pictureRecorderIndex++;
}
CanvasKitRenderer.instance.rasterizer
.rasterizeToCanvas(overlay, pictures);
rasterizer.rasterizeToCanvas(overlay, pictures);
}
for (final CkPictureRecorder recorder
in _context.pictureRecordersCreatedDuringPreroll) {
Expand Down Expand Up @@ -439,18 +437,18 @@ class HtmlViewEmbedder {

if (diffResult.addToBeginning) {
final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
skiaSceneHost.insertBefore(platformViewRoot, elementToInsertBefore);
sceneHost.insertBefore(platformViewRoot, elementToInsertBefore);
final RenderCanvas? overlay = _overlays[viewId];
if (overlay != null) {
skiaSceneHost.insertBefore(
sceneHost.insertBefore(
overlay.htmlElement, elementToInsertBefore);
}
} else {
final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
skiaSceneHost.append(platformViewRoot);
sceneHost.append(platformViewRoot);
final RenderCanvas? overlay = _overlays[viewId];
if (overlay != null) {
skiaSceneHost.append(overlay.htmlElement);
sceneHost.append(overlay.htmlElement);
}
}
}
Expand All @@ -463,17 +461,17 @@ class HtmlViewEmbedder {
if (!overlayElement.isConnected!) {
// This overlay wasn't added to the DOM.
if (i == _compositionOrder.length - 1) {
skiaSceneHost.append(overlayElement);
sceneHost.append(overlayElement);
} else {
final int nextView = _compositionOrder[i + 1];
final DomElement nextElement = _viewClipChains[nextView]!.root;
skiaSceneHost.insertBefore(overlayElement, nextElement);
sceneHost.insertBefore(overlayElement, nextElement);
}
}
}
}
} else {
RenderCanvasFactory.instance.removeSurfacesFromDom();
renderCanvasFactory.removeSurfacesFromDom();
for (int i = 0; i < _compositionOrder.length; i++) {
final int viewId = _compositionOrder[i];

Expand All @@ -492,9 +490,9 @@ class HtmlViewEmbedder {

final DomElement platformViewRoot = _viewClipChains[viewId]!.root;
final RenderCanvas? overlay = _overlays[viewId];
skiaSceneHost.append(platformViewRoot);
sceneHost.append(platformViewRoot);
if (overlay != null) {
skiaSceneHost.append(overlay.htmlElement);
sceneHost.append(overlay.htmlElement);
}
_activeCompositionOrder.add(viewId);
unusedViews.remove(viewId);
Expand Down Expand Up @@ -528,7 +526,7 @@ class HtmlViewEmbedder {
void _releaseOverlay(int viewId) {
if (_overlays[viewId] != null) {
final RenderCanvas overlay = _overlays[viewId]!;
RenderCanvasFactory.instance.releaseCanvas(overlay);
renderCanvasFactory.releaseCanvas(overlay);
_overlays.remove(viewId);
}
}
Expand Down Expand Up @@ -568,7 +566,7 @@ class HtmlViewEmbedder {
if (diffResult == null) {
// Everything is going to be explicitly recomposited anyway. Release all
// the surfaces and assign an overlay to all the surfaces needing one.
RenderCanvasFactory.instance.releaseCanvases();
renderCanvasFactory.releaseCanvases();
_overlays.clear();
viewsNeedingOverlays.forEach(_initializeOverlay);
} else {
Expand Down Expand Up @@ -638,7 +636,7 @@ class HtmlViewEmbedder {
assert(!_overlays.containsKey(viewId));

// Try reusing a cached overlay created for another platform view.
final RenderCanvas overlay = RenderCanvasFactory.instance.getCanvas();
final RenderCanvas overlay = renderCanvasFactory.getCanvas();
_overlays[viewId] = overlay;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/picture.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'canvas.dart';
import 'canvaskit_api.dart';
import 'image.dart';
import 'native_memory.dart';
import 'render_canvas_factory.dart';
import 'renderer.dart';
import 'surface.dart';

/// Implements [ui.Picture] on top of [SkPicture].
Expand Down Expand Up @@ -99,7 +99,7 @@ class CkPicture implements ScenePicture {
CkImage toImageSync(int width, int height) {
assert(debugCheckNotDisposed('Cannot convert picture to image.'));

final Surface surface = RenderCanvasFactory.instance.pictureToImageSurface;
final Surface surface = CanvasKitRenderer.instance.pictureToImageSurface;
final CkSurface ckSurface = surface
.createOrUpdateSurface(ui.Size(width.toDouble(), height.toDouble()));
final CkCanvas ckCanvas = ckSurface.getCanvas();
Expand Down
84 changes: 29 additions & 55 deletions lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,80 +2,54 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:meta/meta.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;

/// A class that can rasterize [LayerTree]s into a given [Surface].
/// A class that can rasterize [LayerTree]s into a given `sceneHost` element.
class Rasterizer {
Rasterizer(this.sceneHost);

final DomElement sceneHost;
final CompositorContext context = CompositorContext();
final List<ui.VoidCallback> _postFrameCallbacks = <ui.VoidCallback>[];
final RenderCanvasFactory renderCanvasFactory = RenderCanvasFactory();
late final HtmlViewEmbedder viewEmbedder =
HtmlViewEmbedder(sceneHost, this, renderCanvasFactory);

/// This is an SkSurface backed by an OffScreenCanvas. This single Surface is
/// used to render to many RenderCanvases to produce the rendered scene.
final Surface _offscreenSurface = Surface();
ui.Size _currentFrameSize = ui.Size.zero;

/// Render the given [pictures] so it is displayed by the given [canvas].
Future<void> rasterizeToCanvas(
RenderCanvas canvas, List<CkPicture> pictures) async {
await _offscreenSurface.rasterizeToCanvas(
_currentFrameSize, canvas, pictures);
await CanvasKitRenderer.instance.offscreenSurface.rasterizeToCanvas(
_currentFrameSize,
canvas,
pictures,
);
}

/// Sets the maximum size of the Skia resource cache, in bytes.
void setSkiaResourceCacheMaxBytes(int bytes) =>
_offscreenSurface.setSkiaResourceCacheMaxBytes(bytes);

/// Creates a new frame from this rasterizer's surface, draws the given
/// [LayerTree] into it, and then submits the frame.
void draw(LayerTree layerTree) {
try {
if (layerTree.frameSize.isEmpty) {
// Available drawing area is empty. Skip drawing.
return;
}

_currentFrameSize = layerTree.frameSize;
_offscreenSurface.acquireFrame(_currentFrameSize);
HtmlViewEmbedder.instance.frameSize = _currentFrameSize;
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
pictureRecorder.beginRecording(ui.Offset.zero & _currentFrameSize);
pictureRecorder.recordingCanvas!.clear(const ui.Color(0x00000000));
final Frame compositorFrame = context.acquireFrame(
pictureRecorder.recordingCanvas!, HtmlViewEmbedder.instance);

compositorFrame.raster(layerTree, ignoreRasterCache: true);

CanvasKitRenderer.instance.sceneHost!
.prepend(RenderCanvasFactory.instance.baseCanvas.htmlElement);
rasterizeToCanvas(RenderCanvasFactory.instance.baseCanvas,
<CkPicture>[pictureRecorder.endRecording()]);

HtmlViewEmbedder.instance.submitFrame();
} finally {
_runPostFrameCallbacks();
if (layerTree.frameSize.isEmpty) {
// Available drawing area is empty. Skip drawing.
return;
}
}

void addPostFrameCallback(ui.VoidCallback callback) {
_postFrameCallbacks.add(callback);
}
_currentFrameSize = layerTree.frameSize;
CanvasKitRenderer.instance.offscreenSurface.acquireFrame(_currentFrameSize);
viewEmbedder.frameSize = _currentFrameSize;
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
pictureRecorder.beginRecording(ui.Offset.zero & _currentFrameSize);
pictureRecorder.recordingCanvas!.clear(const ui.Color(0x00000000));
final Frame compositorFrame = context.acquireFrame(
pictureRecorder.recordingCanvas!, viewEmbedder);

void _runPostFrameCallbacks() {
for (int i = 0; i < _postFrameCallbacks.length; i++) {
final ui.VoidCallback callback = _postFrameCallbacks[i];
callback();
}
for (int i = 0; i < frameReferences.length; i++) {
frameReferences[i].value = null;
}
frameReferences.clear();
}
compositorFrame.raster(layerTree, ignoreRasterCache: true);

sceneHost.prepend(renderCanvasFactory.baseCanvas.htmlElement);
rasterizeToCanvas(renderCanvasFactory.baseCanvas,
<CkPicture>[pictureRecorder.endRecording()]);

/// Forces the post-frame callbacks to run. Useful in tests.
@visibleForTesting
void debugRunPostFrameCallbacks() {
_runPostFrameCallbacks();
viewEmbedder.submitFrame();
}
}
25 changes: 0 additions & 25 deletions lib/web_ui/lib/src/engine/canvaskit/render_canvas_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,11 @@ class RenderCanvasFactory {
}());
}

/// The lazy-initialized singleton surface factory.
///
/// [debugClear] causes this singleton to be reinitialized.
static RenderCanvasFactory get instance =>
_instance ??= RenderCanvasFactory();

/// Returns the raw (potentially uninitialized) value of the singleton.
///
/// Useful in tests for checking the lifecycle of this class.
static RenderCanvasFactory? get debugUninitializedInstance => _instance;

// Override the current instance with a new one.
//
// This should only be used in tests.
static void debugSetInstance(RenderCanvasFactory newInstance) {
_instance = newInstance;
}

static RenderCanvasFactory? _instance;

/// The base canvas to paint on. This is the default canvas which will be
/// painted to. If there are no platform views, then this canvas will render
/// the entire scene.
final RenderCanvas baseCanvas = RenderCanvas();

/// A surface used specifically for `Picture.toImage` when software rendering
/// is supported.
late final Surface pictureToImageSurface = Surface();

/// Canvases created by this factory which are currently in use.
final List<RenderCanvas> _liveCanvases = <RenderCanvas>[];

Expand Down Expand Up @@ -137,6 +113,5 @@ class RenderCanvasFactory {
baseCanvas.dispose();
_liveCanvases.clear();
_cache.clear();
_instance = null;
}
}
50 changes: 40 additions & 10 deletions lib/web_ui/lib/src/engine/canvaskit/renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,16 @@ class CanvasKitRenderer implements Renderer {
DomElement? _sceneHost;
DomElement? get sceneHost => _sceneHost;

late Rasterizer rasterizer = Rasterizer();
/// This is an SkSurface backed by an OffScreenCanvas. This single Surface is
/// used to render to many RenderCanvases to produce the rendered scene.
final Surface offscreenSurface = Surface();

set resourceCacheMaxBytes(int bytes) => rasterizer.setSkiaResourceCacheMaxBytes(bytes);
set resourceCacheMaxBytes(int bytes) =>
offscreenSurface.setSkiaResourceCacheMaxBytes(bytes);

/// A surface used specifically for `Picture.toImage` when software rendering
/// is supported.
final Surface pictureToImageSurface = Surface();

@override
Future<void> initialize() async {
Expand All @@ -64,13 +71,7 @@ class CanvasKitRenderer implements Renderer {

@override
void reset(FlutterViewEmbedder embedder) {
// CanvasKit uses a static scene element that never gets replaced, so it's
// added eagerly during initialization here and never touched, unless the
// system is reset due to hot restart or in a test.
_sceneHost = createDomElement('flt-scene');
// TODO(harryterkelsen): Do this operation on the appropriate Flutter View.
final EngineFlutterView implicitView = EnginePlatformDispatcher.instance.implicitView!;
implicitView.dom.setScene(_sceneHost!);
// No work required.
}

@override
Expand Down Expand Up @@ -373,7 +374,7 @@ class CanvasKitRenderer implements Renderer {
CkParagraphBuilder(style);

@override
void renderScene(ui.Scene scene) {
void renderScene(ui.Scene scene, ui.FlutterView view) {
// "Build finish" and "raster start" happen back-to-back because we
// render on the same thread, so there's no overhead from hopping to
// another thread.
Expand All @@ -384,10 +385,39 @@ class CanvasKitRenderer implements Renderer {
frameTimingsOnBuildFinish();
frameTimingsOnRasterStart();

// TODO(harryterkelsen): Use `FlutterViewManager.onViewsChanged` to manage
// the lifecycle of Rasterizers,
// https://github.com/flutter/flutter/issues/137073.
final Rasterizer rasterizer =
_getRasterizerForView(view as EngineFlutterView);

rasterizer.draw((scene as LayerScene).layerTree);
frameTimingsOnRasterFinish();
}

final Map<EngineFlutterView, Rasterizer> _rasterizers =
<EngineFlutterView, Rasterizer>{};

Rasterizer _getRasterizerForView(EngineFlutterView view) {
return _rasterizers.putIfAbsent(view, () {
return Rasterizer(view.dom.sceneHost);
});
}

/// Returns the [Rasterizer] that has been created for the given [view].
/// Used in tests.
Rasterizer debugGetRasterizerForView(EngineFlutterView view) {
return _getRasterizerForView(view);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this look in the map directly? Delegating to _getRasterizerForView will actually end up creating a new rasterizer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because the test which uses this needs to get a hold of the Rasterizer before the first time renderScene is called so it isn't created yet.

}

/// Resets the state of the renderer. Used in tests.
void debugClear() {
for (final Rasterizer rasterizer in _rasterizers.values) {
rasterizer.renderCanvasFactory.debugClear();
rasterizer.viewEmbedder.debugClear();
}
}

@override
void clearFragmentProgramCache() {
_programs.clear();
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/html/renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ class HtmlRenderer implements Renderer {
CanvasParagraphBuilder(style as EngineParagraphStyle);

@override
void renderScene(ui.Scene scene) {
void renderScene(ui.Scene scene, ui.FlutterView view) {
final EngineFlutterView implicitView = EnginePlatformDispatcher.instance.implicitView!;
implicitView.dom.setScene((scene as SurfaceScene).webOnlyRootElement!);
frameTimingsOnRasterFinish();
Expand Down
Loading