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 1 commit
Commits
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
4 changes: 4 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ class CkCanvas {
skCanvas.translate(dx, dy);
}

bool quickReject(ui.Rect rect) {
return skCanvas.quickReject(toSkRect(rect));
}

Float32List getLocalToDevice() {
final List<dynamic> list = skCanvas.getLocalToDevice();
final Float32List matrix4 = Float32List(16);
Expand Down
4 changes: 4 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2626,6 +2626,10 @@ extension SkCanvasExtension on SkCanvas {
List<dynamic> getLocalToDevice() => _getLocalToDevice().toObjectShallow as
List<dynamic>;

@JS('quickReject')
external JSBoolean _quickReject(JSFloat32Array rect);
bool quickReject(Float32List rect) => _quickReject(rect.toJS).toDart;

external JSVoid drawPicture(SkPicture picture);

@JS('drawParagraph')
Expand Down
60 changes: 8 additions & 52 deletions lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,6 @@ class HtmlViewEmbedder {
_frameSize = size;
}

/// Returns a list of recording canvases which the pictures in the upcoming
/// paint step will be drawn into. These recording canvases are combined into
/// an N-way canvas for the rasterizer to record clip and transform operations
/// during the measure step.
Iterable<CkCanvas> getPictureCanvases() {
return _context.measuringPictureRecorders.values
.map((CkPictureRecorder r) => r.recordingCanvas!);
}

/// Returns a list of canvases for the optimized rendering. These are used in
/// the paint step.
Iterable<CkCanvas> getOptimizedCanvases() {
Expand All @@ -109,24 +100,10 @@ class HtmlViewEmbedder {
_viewsToRecomposite.add(viewId);
}

/// Record that a picture recorder is needed for [picture] to be measured.
void prerollPicture(PictureLayer picture) {
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
pictureRecorder.beginRecording(ui.Offset.zero & _frameSize.toSize());
_context.measuringPictureRecorders[picture] = pictureRecorder;
}

/// Returns the canvas that was created to measure [picture].
CkCanvas getMeasuringCanvasFor(PictureLayer picture) {
return _context.measuringPictureRecorders[picture]!.recordingCanvas!;
}

/// Adds the picture recorder associated with [picture] to the unoptimized
/// scene.
void addPictureToUnoptimizedScene(PictureLayer picture) {
final CkPictureRecorder recorder =
_context.measuringPictureRecorders[picture]!;
_context.sceneElements.add(PictureSceneElement(picture, recorder));
_context.sceneElements.add(PictureSceneElement(picture));
}

/// Prepares to composite [viewId].
Expand Down Expand Up @@ -366,24 +343,8 @@ class HtmlViewEmbedder {
/// Optimizes the scene to use the fewest possible canvases. This sets up
/// the final paint pass to paint the pictures into the optimized canvases.
void optimizeRendering() {
final Map<CkPicture, PictureLayer> scenePictureToRawPicture =
<CkPicture, PictureLayer>{};
final Iterable<SceneElement> unoptimizedRendering =
_context.sceneElements.map<SceneElement>((SceneElement element) {
if (element is PictureSceneElement) {
final CkPicture scenePicture = element.pictureRecorder.endRecording();
if (scenePicture.cullRect.isEmpty) {
element.picture.isCulled = true;
}
element.scenePicture = scenePicture;
scenePictureToRawPicture[scenePicture] = element.picture;
return element;
} else {
return element;
}
});
Rendering rendering = createOptimizedRendering(
unoptimizedRendering, _currentCompositionParams);
_context.sceneElements, _currentCompositionParams);
rendering = _modifyRenderingForMaxCanvases(rendering);
_context.optimizedRendering = rendering;
// Create new picture recorders for the optimized render canvases and record
Expand All @@ -396,8 +357,8 @@ class HtmlViewEmbedder {
final CkPictureRecorder pictureRecorder = CkPictureRecorder();
pictureRecorder.beginRecording(ui.Offset.zero & _frameSize.toSize());
optimizedCanvasRecorders.add(pictureRecorder);
for (final CkPicture picture in renderCanvas.pictures) {
pictureToOptimizedCanvasMap[scenePictureToRawPicture[picture]!] =
for (final PictureLayer picture in renderCanvas.pictures) {
pictureToOptimizedCanvasMap[picture] =
pictureRecorder;
}
}
Expand Down Expand Up @@ -466,8 +427,8 @@ class HtmlViewEmbedder {
entity.debugComputedBounds!, platformViewBoundsPaint);
}
} else if (entity is RenderingRenderCanvas) {
for (final CkPicture picture in entity.pictures) {
boundsCanvas.drawRect(picture.cullRect, pictureBoundsPaint);
for (final PictureLayer picture in entity.pictures) {
boundsCanvas.drawRect(picture.sceneBounds!, pictureBoundsPaint);
}
}
}
Expand Down Expand Up @@ -538,7 +499,7 @@ class HtmlViewEmbedder {
return rendering;
}
int numCanvasesToDelete = numCanvases - maximumCanvases;
final List<CkPicture> picturesForLastCanvas = <CkPicture>[];
final List<PictureLayer> picturesForLastCanvas = <PictureLayer>[];
final List<RenderingEntity> modifiedEntities =
List<RenderingEntity>.from(rendering.entities);
bool sawLastCanvas = false;
Expand Down Expand Up @@ -961,14 +922,9 @@ class MutatorsStack extends Iterable<Mutator> {
sealed class SceneElement {}

class PictureSceneElement extends SceneElement {
PictureSceneElement(this.picture, this.pictureRecorder);
PictureSceneElement(this.picture);

final PictureLayer picture;
final CkPictureRecorder pictureRecorder;

/// The picture as it would be painted in the final scene, with clips and
/// transforms applied. This is set by [optimizeRendering].
CkPicture? scenePicture;
}

class PlatformViewSceneElement extends SceneElement {
Expand Down
5 changes: 0 additions & 5 deletions lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,7 @@ class LayerTree {
/// tree. This paint pass is just used to measure the bounds for each picture
/// so we can optimize the total number of canvases required.
void measure(Frame frame, BitmapSize size, {bool ignoreRasterCache = false}) {
final CkNWayCanvas nWayCanvas = CkNWayCanvas();
final Iterable<CkCanvas> recordingCanvases =
frame.viewEmbedder!.getPictureCanvases();
recordingCanvases.forEach(nWayCanvas.addCanvas);
final MeasureVisitor measureVisitor = MeasureVisitor(
nWayCanvas,
size,
frame.viewEmbedder!,
);
Expand Down
69 changes: 2 additions & 67 deletions lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,6 @@ import 'dart:typed_data';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;

import '../color_filter.dart';
import '../vector_math.dart';
import 'canvas.dart';
import 'canvaskit_api.dart';
import 'color_filter.dart';
import 'embedded_views.dart';
import 'image_filter.dart';
import 'layer.dart';
import 'n_way_canvas.dart';
import 'painting.dart';

abstract class LayerVisitor {
void visitRoot(RootLayer root);
void visitBackdropFilter(BackdropFilterEngineLayer backdropFilter);
Expand Down Expand Up @@ -187,7 +176,6 @@ class PrerollVisitor extends LayerVisitor {
// The picture may have been culled on a previous frame, but has since
// scrolled back into the clip region. Reset the `isCulled` flag.
picture.isCulled = false;
viewEmbedder?.prerollPicture(picture);
}

@override
Expand Down Expand Up @@ -229,7 +217,6 @@ class PrerollVisitor extends LayerVisitor {
/// prepares for them to be optimized into few canvases.
class MeasureVisitor extends LayerVisitor {
MeasureVisitor(
this.nWayCanvas,
BitmapSize size,
this.viewEmbedder,
) {
Expand All @@ -238,11 +225,6 @@ class MeasureVisitor extends LayerVisitor {
measuringRecorder.beginRecording(ui.Offset.zero & size.toSize());
}

/// A multi-canvas that applies clips, transforms, and opacity
/// operations to all canvases (root canvas and overlay canvases for the
/// platform views).
CkNWayCanvas nWayCanvas;

/// A stack of image filters which apply their transforms to measured bounds.
List<CkManagedSkImageFilterConvertible> imageFilterStack =
<CkManagedSkImageFilterConvertible>[];
Expand Down Expand Up @@ -285,75 +267,54 @@ class MeasureVisitor extends LayerVisitor {
void visitClipPath(ClipPathEngineLayer clipPath) {
assert(clipPath.needsPainting);

nWayCanvas.save();
measuringCanvas.save();
nWayCanvas.clipPath(
clipPath.clipPath, clipPath.clipBehavior != ui.Clip.hardEdge);
measuringCanvas.clipPath(
clipPath.clipPath, clipPath.clipBehavior != ui.Clip.hardEdge);

if (clipPath.clipBehavior == ui.Clip.antiAliasWithSaveLayer) {
nWayCanvas.saveLayer(clipPath.paintBounds, null);
measuringCanvas.saveLayer(clipPath.paintBounds, null);
}
measureChildren(clipPath);
if (clipPath.clipBehavior == ui.Clip.antiAliasWithSaveLayer) {
nWayCanvas.restore();
measuringCanvas.restore();
}
nWayCanvas.restore();
measuringCanvas.restore();
}

@override
void visitClipRect(ClipRectEngineLayer clipRect) {
assert(clipRect.needsPainting);

nWayCanvas.save();
measuringCanvas.save();
nWayCanvas.clipRect(
clipRect.clipRect,
ui.ClipOp.intersect,
clipRect.clipBehavior != ui.Clip.hardEdge,
);
measuringCanvas.clipRect(
clipRect.clipRect,
ui.ClipOp.intersect,
clipRect.clipBehavior != ui.Clip.hardEdge,
);
if (clipRect.clipBehavior == ui.Clip.antiAliasWithSaveLayer) {
nWayCanvas.saveLayer(clipRect.clipRect, null);
measuringCanvas.saveLayer(clipRect.clipRect, null);
}
measureChildren(clipRect);
if (clipRect.clipBehavior == ui.Clip.antiAliasWithSaveLayer) {
nWayCanvas.restore();
measuringCanvas.restore();
}
nWayCanvas.restore();
measuringCanvas.restore();
}

@override
void visitClipRRect(ClipRRectEngineLayer clipRRect) {
assert(clipRRect.needsPainting);

nWayCanvas.save();
measuringCanvas.save();
nWayCanvas.clipRRect(
clipRRect.clipRRect, clipRRect.clipBehavior != ui.Clip.hardEdge);
measuringCanvas.clipRRect(
clipRRect.clipRRect, clipRRect.clipBehavior != ui.Clip.hardEdge);
if (clipRRect.clipBehavior == ui.Clip.antiAliasWithSaveLayer) {
nWayCanvas.saveLayer(clipRRect.paintBounds, null);
measuringCanvas.saveLayer(clipRRect.paintBounds, null);
}
measureChildren(clipRRect);
if (clipRRect.clipBehavior == ui.Clip.antiAliasWithSaveLayer) {
nWayCanvas.restore();
measuringCanvas.restore();
}
nWayCanvas.restore();
measuringCanvas.restore();
}

Expand All @@ -364,19 +325,14 @@ class MeasureVisitor extends LayerVisitor {
final CkPaint paint = CkPaint();
paint.color = ui.Color.fromARGB(opacity.alpha, 0, 0, 0);

nWayCanvas.save();
measuringCanvas.save();
nWayCanvas.translate(opacity.offset.dx, opacity.offset.dy);
measuringCanvas.translate(opacity.offset.dx, opacity.offset.dy);

final ui.Rect saveLayerBounds = opacity.paintBounds.shift(-opacity.offset);

nWayCanvas.saveLayer(saveLayerBounds, paint);
measuringCanvas.saveLayer(saveLayerBounds, paint);
measureChildren(opacity);
// Restore twice: once for the translate and once for the saveLayer.
nWayCanvas.restore();
nWayCanvas.restore();
measuringCanvas.restore();
measuringCanvas.restore();
}
Expand All @@ -385,12 +341,9 @@ class MeasureVisitor extends LayerVisitor {
void visitTransform(TransformEngineLayer transform) {
assert(transform.needsPainting);

nWayCanvas.save();
measuringCanvas.save();
nWayCanvas.transform(transform.transform.storage);
measuringCanvas.transform(transform.transform.storage);
measureChildren(transform);
nWayCanvas.restore();
measuringCanvas.restore();
}

Expand All @@ -404,15 +357,11 @@ class MeasureVisitor extends LayerVisitor {
assert(imageFilter.needsPainting);
final ui.Rect offsetPaintBounds =
imageFilter.paintBounds.shift(-imageFilter.offset);
nWayCanvas.save();
measuringCanvas.save();
nWayCanvas.translate(imageFilter.offset.dx, imageFilter.offset.dy);
measuringCanvas.translate(imageFilter.offset.dx, imageFilter.offset.dy);
nWayCanvas.clipRect(offsetPaintBounds, ui.ClipOp.intersect, false);
measuringCanvas.clipRect(offsetPaintBounds, ui.ClipOp.intersect, false);
final CkPaint paint = CkPaint();
paint.imageFilter = imageFilter.filter;
nWayCanvas.saveLayer(offsetPaintBounds, paint);
measuringCanvas.saveLayer(offsetPaintBounds, paint);
if (imageFilter.filter is! ui.ColorFilter) {
imageFilterStack
Expand All @@ -422,8 +371,6 @@ class MeasureVisitor extends LayerVisitor {
if (imageFilter.filter is! ui.ColorFilter) {
imageFilterStack.removeLast();
}
nWayCanvas.restore();
nWayCanvas.restore();
measuringCanvas.restore();
measuringCanvas.restore();
}
Expand All @@ -432,24 +379,17 @@ class MeasureVisitor extends LayerVisitor {
void visitShaderMask(ShaderMaskEngineLayer shaderMask) {
assert(shaderMask.needsPainting);

nWayCanvas.saveLayer(shaderMask.paintBounds, null);
measuringCanvas.saveLayer(shaderMask.paintBounds, null);
measureChildren(shaderMask);

nWayCanvas.restore();
measuringCanvas.restore();
}

@override
void visitPicture(PictureLayer picture) {
assert(picture.needsPainting);

final CkCanvas pictureRecorderCanvas =
viewEmbedder.getMeasuringCanvasFor(picture);

pictureRecorderCanvas.save();
measuringCanvas.save();
pictureRecorderCanvas.translate(picture.offset.dx, picture.offset.dy);
measuringCanvas.translate(picture.offset.dx, picture.offset.dy);

// Get the picture bounds using the measuring canvas.
Expand All @@ -467,8 +407,8 @@ class MeasureVisitor extends LayerVisitor {
}
picture.sceneBounds = transformedBounds;

pictureRecorderCanvas.drawPicture(picture.picture);
pictureRecorderCanvas.restore();
picture.isCulled = measuringCanvas.quickReject(picture.picture.cullRect);

measuringCanvas.restore();

viewEmbedder.addPictureToUnoptimizedScene(picture);
Expand All @@ -485,19 +425,14 @@ class MeasureVisitor extends LayerVisitor {
// then it will fill the entire `cullRect` of the picture, ignoring the
// `paintBounds` passed to `saveLayer`. See:
// https://github.com/flutter/flutter/issues/88866
nWayCanvas.save();
measuringCanvas.save();

// TODO(hterkelsen): Only clip if the ColorFilter affects transparent black.
nWayCanvas.clipRect(colorFilter.paintBounds, ui.ClipOp.intersect, false);
measuringCanvas.clipRect(
colorFilter.paintBounds, ui.ClipOp.intersect, false);

nWayCanvas.saveLayer(colorFilter.paintBounds, paint);
measuringCanvas.saveLayer(colorFilter.paintBounds, paint);
measureChildren(colorFilter);
nWayCanvas.restore();
nWayCanvas.restore();
measuringCanvas.restore();
measuringCanvas.restore();
}
Expand Down
Loading