Unverified Commit 883e2dc2 authored by liyuqian's avatar liyuqian Committed by GitHub

Maintain dirtiness and use retained engine layers (#23434)

For #21756
parent 628e8ec0
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:collection'; import 'dart:collection';
import 'dart:ui' as ui show Image, ImageFilter, Picture, Scene, SceneBuilder; import 'dart:ui' as ui show EngineLayer, Image, ImageFilter, Picture, Scene, SceneBuilder;
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
...@@ -41,6 +41,59 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin { ...@@ -41,6 +41,59 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
@override @override
ContainerLayer get parent => super.parent; ContainerLayer get parent => super.parent;
// Whether this layer has any changes since its last call to [addToScene].
//
// Initialized to true as a new layer has never called [addToScene].
bool _needsAddToScene = true;
/// Mark that this layer has changed and [addToScene] needs to be called.
@protected
void markNeedsAddToScene() {
_needsAddToScene = true;
}
/// Mark that this layer is in sync with engine.
///
/// This is only for debug and test purpose only.
@visibleForTesting
void debugMarkClean() {
assert((){
_needsAddToScene = false;
return true;
}());
}
/// Subclasses may override this to true to disable retained rendering.
@protected
bool get alwaysNeedsAddToScene => false;
bool _subtreeNeedsAddToScene;
/// Whether any layer in the subtree needs [addToScene].
///
/// This is for debug and test purpose only. It only becomes valid after
/// calling [updateSubtreeNeedsAddToScene].
@visibleForTesting
bool get debugSubtreeNeedsAddToScene {
bool result;
assert((){
result = _subtreeNeedsAddToScene;
return true;
}());
return result;
}
ui.EngineLayer _engineLayer;
/// Traverse the layer tree and compute if any subtree needs [addToScene].
///
/// A subtree needs [addToScene] if any of its layer needs [addToScene].
/// The [ContainerLayer] will override this to respect its children.
@protected
void updateSubtreeNeedsAddToScene() {
_subtreeNeedsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;
}
/// This layer's next sibling in the parent layer's child list. /// This layer's next sibling in the parent layer's child list.
Layer get nextSibling => _nextSibling; Layer get nextSibling => _nextSibling;
Layer _nextSibling; Layer _nextSibling;
...@@ -49,6 +102,18 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin { ...@@ -49,6 +102,18 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
Layer get previousSibling => _previousSibling; Layer get previousSibling => _previousSibling;
Layer _previousSibling; Layer _previousSibling;
@override
void dropChild(AbstractNode child) {
markNeedsAddToScene();
super.dropChild(child);
}
@override
void adoptChild(AbstractNode child) {
markNeedsAddToScene();
super.adoptChild(child);
}
/// Removes this layer from its parent layer's child list. /// Removes this layer from its parent layer's child list.
/// ///
/// This has no effect if the layer's parent is already null. /// This has no effect if the layer's parent is already null.
...@@ -104,7 +169,29 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin { ...@@ -104,7 +169,29 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
S find<S>(Offset regionOffset); S find<S>(Offset regionOffset);
/// Override this method to upload this layer to the engine. /// Override this method to upload this layer to the engine.
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]); ///
/// Return the engine layer for retained rendering. When there's no
/// corresponding engine layer, null is returned.
@protected
ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]);
void _addToSceneWithRetainedRendering(ui.SceneBuilder builder) {
// There can't be a loop by adding a retained layer subtree whose
// _subtreeNeedsAddToScene is false.
//
// Proof by contradiction:
//
// If we introduce a loop, this retained layer must be appended to one of
// its descendent layers, say A. That means the child structure of A has
// changed so A's _needsAddToScene is true. This contradicts
// _subtreeNeedsAddToScene being false.
if (!_subtreeNeedsAddToScene && _engineLayer != null) {
builder.addRetained(_engineLayer);
return;
}
_engineLayer = addToScene(builder);
_needsAddToScene = false;
}
/// The object responsible for creating this layer. /// The object responsible for creating this layer.
/// ///
...@@ -144,7 +231,12 @@ class PictureLayer extends Layer { ...@@ -144,7 +231,12 @@ class PictureLayer extends Layer {
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
ui.Picture picture; ui.Picture get picture => _picture;
ui.Picture _picture;
set picture(ui.Picture picture) {
_needsAddToScene = true;
_picture = picture;
}
/// Hints that the painting in this layer is complex and would benefit from /// Hints that the painting in this layer is complex and would benefit from
/// caching. /// caching.
...@@ -154,7 +246,14 @@ class PictureLayer extends Layer { ...@@ -154,7 +246,14 @@ class PictureLayer extends Layer {
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
bool isComplexHint = false; bool get isComplexHint => _isComplexHint;
bool _isComplexHint = false;
set isComplexHint(bool value) {
if (value != _isComplexHint) {
_isComplexHint = value;
markNeedsAddToScene();
}
}
/// Hints that the painting in this layer is likely to change next frame. /// Hints that the painting in this layer is likely to change next frame.
/// ///
...@@ -165,11 +264,19 @@ class PictureLayer extends Layer { ...@@ -165,11 +264,19 @@ class PictureLayer extends Layer {
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
bool willChangeHint = false; bool get willChangeHint => _willChangeHint;
bool _willChangeHint = false;
set willChangeHint(bool value) {
if (value != _willChangeHint) {
_willChangeHint = value;
markNeedsAddToScene();
}
}
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint); builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint);
return null; // this does not return an engine layer yet.
} }
@override @override
...@@ -234,7 +341,7 @@ class TextureLayer extends Layer { ...@@ -234,7 +341,7 @@ class TextureLayer extends Layer {
final bool freeze; final bool freeze;
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
final Rect shiftedRect = rect.shift(layerOffset); final Rect shiftedRect = rect.shift(layerOffset);
builder.addTexture( builder.addTexture(
textureId, textureId,
...@@ -243,6 +350,7 @@ class TextureLayer extends Layer { ...@@ -243,6 +350,7 @@ class TextureLayer extends Layer {
height: shiftedRect.height, height: shiftedRect.height,
freeze: freeze, freeze: freeze,
); );
return null; // this does not return an engine layer yet.
} }
@override @override
...@@ -256,18 +364,25 @@ class TextureLayer extends Layer { ...@@ -256,18 +364,25 @@ class TextureLayer extends Layer {
class PerformanceOverlayLayer extends Layer { class PerformanceOverlayLayer extends Layer {
/// Creates a layer that displays a performance overlay. /// Creates a layer that displays a performance overlay.
PerformanceOverlayLayer({ PerformanceOverlayLayer({
@required this.overlayRect, @required Rect overlayRect,
@required this.optionsMask, @required this.optionsMask,
@required this.rasterizerThreshold, @required this.rasterizerThreshold,
@required this.checkerboardRasterCacheImages, @required this.checkerboardRasterCacheImages,
@required this.checkerboardOffscreenLayers, @required this.checkerboardOffscreenLayers,
}); }) : _overlayRect = overlayRect;
/// The rectangle in this layer's coordinate system that the overlay should occupy. /// The rectangle in this layer's coordinate system that the overlay should occupy.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
Rect overlayRect; Rect get overlayRect => _overlayRect;
Rect _overlayRect;
set overlayRect(Rect value) {
if (value != _overlayRect) {
_overlayRect = value;
markNeedsAddToScene();
}
}
/// The mask is created by shifting 1 by the index of the specific /// The mask is created by shifting 1 by the index of the specific
/// [PerformanceOverlayOption] to enable. /// [PerformanceOverlayOption] to enable.
...@@ -302,12 +417,13 @@ class PerformanceOverlayLayer extends Layer { ...@@ -302,12 +417,13 @@ class PerformanceOverlayLayer extends Layer {
final bool checkerboardOffscreenLayers; final bool checkerboardOffscreenLayers;
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
assert(optionsMask != null); assert(optionsMask != null);
builder.addPerformanceOverlay(optionsMask, overlayRect.shift(layerOffset)); builder.addPerformanceOverlay(optionsMask, overlayRect.shift(layerOffset));
builder.setRasterizerTracingThreshold(rasterizerThreshold); builder.setRasterizerTracingThreshold(rasterizerThreshold);
builder.setCheckerboardRasterCacheImages(checkerboardRasterCacheImages); builder.setCheckerboardRasterCacheImages(checkerboardRasterCacheImages);
builder.setCheckerboardOffscreenLayers(checkerboardOffscreenLayers); builder.setCheckerboardOffscreenLayers(checkerboardOffscreenLayers);
return null; // this does not return an engine layer yet.
} }
@override @override
...@@ -348,6 +464,17 @@ class ContainerLayer extends Layer { ...@@ -348,6 +464,17 @@ class ContainerLayer extends Layer {
return child == equals; return child == equals;
} }
@override
void updateSubtreeNeedsAddToScene() {
super.updateSubtreeNeedsAddToScene();
Layer child = firstChild;
while (child != null) {
child.updateSubtreeNeedsAddToScene();
_subtreeNeedsAddToScene = _subtreeNeedsAddToScene || child._subtreeNeedsAddToScene;
child = child.nextSibling;
}
}
@override @override
S find<S>(Offset regionOffset) { S find<S>(Offset regionOffset) {
Layer current = lastChild; Layer current = lastChild;
...@@ -451,8 +578,9 @@ class ContainerLayer extends Layer { ...@@ -451,8 +578,9 @@ class ContainerLayer extends Layer {
} }
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder, layerOffset);
return null; // ContainerLayer does not have a corresponding engine layer
} }
/// Uploads all of this layer's children to the engine. /// Uploads all of this layer's children to the engine.
...@@ -465,7 +593,11 @@ class ContainerLayer extends Layer { ...@@ -465,7 +593,11 @@ class ContainerLayer extends Layer {
void addChildrenToScene(ui.SceneBuilder builder, [Offset childOffset = Offset.zero]) { void addChildrenToScene(ui.SceneBuilder builder, [Offset childOffset = Offset.zero]) {
Layer child = firstChild; Layer child = firstChild;
while (child != null) { while (child != null) {
child.addToScene(builder, childOffset); if (childOffset == Offset.zero) {
child._addToSceneWithRetainedRendering(builder);
} else {
child.addToScene(builder, childOffset);
}
child = child.nextSibling; child = child.nextSibling;
} }
} }
...@@ -540,7 +672,7 @@ class OffsetLayer extends ContainerLayer { ...@@ -540,7 +672,7 @@ class OffsetLayer extends ContainerLayer {
/// ///
/// By default, [offset] is zero. It must be non-null before the compositing /// By default, [offset] is zero. It must be non-null before the compositing
/// phase of the pipeline. /// phase of the pipeline.
OffsetLayer({ this.offset = Offset.zero }); OffsetLayer({ Offset offset = Offset.zero }) : _offset = offset;
/// Offset from parent in the parent's coordinate system. /// Offset from parent in the parent's coordinate system.
/// ///
...@@ -549,7 +681,14 @@ class OffsetLayer extends ContainerLayer { ...@@ -549,7 +681,14 @@ class OffsetLayer extends ContainerLayer {
/// ///
/// The [offset] property must be non-null before the compositing phase of the /// The [offset] property must be non-null before the compositing phase of the
/// pipeline. /// pipeline.
Offset offset; Offset get offset => _offset;
Offset _offset;
set offset(Offset value) {
if (value != _offset) {
markNeedsAddToScene();
}
_offset = value;
}
@override @override
S find<S>(Offset regionOffset) { S find<S>(Offset regionOffset) {
...@@ -563,16 +702,25 @@ class OffsetLayer extends ContainerLayer { ...@@ -563,16 +702,25 @@ class OffsetLayer extends ContainerLayer {
transform.multiply(Matrix4.translationValues(offset.dx, offset.dy, 0.0)); transform.multiply(Matrix4.translationValues(offset.dx, offset.dy, 0.0));
} }
/// Consider this layer as the root and build a scene (a tree of layers)
/// in the engine.
ui.Scene buildScene(ui.SceneBuilder builder) {
updateSubtreeNeedsAddToScene();
addToScene(builder);
return builder.build();
}
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
// Skia has a fast path for concatenating scale/translation only matrices. // Skia has a fast path for concatenating scale/translation only matrices.
// Hence pushing a translation-only transform layer should be fast. For // Hence pushing a translation-only transform layer should be fast. For
// retained rendering, we don't want to push the offset down to each leaf // retained rendering, we don't want to push the offset down to each leaf
// node. Otherwise, changing an offset layer on the very high level could // node. Otherwise, changing an offset layer on the very high level could
// cascade the change to too many leaves. // cascade the change to too many leaves.
builder.pushOffset(layerOffset.dx + offset.dx, layerOffset.dy + offset.dy); final ui.EngineLayer engineLayer = builder.pushOffset(layerOffset.dx + offset.dx, layerOffset.dy + offset.dy);
addChildrenToScene(builder); addChildrenToScene(builder);
builder.pop(); builder.pop();
return engineLayer;
} }
@override @override
...@@ -608,8 +756,7 @@ class OffsetLayer extends ContainerLayer { ...@@ -608,8 +756,7 @@ class OffsetLayer extends ContainerLayer {
); );
transform.scale(pixelRatio, pixelRatio); transform.scale(pixelRatio, pixelRatio);
builder.pushTransform(transform.storage); builder.pushTransform(transform.storage);
addToScene(builder); final ui.Scene scene = buildScene(builder);
final ui.Scene scene = builder.build();
try { try {
// Size is rounded up to the next pixel to make sure we don't clip off // Size is rounded up to the next pixel to make sure we don't clip off
// anything. // anything.
...@@ -633,14 +780,22 @@ class ClipRectLayer extends ContainerLayer { ...@@ -633,14 +780,22 @@ class ClipRectLayer extends ContainerLayer {
/// ///
/// The [clipRect] property must be non-null before the compositing phase of /// The [clipRect] property must be non-null before the compositing phase of
/// the pipeline. /// the pipeline.
ClipRectLayer({ this.clipRect, Clip clipBehavior = Clip.hardEdge }) : ClipRectLayer({ @required Rect clipRect, Clip clipBehavior = Clip.hardEdge }) :
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none); _clipRect = clipRect, _clipBehavior = clipBehavior,
assert(clipBehavior != null), assert(clipBehavior != Clip.none);
/// The rectangle to clip in the parent's coordinate system. /// The rectangle to clip in the parent's coordinate system.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
Rect clipRect; Rect get clipRect => _clipRect;
Rect _clipRect;
set clipRect(Rect value) {
if (value != _clipRect) {
_clipRect = value;
markNeedsAddToScene();
}
}
/// {@template flutter.clipper.clipBehavior} /// {@template flutter.clipper.clipBehavior}
/// Controls how to clip (default to [Clip.antiAlias]). /// Controls how to clip (default to [Clip.antiAlias]).
...@@ -652,7 +807,10 @@ class ClipRectLayer extends ContainerLayer { ...@@ -652,7 +807,10 @@ class ClipRectLayer extends ContainerLayer {
set clipBehavior(Clip value) { set clipBehavior(Clip value) {
assert(value != null); assert(value != null);
assert(value != Clip.none); assert(value != Clip.none);
_clipBehavior = value; if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsAddToScene();
}
} }
@override @override
...@@ -663,7 +821,7 @@ class ClipRectLayer extends ContainerLayer { ...@@ -663,7 +821,7 @@ class ClipRectLayer extends ContainerLayer {
} }
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
bool enabled = true; bool enabled = true;
assert(() { assert(() {
enabled = !debugDisableClipLayers; enabled = !debugDisableClipLayers;
...@@ -674,6 +832,7 @@ class ClipRectLayer extends ContainerLayer { ...@@ -674,6 +832,7 @@ class ClipRectLayer extends ContainerLayer {
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder, layerOffset);
if (enabled) if (enabled)
builder.pop(); builder.pop();
return null; // this does not return an engine layer yet.
} }
@override @override
...@@ -693,14 +852,22 @@ class ClipRRectLayer extends ContainerLayer { ...@@ -693,14 +852,22 @@ class ClipRRectLayer extends ContainerLayer {
/// ///
/// The [clipRRect] property must be non-null before the compositing phase of /// The [clipRRect] property must be non-null before the compositing phase of
/// the pipeline. /// the pipeline.
ClipRRectLayer({ this.clipRRect, Clip clipBehavior = Clip.antiAlias }) : ClipRRectLayer({ @required RRect clipRRect, Clip clipBehavior = Clip.antiAlias }) :
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none); _clipRRect = clipRRect, _clipBehavior = clipBehavior,
assert(clipBehavior != null), assert(clipBehavior != Clip.none);
/// The rounded-rect to clip in the parent's coordinate system. /// The rounded-rect to clip in the parent's coordinate system.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
RRect clipRRect; RRect get clipRRect => _clipRRect;
RRect _clipRRect;
set clipRRect(RRect value) {
if (value != _clipRRect) {
_clipRRect = value;
markNeedsAddToScene();
}
}
/// {@macro flutter.clipper.clipBehavior} /// {@macro flutter.clipper.clipBehavior}
Clip get clipBehavior => _clipBehavior; Clip get clipBehavior => _clipBehavior;
...@@ -708,7 +875,10 @@ class ClipRRectLayer extends ContainerLayer { ...@@ -708,7 +875,10 @@ class ClipRRectLayer extends ContainerLayer {
set clipBehavior(Clip value) { set clipBehavior(Clip value) {
assert(value != null); assert(value != null);
assert(value != Clip.none); assert(value != Clip.none);
_clipBehavior = value; if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsAddToScene();
}
} }
@override @override
...@@ -719,7 +889,7 @@ class ClipRRectLayer extends ContainerLayer { ...@@ -719,7 +889,7 @@ class ClipRRectLayer extends ContainerLayer {
} }
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
bool enabled = true; bool enabled = true;
assert(() { assert(() {
enabled = !debugDisableClipLayers; enabled = !debugDisableClipLayers;
...@@ -730,6 +900,7 @@ class ClipRRectLayer extends ContainerLayer { ...@@ -730,6 +900,7 @@ class ClipRRectLayer extends ContainerLayer {
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder, layerOffset);
if (enabled) if (enabled)
builder.pop(); builder.pop();
return null; // this does not return an engine layer yet.
} }
@override @override
...@@ -749,14 +920,22 @@ class ClipPathLayer extends ContainerLayer { ...@@ -749,14 +920,22 @@ class ClipPathLayer extends ContainerLayer {
/// ///
/// The [clipPath] property must be non-null before the compositing phase of /// The [clipPath] property must be non-null before the compositing phase of
/// the pipeline. /// the pipeline.
ClipPathLayer({ this.clipPath, Clip clipBehavior = Clip.antiAlias }) : ClipPathLayer({ @required Path clipPath, Clip clipBehavior = Clip.antiAlias }) :
_clipBehavior = clipBehavior, assert(clipBehavior != null), assert(clipBehavior != Clip.none); _clipPath = clipPath, _clipBehavior = clipBehavior,
assert(clipBehavior != null), assert(clipBehavior != Clip.none);
/// The path to clip in the parent's coordinate system. /// The path to clip in the parent's coordinate system.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
Path clipPath; Path get clipPath => _clipPath;
Path _clipPath;
set clipPath(Path value) {
if (value != _clipPath) {
_clipPath = value;
markNeedsAddToScene();
}
}
/// {@macro flutter.clipper.clipBehavior} /// {@macro flutter.clipper.clipBehavior}
Clip get clipBehavior => _clipBehavior; Clip get clipBehavior => _clipBehavior;
...@@ -764,7 +943,10 @@ class ClipPathLayer extends ContainerLayer { ...@@ -764,7 +943,10 @@ class ClipPathLayer extends ContainerLayer {
set clipBehavior(Clip value) { set clipBehavior(Clip value) {
assert(value != null); assert(value != null);
assert(value != Clip.none); assert(value != Clip.none);
_clipBehavior = value; if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsAddToScene();
}
} }
@override @override
...@@ -775,7 +957,7 @@ class ClipPathLayer extends ContainerLayer { ...@@ -775,7 +957,7 @@ class ClipPathLayer extends ContainerLayer {
} }
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
bool enabled = true; bool enabled = true;
assert(() { assert(() {
enabled = !debugDisableClipLayers; enabled = !debugDisableClipLayers;
...@@ -786,6 +968,7 @@ class ClipPathLayer extends ContainerLayer { ...@@ -786,6 +968,7 @@ class ClipPathLayer extends ContainerLayer {
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder, layerOffset);
if (enabled) if (enabled)
builder.pop(); builder.pop();
return null; // this does not return an engine layer yet.
} }
} }
...@@ -826,7 +1009,7 @@ class TransformLayer extends OffsetLayer { ...@@ -826,7 +1009,7 @@ class TransformLayer extends OffsetLayer {
bool _inverseDirty = true; bool _inverseDirty = true;
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
_lastEffectiveTransform = transform; _lastEffectiveTransform = transform;
final Offset totalOffset = offset + layerOffset; final Offset totalOffset = offset + layerOffset;
if (totalOffset != Offset.zero) { if (totalOffset != Offset.zero) {
...@@ -836,6 +1019,7 @@ class TransformLayer extends OffsetLayer { ...@@ -836,6 +1019,7 @@ class TransformLayer extends OffsetLayer {
builder.pushTransform(_lastEffectiveTransform.storage); builder.pushTransform(_lastEffectiveTransform.storage);
addChildrenToScene(builder); addChildrenToScene(builder);
builder.pop(); builder.pop();
return null; // this does not return an engine layer yet.
} }
@override @override
...@@ -875,7 +1059,8 @@ class OpacityLayer extends ContainerLayer { ...@@ -875,7 +1059,8 @@ class OpacityLayer extends ContainerLayer {
/// ///
/// The [alpha] property must be non-null before the compositing phase of /// The [alpha] property must be non-null before the compositing phase of
/// the pipeline. /// the pipeline.
OpacityLayer({ this.alpha }); OpacityLayer({ @required int alpha, Offset offset = Offset.zero })
: _alpha = alpha, _offset = offset;
/// The amount to multiply into the alpha channel. /// The amount to multiply into the alpha channel.
/// ///
...@@ -884,20 +1069,38 @@ class OpacityLayer extends ContainerLayer { ...@@ -884,20 +1069,38 @@ class OpacityLayer extends ContainerLayer {
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
int alpha; int get alpha => _alpha;
int _alpha;
set alpha(int value) {
if (value != _alpha) {
_alpha = value;
markNeedsAddToScene();
}
}
/// Offset from parent in the parent's coordinate system.
Offset get offset => _offset;
Offset _offset;
set offset(Offset value) {
if (value != _offset) {
_offset = value;
markNeedsAddToScene();
}
}
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
bool enabled = true; bool enabled = true;
assert(() { assert(() {
enabled = !debugDisableOpacityLayers; enabled = !debugDisableOpacityLayers;
return true; return true;
}()); }());
if (enabled) if (enabled)
builder.pushOpacity(alpha); builder.pushOpacity(alpha, offset: offset + layerOffset);
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder);
if (enabled) if (enabled)
builder.pop(); builder.pop();
return null; // this does not return an engine layer yet.
} }
@override @override
...@@ -913,31 +1116,54 @@ class ShaderMaskLayer extends ContainerLayer { ...@@ -913,31 +1116,54 @@ class ShaderMaskLayer extends ContainerLayer {
/// ///
/// The [shader], [maskRect], and [blendMode] properties must be non-null /// The [shader], [maskRect], and [blendMode] properties must be non-null
/// before the compositing phase of the pipeline. /// before the compositing phase of the pipeline.
ShaderMaskLayer({ this.shader, this.maskRect, this.blendMode }); ShaderMaskLayer({ @required Shader shader, @required Rect maskRect, @required BlendMode blendMode })
: _shader = shader, _maskRect = maskRect, _blendMode = blendMode;
/// The shader to apply to the children. /// The shader to apply to the children.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
Shader shader; Shader get shader => _shader;
Shader _shader;
set shader(Shader value) {
if (value != _shader) {
_shader = value;
markNeedsAddToScene();
}
}
/// The size of the shader. /// The size of the shader.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
Rect maskRect; Rect get maskRect => _maskRect;
Rect _maskRect;
set maskRect(Rect value) {
if (value != _maskRect) {
_maskRect = value;
markNeedsAddToScene();
}
}
/// The blend mode to apply when blending the shader with the children. /// The blend mode to apply when blending the shader with the children.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
BlendMode blendMode; BlendMode get blendMode => _blendMode;
BlendMode _blendMode;
set blendMode(BlendMode value) {
if (value != _blendMode) {
_blendMode = value;
markNeedsAddToScene();
}
}
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
builder.pushShaderMask(shader, maskRect.shift(layerOffset), blendMode); builder.pushShaderMask(shader, maskRect.shift(layerOffset), blendMode);
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder, layerOffset);
builder.pop(); builder.pop();
return null; // this does not return an engine layer yet.
} }
@override @override
...@@ -955,19 +1181,27 @@ class BackdropFilterLayer extends ContainerLayer { ...@@ -955,19 +1181,27 @@ class BackdropFilterLayer extends ContainerLayer {
/// ///
/// The [filter] property must be non-null before the compositing phase of the /// The [filter] property must be non-null before the compositing phase of the
/// pipeline. /// pipeline.
BackdropFilterLayer({ this.filter }); BackdropFilterLayer({ @required ui.ImageFilter filter }) : _filter = filter;
/// The filter to apply to the existing contents of the scene. /// The filter to apply to the existing contents of the scene.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
ui.ImageFilter filter; ui.ImageFilter get filter => _filter;
ui.ImageFilter _filter;
set filter(ui.ImageFilter value) {
if (value != _filter) {
_filter = value;
markNeedsAddToScene();
}
}
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
builder.pushBackdropFilter(filter); builder.pushBackdropFilter(filter);
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder, layerOffset);
builder.pop(); builder.pop();
return null; // this does not return an engine layer yet.
} }
} }
...@@ -986,25 +1220,44 @@ class PhysicalModelLayer extends ContainerLayer { ...@@ -986,25 +1220,44 @@ class PhysicalModelLayer extends ContainerLayer {
/// ///
/// The [clipPath], [elevation], and [color] arguments must not be null. /// The [clipPath], [elevation], and [color] arguments must not be null.
PhysicalModelLayer({ PhysicalModelLayer({
@required this.clipPath, @required Path clipPath,
this.clipBehavior = Clip.none, Clip clipBehavior = Clip.none,
@required this.elevation, @required double elevation,
@required this.color, @required Color color,
@required this.shadowColor, @required Color shadowColor,
}) : assert(clipPath != null), }) : assert(clipPath != null),
assert(clipBehavior != null), assert(clipBehavior != null),
assert(elevation != null), assert(elevation != null),
assert(color != null), assert(color != null),
assert(shadowColor != null); assert(shadowColor != null),
_clipPath = clipPath,
_clipBehavior = clipBehavior,
_elevation = elevation,
_color = color,
_shadowColor = shadowColor;
/// The path to clip in the parent's coordinate system. /// The path to clip in the parent's coordinate system.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
Path clipPath; Path get clipPath => _clipPath;
Path _clipPath;
set clipPath(Path value) {
if (value != _clipPath) {
_clipPath = value;
markNeedsAddToScene();
}
}
/// {@macro flutter.widgets.Clip} /// {@macro flutter.widgets.Clip}
Clip clipBehavior; Clip get clipBehavior => _clipBehavior;
Clip _clipBehavior;
set clipBehavior(Clip value) {
if (value != _clipBehavior) {
_clipBehavior = value;
markNeedsAddToScene();
}
}
/// The z-coordinate at which to place this physical object. /// The z-coordinate at which to place this physical object.
/// ///
...@@ -1016,16 +1269,37 @@ class PhysicalModelLayer extends ContainerLayer { ...@@ -1016,16 +1269,37 @@ class PhysicalModelLayer extends ContainerLayer {
/// flag is set. For this reason, this property will often be set to zero in /// flag is set. For this reason, this property will often be set to zero in
/// tests even if the layer should be raised. To verify the actual value, /// tests even if the layer should be raised. To verify the actual value,
/// consider setting [debugDisableShadows] to false in your test. /// consider setting [debugDisableShadows] to false in your test.
double elevation; double get elevation => _elevation;
double _elevation;
set elevation(double value) {
if (value != _elevation) {
_elevation = value;
markNeedsAddToScene();
}
}
/// The background color. /// The background color.
/// ///
/// The scene must be explicitly recomposited after this property is changed /// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]). /// (as described at [Layer]).
Color color; Color get color => _color;
Color _color;
set color(Color value) {
if (value != _color) {
_color = value;
markNeedsAddToScene();
}
}
/// The shadow color. /// The shadow color.
Color shadowColor; Color get shadowColor => _shadowColor;
Color _shadowColor;
set shadowColor(Color value) {
if (value != _shadowColor) {
_shadowColor = value;
markNeedsAddToScene();
}
}
@override @override
S find<S>(Offset regionOffset) { S find<S>(Offset regionOffset) {
...@@ -1035,14 +1309,15 @@ class PhysicalModelLayer extends ContainerLayer { ...@@ -1035,14 +1309,15 @@ class PhysicalModelLayer extends ContainerLayer {
} }
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
ui.EngineLayer engineLayer;
bool enabled = true; bool enabled = true;
assert(() { assert(() {
enabled = !debugDisablePhysicalShapeLayers; enabled = !debugDisablePhysicalShapeLayers;
return true; return true;
}()); }());
if (enabled) { if (enabled) {
builder.pushPhysicalShape( engineLayer = builder.pushPhysicalShape(
path: clipPath.shift(layerOffset), path: clipPath.shift(layerOffset),
elevation: elevation, elevation: elevation,
color: color, color: color,
...@@ -1053,6 +1328,7 @@ class PhysicalModelLayer extends ContainerLayer { ...@@ -1053,6 +1328,7 @@ class PhysicalModelLayer extends ContainerLayer {
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder, layerOffset);
if (enabled) if (enabled)
builder.pop(); builder.pop();
return engineLayer;
} }
@override @override
...@@ -1115,6 +1391,10 @@ class LeaderLayer extends ContainerLayer { ...@@ -1115,6 +1391,10 @@ class LeaderLayer extends ContainerLayer {
/// pipeline. /// pipeline.
Offset offset; Offset offset;
/// {@macro flutter.leaderFollower.alwaysNeedsAddToScene}
@override
bool get alwaysNeedsAddToScene => true;
@override @override
void attach(Object owner) { void attach(Object owner) {
super.attach(owner); super.attach(owner);
...@@ -1144,7 +1424,7 @@ class LeaderLayer extends ContainerLayer { ...@@ -1144,7 +1424,7 @@ class LeaderLayer extends ContainerLayer {
} }
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
assert(offset != null); assert(offset != null);
_lastOffset = offset + layerOffset; _lastOffset = offset + layerOffset;
if (_lastOffset != Offset.zero) if (_lastOffset != Offset.zero)
...@@ -1152,6 +1432,7 @@ class LeaderLayer extends ContainerLayer { ...@@ -1152,6 +1432,7 @@ class LeaderLayer extends ContainerLayer {
addChildrenToScene(builder); addChildrenToScene(builder);
if (_lastOffset != Offset.zero) if (_lastOffset != Offset.zero)
builder.pop(); builder.pop();
return null; // this does not have an engine layer.
} }
/// Applies the transform that would be applied when compositing the given /// Applies the transform that would be applied when compositing the given
...@@ -1347,15 +1628,28 @@ class FollowerLayer extends ContainerLayer { ...@@ -1347,15 +1628,28 @@ class FollowerLayer extends ContainerLayer {
_inverseDirty = true; _inverseDirty = true;
} }
/// {@template flutter.leaderFollower.alwaysNeedsAddToScene}
/// This disables retained rendering for Leader/FollowerLayer.
///
/// A FollowerLayer copies changes from a LeaderLayer that could be anywhere
/// in the Layer tree, and that LeaderLayer could change without notifying the
/// FollowerLayer. Therefore we have to always call a FollowerLayer's
/// [addToScene]. In order to call FollowerLayer's [addToScene], LeaderLayer's
/// [addToScene] must be called first so LeaderLayer must also be considered
/// as [alwaysNeedsAddToScene].
/// {@endtemplate}
@override
bool get alwaysNeedsAddToScene => true;
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
assert(link != null); assert(link != null);
assert(showWhenUnlinked != null); assert(showWhenUnlinked != null);
if (link.leader == null && !showWhenUnlinked) { if (link.leader == null && !showWhenUnlinked) {
_lastTransform = null; _lastTransform = null;
_lastOffset = null; _lastOffset = null;
_inverseDirty = true; _inverseDirty = true;
return; return null; // this does not have an engine layer.
} }
_establishTransform(); _establishTransform();
if (_lastTransform != null) { if (_lastTransform != null) {
...@@ -1371,6 +1665,7 @@ class FollowerLayer extends ContainerLayer { ...@@ -1371,6 +1665,7 @@ class FollowerLayer extends ContainerLayer {
builder.pop(); builder.pop();
} }
_inverseDirty = true; _inverseDirty = true;
return null; // this does not have an engine layer.
} }
@override @override
......
...@@ -198,6 +198,7 @@ class PaintingContext extends ClipContext { ...@@ -198,6 +198,7 @@ class PaintingContext extends ClipContext {
return true; return true;
}()); }());
} }
assert(child._layer != null);
child._layer.offset = offset; child._layer.offset = offset;
appendLayer(child._layer); appendLayer(child._layer);
} }
...@@ -488,7 +489,7 @@ class PaintingContext extends ClipContext { ...@@ -488,7 +489,7 @@ class PaintingContext extends ClipContext {
/// ancestor render objects that this render object will include a composited /// ancestor render objects that this render object will include a composited
/// layer, which, for example, causes them to use composited clips. /// layer, which, for example, causes them to use composited clips.
void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) { void pushOpacity(Offset offset, int alpha, PaintingContextCallback painter) {
pushLayer(OpacityLayer(alpha: alpha), painter, offset); pushLayer(OpacityLayer(alpha: alpha, offset: offset), painter, Offset.zero);
} }
@override @override
......
...@@ -192,8 +192,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -192,8 +192,7 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
Timeline.startSync('Compositing', arguments: timelineWhitelistArguments); Timeline.startSync('Compositing', arguments: timelineWhitelistArguments);
try { try {
final ui.SceneBuilder builder = ui.SceneBuilder(); final ui.SceneBuilder builder = ui.SceneBuilder();
layer.addToScene(builder); final ui.Scene scene = layer.buildScene(builder);
final ui.Scene scene = builder.build();
if (automaticSystemUiAdjustment) if (automaticSystemUiAdjustment)
_updateSystemChrome(); _updateSystemChrome();
ui.window.render(scene); ui.window.render(scene);
......
...@@ -12,6 +12,7 @@ import 'dart:ui' as ui ...@@ -12,6 +12,7 @@ import 'dart:ui' as ui
show show
window, window,
ClipOp, ClipOp,
EngineLayer,
Image, Image,
ImageByteFormat, ImageByteFormat,
Paragraph, Paragraph,
...@@ -54,8 +55,8 @@ class _ProxyLayer extends Layer { ...@@ -54,8 +55,8 @@ class _ProxyLayer extends Layer {
final Layer _layer; final Layer _layer;
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
_layer.addToScene(builder, layerOffset); return _layer.addToScene(builder, layerOffset);
} }
@override @override
...@@ -312,8 +313,9 @@ Rect _calculateSubtreeBounds(RenderObject object) { ...@@ -312,8 +313,9 @@ Rect _calculateSubtreeBounds(RenderObject object) {
/// screenshots render to the scene in the local coordinate system of the layer. /// screenshots render to the scene in the local coordinate system of the layer.
class _ScreenshotContainerLayer extends OffsetLayer { class _ScreenshotContainerLayer extends OffsetLayer {
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
addChildrenToScene(builder, layerOffset); addChildrenToScene(builder, layerOffset);
return null; // this does not have an engine layer.
} }
} }
...@@ -588,7 +590,7 @@ class _ScreenshotPaintingContext extends PaintingContext { ...@@ -588,7 +590,7 @@ class _ScreenshotPaintingContext extends PaintingContext {
// We must build the regular scene before we can build the screenshot // We must build the regular scene before we can build the screenshot
// scene as building the screenshot scene assumes addToScene has already // scene as building the screenshot scene assumes addToScene has already
// been called successfully for all layers in the regular scene. // been called successfully for all layers in the regular scene.
repaintBoundary.layer.addToScene(ui.SceneBuilder()); repaintBoundary.layer.buildScene(ui.SceneBuilder());
return data.containerLayer.toImage(renderBounds, pixelRatio: pixelRatio); return data.containerLayer.toImage(renderBounds, pixelRatio: pixelRatio);
} }
...@@ -2226,9 +2228,9 @@ class _InspectorOverlayLayer extends Layer { ...@@ -2226,9 +2228,9 @@ class _InspectorOverlayLayer extends Layer {
double _textPainterMaxWidth; double _textPainterMaxWidth;
@override @override
void addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) { ui.EngineLayer addToScene(ui.SceneBuilder builder, [Offset layerOffset = Offset.zero]) {
if (!selection.active) if (!selection.active)
return; return null;
final RenderObject selected = selection.current; final RenderObject selected = selection.current;
final List<_TransformedRect> candidates = <_TransformedRect>[]; final List<_TransformedRect> candidates = <_TransformedRect>[];
...@@ -2251,6 +2253,7 @@ class _InspectorOverlayLayer extends Layer { ...@@ -2251,6 +2253,7 @@ class _InspectorOverlayLayer extends Layer {
_picture = _buildPicture(state); _picture = _buildPicture(state);
} }
builder.addPicture(layerOffset, _picture); builder.addPicture(layerOffset, _picture);
return null; // this does not have an engine layer.
} }
ui.Picture _buildPicture(_InspectorOverlayRenderState state) { ui.Picture _buildPicture(_InspectorOverlayRenderState state) {
......
...@@ -40,4 +40,73 @@ void main() { ...@@ -40,4 +40,73 @@ void main() {
expect(boundary.layer, isNotNull); expect(boundary.layer, isNotNull);
expect(boundary.layer.attached, isTrue); // this time it did again! expect(boundary.layer.attached, isTrue); // this time it did again!
}); });
test('layer subtree dirtiness is correctly computed', () {
final ContainerLayer a = ContainerLayer();
final ContainerLayer b = ContainerLayer();
final ContainerLayer c = ContainerLayer();
final ContainerLayer d = ContainerLayer();
final ContainerLayer e = ContainerLayer();
final ContainerLayer f = ContainerLayer();
final ContainerLayer g = ContainerLayer();
final PictureLayer h = PictureLayer(Rect.zero);
final PictureLayer i = PictureLayer(Rect.zero);
final PictureLayer j = PictureLayer(Rect.zero);
// The tree is like the following where b and j are dirty:
// a____
// / \
// (x)b___ c
// / \ \ |
// d e f g
// / \ |
// h i j(x)
a.append(b);
a.append(c);
b.append(d);
b.append(e);
b.append(f);
d.append(h);
d.append(i);
c.append(g);
g.append(j);
a.debugMarkClean();
b.markNeedsAddToScene(); // ignore: invalid_use_of_protected_member
c.debugMarkClean();
d.debugMarkClean();
e.debugMarkClean();
f.debugMarkClean();
g.debugMarkClean();
h.debugMarkClean();
i.debugMarkClean();
j.markNeedsAddToScene(); // ignore: invalid_use_of_protected_member
a.updateSubtreeNeedsAddToScene();
expect(a.debugSubtreeNeedsAddToScene, true);
expect(b.debugSubtreeNeedsAddToScene, true);
expect(c.debugSubtreeNeedsAddToScene, true);
expect(g.debugSubtreeNeedsAddToScene, true);
expect(j.debugSubtreeNeedsAddToScene, true);
expect(d.debugSubtreeNeedsAddToScene, false);
expect(e.debugSubtreeNeedsAddToScene, false);
expect(f.debugSubtreeNeedsAddToScene, false);
expect(h.debugSubtreeNeedsAddToScene, false);
expect(i.debugSubtreeNeedsAddToScene, false);
});
test('leader and follower layers are always dirty', () {
final LayerLink link = LayerLink();
final LeaderLayer leaderLayer = LeaderLayer(link: link);
final FollowerLayer followerLayer = FollowerLayer(link: link);
leaderLayer.debugMarkClean();
followerLayer.debugMarkClean();
leaderLayer.updateSubtreeNeedsAddToScene();
followerLayer.updateSubtreeNeedsAddToScene();
expect(leaderLayer.debugSubtreeNeedsAddToScene, true);
expect(followerLayer.debugSubtreeNeedsAddToScene, true);
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment