Reland "Add a way to raster cache the children of a Layer (#32370)" (flutter/engine#32378)

This commit is contained in:
ColdPaleLight 2022-04-06 19:16:04 +08:00 committed by GitHub
parent abb86f34f3
commit ffe61d39ca
26 changed files with 442 additions and 274 deletions

View File

@ -29,6 +29,7 @@ TEST_F(BackdropFilterLayerTest, PaintingEmptyLayerDies) {
parent->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -44,6 +45,7 @@ TEST_F(BackdropFilterLayerTest, PaintBeforePrerollDies) {
layer->Add(mock_layer);
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
@ -63,6 +65,7 @@ TEST_F(BackdropFilterLayerTest, EmptyFilter) {
parent->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -92,6 +95,7 @@ TEST_F(BackdropFilterLayerTest, SimpleFilter) {
parent->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -121,6 +125,7 @@ TEST_F(BackdropFilterLayerTest, NonSrcOverBlend) {
parent->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -163,6 +168,7 @@ TEST_F(BackdropFilterLayerTest, MultipleChildren) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer->paint_bounds(), children_bounds);
EXPECT_EQ(layer->child_paint_bounds(), children_bounds);
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));

View File

@ -28,6 +28,7 @@ TEST_F(ClipPathLayerTest, PaintingEmptyLayerDies) {
EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -39,6 +40,7 @@ TEST_F(ClipPathLayerTest, PaintBeforePrerollDies) {
const SkPath layer_path = SkPath().addRect(layer_bounds);
auto layer = std::make_shared<ClipPathLayer>(layer_path, Clip::hardEdge);
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -63,6 +65,7 @@ TEST_F(ClipPathLayerTest, PaintingCulledLayerDies) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
@ -100,6 +103,7 @@ TEST_F(ClipPathLayerTest, ChildOutsideBounds) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds);
@ -135,6 +139,7 @@ TEST_F(ClipPathLayerTest, FullyContainedChild) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
@ -177,6 +182,7 @@ TEST_F(ClipPathLayerTest, PartiallyContainedChild) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds);

View File

@ -28,6 +28,7 @@ TEST_F(ClipRectLayerTest, PaintingEmptyLayerDies) {
EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -38,6 +39,7 @@ TEST_F(ClipRectLayerTest, PaintBeforePrerollDies) {
const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
auto layer = std::make_shared<ClipRectLayer>(layer_bounds, Clip::hardEdge);
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -61,6 +63,7 @@ TEST_F(ClipRectLayerTest, PaintingCulledLayerDies) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
@ -98,6 +101,7 @@ TEST_F(ClipRectLayerTest, ChildOutsideBounds) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds);
@ -133,6 +137,7 @@ TEST_F(ClipRectLayerTest, FullyContainedChild) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
@ -175,6 +180,7 @@ TEST_F(ClipRectLayerTest, PartiallyContainedChild) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds);

View File

@ -30,6 +30,7 @@ TEST_F(ClipRRectLayerTest, PaintingEmptyLayerDies) {
EXPECT_EQ(preroll_context()->cull_rect, kGiantRect); // Untouched
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -41,6 +42,7 @@ TEST_F(ClipRRectLayerTest, PaintBeforePrerollDies) {
const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds);
auto layer = std::make_shared<ClipRRectLayer>(layer_rrect, Clip::hardEdge);
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -66,6 +68,7 @@ TEST_F(ClipRRectLayerTest, PaintingCulledLayerDies) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), kEmptyRect);
@ -103,6 +106,7 @@ TEST_F(ClipRRectLayerTest, ChildOutsideBounds) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds);
@ -138,6 +142,7 @@ TEST_F(ClipRRectLayerTest, FullyContainedChild) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), layer_bounds);
@ -180,6 +185,7 @@ TEST_F(ClipRRectLayerTest, PartiallyContainedChild) {
EXPECT_TRUE(preroll_context()->mutators_stack.is_empty()); // Untouched
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_intersect_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_cull_rect(), intersect_bounds);

View File

@ -31,10 +31,12 @@ void ColorFilterLayer::Preroll(PrerollContext* context,
ContainerLayer::Preroll(context, matrix);
if (render_count_ >= kMinimumRendersBeforeCachingFilterLayer) {
TryToPrepareRasterCache(context, this, matrix);
TryToPrepareRasterCache(context, this, matrix,
RasterCacheLayerStrategy::kLayer);
} else {
render_count_++;
TryToPrepareRasterCache(context, GetCacheableChild(), matrix);
TryToPrepareRasterCache(context, this, matrix,
RasterCacheLayerStrategy::kLayerChildren);
}
}
@ -43,14 +45,16 @@ void ColorFilterLayer::Paint(PaintContext& context) const {
FML_DCHECK(needs_painting(context));
if (context.raster_cache) {
if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas)) {
if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas,
RasterCacheLayerStrategy::kLayer)) {
return;
}
SkPaint paint;
paint.setColorFilter(filter_);
if (context.raster_cache->Draw(GetCacheableChild(),
*context.leaf_nodes_canvas, &paint)) {
if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas,
RasterCacheLayerStrategy::kLayerChildren,
&paint)) {
return;
}
}

View File

@ -10,7 +10,7 @@
namespace flutter {
class ColorFilterLayer : public MergedContainerLayer {
class ColorFilterLayer : public ContainerLayer {
public:
explicit ColorFilterLayer(sk_sp<SkColorFilter> filter);

View File

@ -22,6 +22,7 @@ TEST_F(ColorFilterLayerTest, PaintingEmptyLayerDies) {
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -36,6 +37,7 @@ TEST_F(ColorFilterLayerTest, PaintBeforePrerollDies) {
layer->Add(mock_layer);
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
@ -52,6 +54,7 @@ TEST_F(ColorFilterLayerTest, EmptyFilter) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -81,6 +84,7 @@ TEST_F(ColorFilterLayerTest, SimpleFilter) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -119,6 +123,7 @@ TEST_F(ColorFilterLayerTest, MultipleChildren) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer->paint_bounds(), children_bounds);
EXPECT_EQ(layer->child_paint_bounds(), children_bounds);
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
@ -166,7 +171,9 @@ TEST_F(ColorFilterLayerTest, Nested) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer1->paint_bounds(), children_bounds);
EXPECT_EQ(layer1->child_paint_bounds(), children_bounds);
EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds());
EXPECT_EQ(layer2->child_paint_bounds(), mock_layer2->paint_bounds());
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer1->needs_painting(paint_context()));
@ -213,7 +220,7 @@ TEST_F(ColorFilterLayerTest, Readback) {
EXPECT_FALSE(preroll_context()->surface_needs_readback);
}
TEST_F(ColorFilterLayerTest, ChildIsCached) {
TEST_F(ColorFilterLayerTest, CacheChild) {
auto layer_filter =
SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW);
auto initial_transform = SkMatrix::Translate(50.0, 25.5);
@ -234,15 +241,31 @@ TEST_F(ColorFilterLayerTest, ChildIsCached) {
EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
}
TEST_F(ColorFilterLayerTest, ChildrenNotCached) {
TEST_F(ColorFilterLayerTest, CacheChildren) {
auto layer_filter =
SkColorMatrixFilter::MakeLightingFilter(SK_ColorGREEN, SK_ColorYELLOW);
auto initial_transform = SkMatrix::Translate(50.0, 25.5);
@ -268,6 +291,14 @@ TEST_F(ColorFilterLayerTest, ChildrenNotCached) {
EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
layer->Preroll(preroll_context(), initial_transform);
@ -276,6 +307,14 @@ TEST_F(ColorFilterLayerTest, ChildrenNotCached) {
EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
}
} // namespace testing

View File

@ -8,7 +8,7 @@
namespace flutter {
ContainerLayer::ContainerLayer() {}
ContainerLayer::ContainerLayer() : child_paint_bounds_(SkRect::MakeEmpty()) {}
void ContainerLayer::Diff(DiffContext* context, const Layer* old_layer) {
auto old_container = static_cast<const ContainerLayer*>(old_layer);
@ -170,6 +170,7 @@ void ContainerLayer::PrerollChildren(PrerollContext* context,
context->has_texture_layer = child_has_texture_layer;
context->subtree_can_inherit_opacity = subtree_can_inherit_opacity;
set_subtree_has_platform_view(child_has_platform_view);
child_paint_bounds_ = *child_paint_bounds;
}
void ContainerLayer::PaintChildren(PaintContext& context) const {
@ -188,71 +189,19 @@ void ContainerLayer::PaintChildren(PaintContext& context) const {
}
}
void ContainerLayer::TryToPrepareRasterCache(PrerollContext* context,
Layer* layer,
const SkMatrix& matrix) {
void ContainerLayer::TryToPrepareRasterCache(
PrerollContext* context,
Layer* layer,
const SkMatrix& matrix,
RasterCacheLayerStrategy strategy) {
if (!context->has_platform_view && !context->has_texture_layer &&
context->raster_cache &&
SkRect::Intersects(context->cull_rect, layer->paint_bounds())) {
context->raster_cache->Prepare(context, layer, matrix);
context->raster_cache->Prepare(context, layer, matrix, strategy);
} else if (context->raster_cache) {
// Don't evict raster cache entry during partial repaint
context->raster_cache->Touch(layer, matrix);
context->raster_cache->Touch(layer, matrix, strategy);
}
}
MergedContainerLayer::MergedContainerLayer() {
// Ensure the layer has only one direct child.
//
// Any children will actually be added as children of this empty
// ContainerLayer which can be accessed via ::GetContainerLayer().
// If only one child is ever added to this layer then that child
// will become the layer returned from ::GetCacheableChild().
// If multiple child layers are added, then this implicit container
// child becomes the cacheable child, but at the potential cost of
// not being as stable in the raster cache from frame to frame.
ContainerLayer::Add(std::make_shared<ContainerLayer>());
// The interposing Container only recurses to its children with no
// additional processing so, by default, it can pass an inherited
// opacity on to its children, subject only to the accumulation
// logic that happens during |PrerollChildren|.
GetChildContainer()->set_layer_can_inherit_opacity(true);
}
void MergedContainerLayer::DiffChildren(DiffContext* context,
const ContainerLayer* old_layer) {
if (context->IsSubtreeDirty()) {
GetChildContainer()->Diff(context, nullptr);
return;
}
FML_DCHECK(old_layer);
// For MergedContainerLayer we want to diff children of ChildContainer
// instead of the ChildContainer itself. This works around the fact
// that ChildContainerLayer is ephemeral and its original_layer_id_ is always
// different.
auto layer = static_cast<const MergedContainerLayer*>(old_layer);
GetChildContainer()->DiffChildren(context, layer->GetChildContainer());
}
void MergedContainerLayer::Add(std::shared_ptr<Layer> layer) {
GetChildContainer()->Add(std::move(layer));
}
ContainerLayer* MergedContainerLayer::GetChildContainer() const {
FML_DCHECK(layers().size() == 1);
return static_cast<ContainerLayer*>(layers()[0].get());
}
Layer* MergedContainerLayer::GetCacheableChild() const {
ContainerLayer* child_container = GetChildContainer();
if (child_container->layers().size() == 1) {
return child_container->layers()[0].get();
}
return child_container;
}
} // namespace flutter

View File

@ -27,12 +27,15 @@ class ContainerLayer : public Layer {
virtual void DiffChildren(DiffContext* context,
const ContainerLayer* old_layer);
void PaintChildren(PaintContext& context) const;
const ContainerLayer* as_container_layer() const override { return this; }
const SkRect& child_paint_bounds() const { return child_paint_bounds_; }
protected:
void PrerollChildren(PrerollContext* context,
const SkMatrix& child_matrix,
SkRect* child_paint_bounds);
void PaintChildren(PaintContext& context) const;
// Try to prepare the raster cache for a given layer.
//
@ -46,92 +49,16 @@ class ContainerLayer : public Layer {
// cache a child layer and one can't access its child's protected method.
static void TryToPrepareRasterCache(PrerollContext* context,
Layer* layer,
const SkMatrix& matrix);
const SkMatrix& matrix,
RasterCacheLayerStrategy strategy);
private:
std::vector<std::shared_ptr<Layer>> layers_;
SkRect child_paint_bounds_;
FML_DISALLOW_COPY_AND_ASSIGN(ContainerLayer);
};
//------------------------------------------------------------------------------
/// Some ContainerLayer objects perform a rendering operation or filter on
/// the rendered output of their children. Often that operation is changed
/// slightly from frame to frame as part of an animation. During such an
/// animation, the children can be cached if they are stable to avoid having
/// to render them on every frame. Even if the children are not stable,
/// rendering them into the raster cache during a Preroll operation will save
/// an extra change of rendering surface during the Paint phase as compared
/// to using the SaveLayer that would otherwise be needed with no caching.
///
/// Typically the Flutter Widget objects that lead to the creation of these
/// layers will try to enforce only a single child Widget by their design.
/// Unfortunately, the process of turning Widgets eventually into engine
/// layers is not a 1:1 process so this layer might end up with multiple
/// child layers even if the Widget only had a single child Widget.
///
/// When such a layer goes to cache the output of its children, it will
/// need to supply a single layer to the cache mechanism since the raster
/// cache uses a layer unique_id() as part of the cache key. If this layer
/// ended up with multiple children, then it must first collect them into
/// one layer for the cache mechanism. In order to provide a single layer
/// for all of the children, this utility class will implicitly collect
/// the children into a secondary ContainerLayer called the child container.
///
/// A by-product of creating a hidden child container, though, is that the
/// child container is created new every time this layer is created with
/// different properties, such as during an animation. In that scenario,
/// it would be best to cache the single real child of this layer if it
/// is unique and if it is stable from frame to frame. To facilitate this
/// optimal caching strategy, this class implements two accessor methods
/// to be used for different purposes:
///
/// When the layer needs to recurse to perform some operation on its children,
/// it can call GetChildContainer() to return the hidden container containing
/// all of the real children.
///
/// When the layer wants to cache the rendered contents of its children, it
/// should call GetCacheableChild() for best performance. This method may
/// end up returning the same layer as GetChildContainer(), but only if the
/// conditions for optimal caching of a single child are not met.
///
class MergedContainerLayer : public ContainerLayer {
public:
MergedContainerLayer();
void Add(std::shared_ptr<Layer> layer) override;
void DiffChildren(DiffContext* context,
const ContainerLayer* old_layer) override;
protected:
/**
* @brief Returns the ContainerLayer used to hold all of the children of the
* MergedContainerLayer. Note that this may not be the best layer to use
* for caching the children.
*
* @see GetCacheableChild()
* @return the ContainerLayer child used to hold the children
*/
ContainerLayer* GetChildContainer() const;
/**
* @brief Returns the best choice for a Layer object that can be used
* in RasterCache operations to cache the children.
*
* The returned Layer must represent all children and try to remain stable
* if the MergedContainerLayer is reconstructed in subsequent frames of
* the scene.
*
* @see GetChildContainer()
* @return the best candidate Layer for caching the children
*/
Layer* GetCacheableChild() const;
private:
FML_DISALLOW_COPY_AND_ASSIGN(MergedContainerLayer);
};
} // namespace flutter
#endif // FLUTTER_FLOW_LAYERS_CONTAINER_LAYER_H_

View File

@ -29,6 +29,7 @@ TEST_F(ContainerLayerTest, PaintingEmptyLayerDies) {
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty());
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -43,6 +44,7 @@ TEST_F(ContainerLayerTest, PaintBeforePrerollDies) {
layer->Add(mock_layer);
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty());
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
@ -62,6 +64,7 @@ TEST_F(ContainerLayerTest, Simple) {
EXPECT_FALSE(preroll_context()->has_platform_view);
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds());
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -96,6 +99,7 @@ TEST_F(ContainerLayerTest, Multiple) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer->paint_bounds(), expected_total_bounds);
EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds());
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
@ -132,6 +136,7 @@ TEST_F(ContainerLayerTest, MultipleWithEmpty) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), SkPath().getBounds());
EXPECT_EQ(layer->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds());
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_FALSE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
@ -169,6 +174,7 @@ TEST_F(ContainerLayerTest, NeedsSystemComposite) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer->paint_bounds(), expected_total_bounds);
EXPECT_EQ(layer->child_paint_bounds(), layer->paint_bounds());
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
@ -186,71 +192,6 @@ TEST_F(ContainerLayerTest, NeedsSystemComposite) {
child_path2, child_paint2}}}));
}
TEST_F(ContainerLayerTest, MergedOneChild) {
SkPath child_path;
child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
SkPaint child_paint(SkColors::kGreen);
SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f);
auto mock_layer = std::make_shared<MockLayer>(child_path, child_paint);
auto layer = std::make_shared<MergedContainerLayer>();
layer->Add(mock_layer);
layer->Preroll(preroll_context(), initial_transform);
EXPECT_FALSE(preroll_context()->has_platform_view);
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer->paint_bounds(), child_path.getBounds());
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
EXPECT_EQ(mock_layer->parent_cull_rect(), kGiantRect);
layer->Paint(paint_context());
EXPECT_EQ(mock_canvas().draw_calls(),
std::vector({MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{child_path, child_paint}}}));
}
TEST_F(ContainerLayerTest, MergedMultipleChildren) {
SkPath child_path1;
child_path1.addRect(5.0f, 6.0f, 20.5f, 21.5f);
SkPath child_path2;
child_path2.addRect(58.0f, 2.0f, 16.5f, 14.5f);
SkPaint child_paint1(SkColors::kGray);
SkPaint child_paint2(SkColors::kGreen);
SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f);
auto mock_layer1 = std::make_shared<MockLayer>(child_path1, child_paint1);
auto mock_layer2 = std::make_shared<MockLayer>(child_path2, child_paint2);
auto layer = std::make_shared<MergedContainerLayer>();
layer->Add(mock_layer1);
layer->Add(mock_layer2);
SkRect expected_total_bounds = child_path1.getBounds();
expected_total_bounds.join(child_path2.getBounds());
layer->Preroll(preroll_context(), initial_transform);
EXPECT_FALSE(preroll_context()->has_platform_view);
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer->paint_bounds(), expected_total_bounds);
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer1->parent_matrix(), initial_transform);
EXPECT_EQ(mock_layer2->parent_matrix(), initial_transform);
EXPECT_EQ(mock_layer1->parent_cull_rect(), kGiantRect);
EXPECT_EQ(mock_layer2->parent_cull_rect(),
kGiantRect); // Siblings are independent
layer->Paint(paint_context());
EXPECT_EQ(
mock_canvas().draw_calls(),
std::vector({MockCanvas::DrawCall{
0, MockCanvas::DrawPathData{child_path1, child_paint1}},
MockCanvas::DrawCall{0, MockCanvas::DrawPathData{
child_path2, child_paint2}}}));
}
using ContainerLayerDiffTest = DiffContextTest;
// Insert PictureLayer amongst container layers

View File

@ -63,7 +63,8 @@ void ImageFilterLayer::Preroll(PrerollContext* context,
// times to consider its properties and children to be stable
// from frame to frame so we try to cache the layer itself
// for maximum performance.
TryToPrepareRasterCache(context, this, matrix);
TryToPrepareRasterCache(context, this, matrix,
RasterCacheLayerStrategy::kLayer);
} else {
// This ImageFilterLayer is not yet considered stable so we
// increment the count to measure how many times it has been
@ -84,7 +85,8 @@ void ImageFilterLayer::Preroll(PrerollContext* context,
// stable between frames and also avoiding a rendering surface
// switch during the Paint phase even if they are not stable.
// This benefit is seen most during animations.
TryToPrepareRasterCache(context, GetCacheableChild(), matrix);
TryToPrepareRasterCache(context, this, matrix,
RasterCacheLayerStrategy::kLayerChildren);
}
}
}
@ -94,15 +96,16 @@ void ImageFilterLayer::Paint(PaintContext& context) const {
FML_DCHECK(needs_painting(context));
if (context.raster_cache) {
if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas)) {
if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas,
RasterCacheLayerStrategy::kLayer)) {
return;
}
if (transformed_filter_) {
SkPaint paint;
paint.setImageFilter(transformed_filter_);
if (context.raster_cache->Draw(GetCacheableChild(),
*context.leaf_nodes_canvas, &paint)) {
if (context.raster_cache->Draw(this, *context.leaf_nodes_canvas,
RasterCacheLayerStrategy::kLayerChildren,
&paint)) {
return;
}
}
@ -115,8 +118,8 @@ void ImageFilterLayer::Paint(PaintContext& context) const {
// case the bounds of the child may not be the same as the filtered version
// so we use the bounds of the child container which do not include any
// modifications that the filter might apply.
Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create(
context, GetChildContainer()->paint_bounds(), &paint);
Layer::AutoSaveLayer save_layer =
Layer::AutoSaveLayer::Create(context, child_paint_bounds(), &paint);
PaintChildren(context);
}

View File

@ -10,7 +10,7 @@
namespace flutter {
class ImageFilterLayer : public MergedContainerLayer {
class ImageFilterLayer : public ContainerLayer {
public:
explicit ImageFilterLayer(sk_sp<SkImageFilter> filter);

View File

@ -37,6 +37,7 @@ TEST_F(ImageFilterLayerTest, PaintBeforePrerollDies) {
layer->Add(mock_layer);
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
@ -53,6 +54,7 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -87,6 +89,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_rounded_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -121,6 +124,7 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), filter_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -163,6 +167,7 @@ TEST_F(ImageFilterLayerTest, MultipleChildren) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer->paint_bounds(), children_rounded_bounds);
EXPECT_EQ(layer->child_paint_bounds(), children_bounds);
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
@ -217,7 +222,9 @@ TEST_F(ImageFilterLayerTest, Nested) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer1->paint_bounds(), children_rounded_bounds);
EXPECT_EQ(layer1->child_paint_bounds(), children_bounds);
EXPECT_EQ(layer2->paint_bounds(), mock_layer2_rounded_bounds);
EXPECT_EQ(layer2->child_paint_bounds(), child_path2.getBounds());
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer1->needs_painting(paint_context()));
@ -267,7 +274,7 @@ TEST_F(ImageFilterLayerTest, Readback) {
EXPECT_FALSE(preroll_context()->surface_needs_readback);
}
TEST_F(ImageFilterLayerTest, ChildIsCached) {
TEST_F(ImageFilterLayerTest, CacheChild) {
auto layer_filter = SkImageFilters::MatrixTransform(
SkMatrix(),
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
@ -289,15 +296,31 @@ TEST_F(ImageFilterLayerTest, ChildIsCached) {
EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
}
TEST_F(ImageFilterLayerTest, ChildrenNotCached) {
TEST_F(ImageFilterLayerTest, CacheChildren) {
auto layer_filter = SkImageFilters::MatrixTransform(
SkMatrix(),
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kLinear), nullptr);
@ -324,6 +347,14 @@ TEST_F(ImageFilterLayerTest, ChildrenNotCached) {
EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
layer->Preroll(preroll_context(), initial_transform);
@ -332,6 +363,14 @@ TEST_F(ImageFilterLayerTest, ChildrenNotCached) {
EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
}
using ImageFilterLayerDiffTest = DiffContextTest;

View File

@ -82,6 +82,7 @@ struct PrerollContext {
bool subtree_can_inherit_opacity = false;
};
class ContainerLayer;
class PictureLayer;
class DisplayListLayer;
class PerformanceOverlayLayer;
@ -330,6 +331,7 @@ class Layer {
uint64_t unique_id() const { return unique_id_; }
virtual const ContainerLayer* as_container_layer() const { return nullptr; }
virtual const PictureLayer* as_picture_layer() const { return nullptr; }
virtual const DisplayListLayer* as_display_list_layer() const {
return nullptr;

View File

@ -37,7 +37,7 @@ void OpacityLayer::Diff(DiffContext* context, const Layer* old_layer) {
void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
TRACE_EVENT0("flutter", "OpacityLayer::Preroll");
FML_DCHECK(!GetChildContainer()->layers().empty()); // We can't be a leaf.
FML_DCHECK(!layers().empty()); // We can't be a leaf.
SkMatrix child_matrix = matrix;
child_matrix.preTranslate(offset_.fX, offset_.fY);
@ -63,7 +63,8 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
#ifndef SUPPORT_FRACTIONAL_TRANSLATION
child_matrix = RasterCache::GetIntegralTransCTM(child_matrix);
#endif
TryToPrepareRasterCache(context, GetCacheableChild(), child_matrix);
TryToPrepareRasterCache(context, this, child_matrix,
RasterCacheLayerStrategy::kLayerChildren);
}
// Restore cull_rect
@ -96,8 +97,9 @@ void OpacityLayer::Paint(PaintContext& context) const {
paint.setAlphaf(subtree_opacity);
if (context.raster_cache &&
context.raster_cache->Draw(GetCacheableChild(),
*context.leaf_nodes_canvas, &paint)) {
context.raster_cache->Draw(this, *context.leaf_nodes_canvas,
RasterCacheLayerStrategy::kLayerChildren,
&paint)) {
return;
}

View File

@ -13,7 +13,7 @@ namespace flutter {
// OpacityLayer is very costly due to the saveLayer call. If there's no child,
// having the OpacityLayer or not has the same effect. In debug_unopt build,
// |Preroll| will assert if there are no children.
class OpacityLayer : public MergedContainerLayer {
class OpacityLayer : public ContainerLayer {
public:
// An offset is provided here because OpacityLayer.addToScene method in the
// Flutter framework can take an optional offset argument.

View File

@ -21,9 +21,8 @@ TEST_F(OpacityLayerTest, LeafLayer) {
auto layer =
std::make_shared<OpacityLayer>(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f));
EXPECT_DEATH_IF_SUPPORTED(
layer->Preroll(preroll_context(), SkMatrix()),
"\\!GetChildContainer\\(\\)->layers\\(\\)\\.empty\\(\\)");
EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()),
"\\!layers\\(\\)\\.empty\\(\\)");
}
TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) {
@ -35,6 +34,7 @@ TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) {
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(mock_layer->paint_bounds(), SkPath().getBounds());
EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds());
EXPECT_FALSE(mock_layer->needs_painting(paint_context()));
EXPECT_FALSE(layer->needs_painting(paint_context()));
@ -72,7 +72,7 @@ TEST_F(OpacityLayerTest, TranslateChildren) {
EXPECT_EQ(layer_bounds, SkRect::MakeXYWH(40, 40, 20, 20));
}
TEST_F(OpacityLayerTest, ChildIsCached) {
TEST_F(OpacityLayerTest, CacheChild) {
const SkAlpha alpha_half = 255 / 2;
auto initial_transform = SkMatrix::Translate(50.0, 25.5);
auto other_transform = SkMatrix::Scale(1.0, 2.0);
@ -93,15 +93,31 @@ TEST_F(OpacityLayerTest, ChildIsCached) {
EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)0);
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(raster_cache()->GetLayerCachedEntriesCount(), (size_t)1);
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), other_canvas));
EXPECT_TRUE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
}
TEST_F(OpacityLayerTest, ChildrenNotCached) {
TEST_F(OpacityLayerTest, CacheChildren) {
const SkAlpha alpha_half = 255 / 2;
auto initial_transform = SkMatrix::Translate(50.0, 25.5);
auto other_transform = SkMatrix::Scale(1.0, 2.0);
@ -127,6 +143,14 @@ TEST_F(OpacityLayerTest, ChildrenNotCached) {
EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
layer->Preroll(preroll_context(), initial_transform);
@ -135,6 +159,14 @@ TEST_F(OpacityLayerTest, ChildrenNotCached) {
EXPECT_FALSE(raster_cache()->Draw(mock_layer1.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), other_canvas));
EXPECT_FALSE(raster_cache()->Draw(mock_layer2.get(), cache_canvas));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayer));
EXPECT_FALSE(raster_cache()->Draw(layer.get(), other_canvas,
RasterCacheLayerStrategy::kLayerChildren));
EXPECT_TRUE(raster_cache()->Draw(layer.get(), cache_canvas,
RasterCacheLayerStrategy::kLayerChildren));
}
TEST_F(OpacityLayerTest, FullyOpaque) {
@ -157,6 +189,7 @@ TEST_F(OpacityLayerTest, FullyOpaque) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds());
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(),
@ -208,6 +241,7 @@ TEST_F(OpacityLayerTest, FullyTransparent) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds());
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(),
@ -255,6 +289,7 @@ TEST_F(OpacityLayerTest, HalfTransparent) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer->paint_bounds(), expected_layer_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_path.getBounds());
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(),
@ -321,16 +356,18 @@ TEST_F(OpacityLayerTest, Nested) {
const SkRect expected_layer2_bounds =
layer2_transform.mapRect(child2_path.getBounds());
SkRect expected_layer1_bounds = expected_layer2_bounds;
expected_layer1_bounds.join(child1_path.getBounds());
expected_layer1_bounds.join(child3_path.getBounds());
expected_layer1_bounds = layer1_transform.mapRect(expected_layer1_bounds);
SkRect layer1_child_bounds = expected_layer2_bounds;
layer1_child_bounds.join(child1_path.getBounds());
layer1_child_bounds.join(child3_path.getBounds());
SkRect expected_layer1_bounds = layer1_transform.mapRect(layer1_child_bounds);
layer1->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(mock_layer1->paint_bounds(), child1_path.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child2_path.getBounds());
EXPECT_EQ(mock_layer3->paint_bounds(), child3_path.getBounds());
EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds);
EXPECT_EQ(layer1->child_paint_bounds(), layer1_child_bounds);
EXPECT_EQ(layer2->paint_bounds(), expected_layer2_bounds);
EXPECT_EQ(layer2->child_paint_bounds(), child2_path.getBounds());
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer3->needs_painting(paint_context()));

View File

@ -25,6 +25,7 @@ TEST_F(PhysicalShapeLayerTest, PaintingEmptyLayerDies) {
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty());
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -55,6 +56,7 @@ TEST_F(PhysicalShapeLayerTest, NonEmptyLayer) {
layer_path, Clip::none);
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds());
EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty());
EXPECT_TRUE(layer->needs_painting(paint_context()));
SkPaint layer_paint;
@ -93,6 +95,7 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPathClip) {
child_paint_bounds.join(child2->paint_bounds());
EXPECT_EQ(layer->paint_bounds(), layer_path.getBounds());
EXPECT_NE(layer->paint_bounds(), child_paint_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_paint_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
SkPaint layer_paint;
@ -142,13 +145,14 @@ TEST_F(PhysicalShapeLayerTest, ChildrenLargerThanPathNoClip) {
layer->Add(child1);
layer->Add(child2);
SkRect total_bounds = SkRect::MakeEmpty();
layer->Preroll(preroll_context(), SkMatrix());
total_bounds.join(child1->paint_bounds());
total_bounds.join(child2->paint_bounds());
SkRect child_bounds = child1->paint_bounds();
child_bounds.join(child2->paint_bounds());
SkRect total_bounds = child_bounds;
total_bounds.join(layer_path.getBounds());
EXPECT_NE(layer->paint_bounds(), layer_path.getBounds());
EXPECT_EQ(layer->paint_bounds(), total_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
SkPaint layer_paint;

View File

@ -23,6 +23,7 @@ TEST_F(ShaderMaskLayerTest, PaintingEmptyLayerDies) {
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -38,6 +39,7 @@ TEST_F(ShaderMaskLayerTest, PaintBeforePrerollDies) {
layer->Add(mock_layer);
EXPECT_EQ(layer->paint_bounds(), kEmptyRect);
EXPECT_EQ(layer->child_paint_bounds(), kEmptyRect);
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
"needs_painting\\(context\\)");
}
@ -57,6 +59,7 @@ TEST_F(ShaderMaskLayerTest, EmptyFilter) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(mock_layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -98,6 +101,7 @@ TEST_F(ShaderMaskLayerTest, SimpleFilter) {
layer->Preroll(preroll_context(), initial_transform);
EXPECT_EQ(layer->paint_bounds(), child_bounds);
EXPECT_EQ(layer->child_paint_bounds(), child_bounds);
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), initial_transform);
@ -147,6 +151,7 @@ TEST_F(ShaderMaskLayerTest, MultipleChildren) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer->paint_bounds(), children_bounds);
EXPECT_EQ(layer->child_paint_bounds(), children_bounds);
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
@ -206,7 +211,9 @@ TEST_F(ShaderMaskLayerTest, Nested) {
EXPECT_EQ(mock_layer1->paint_bounds(), child_path1.getBounds());
EXPECT_EQ(mock_layer2->paint_bounds(), child_path2.getBounds());
EXPECT_EQ(layer1->paint_bounds(), children_bounds);
EXPECT_EQ(layer1->child_paint_bounds(), children_bounds);
EXPECT_EQ(layer2->paint_bounds(), mock_layer2->paint_bounds());
EXPECT_EQ(layer2->child_paint_bounds(), mock_layer2->paint_bounds());
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer1->needs_painting(paint_context()));

View File

@ -21,6 +21,7 @@ TEST_F(TransformLayerTest, PaintingEmptyLayerDies) {
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(layer->paint_bounds(), SkRect::MakeEmpty());
EXPECT_EQ(layer->child_paint_bounds(), SkRect::MakeEmpty());
EXPECT_FALSE(layer->needs_painting(paint_context()));
EXPECT_DEATH_IF_SUPPORTED(layer->Paint(paint_context()),
@ -51,6 +52,7 @@ TEST_F(TransformLayerTest, Identity) {
layer->Preroll(preroll_context(), SkMatrix());
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer->paint_bounds(), mock_layer->paint_bounds());
EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds());
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); // identity
@ -81,6 +83,7 @@ TEST_F(TransformLayerTest, Simple) {
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer->paint_bounds(),
layer_transform.mapRect(mock_layer->paint_bounds()));
EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds());
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer->needs_painting(paint_context()));
EXPECT_EQ(mock_layer->parent_matrix(),
@ -123,8 +126,10 @@ TEST_F(TransformLayerTest, Nested) {
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer2->paint_bounds(),
layer2_transform.mapRect(mock_layer->paint_bounds()));
EXPECT_EQ(layer2->child_paint_bounds(), mock_layer->paint_bounds());
EXPECT_EQ(layer1->paint_bounds(),
layer1_transform.mapRect(layer2->paint_bounds()));
EXPECT_EQ(layer1->child_paint_bounds(), layer2->paint_bounds());
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
EXPECT_TRUE(layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer1->needs_painting(paint_context()));
@ -177,14 +182,18 @@ TEST_F(TransformLayerTest, NestedSeparated) {
preroll_context()->cull_rect = cull_rect;
layer1->Preroll(preroll_context(), initial_transform);
SkRect expected_layer1_bounds = layer2->paint_bounds();
expected_layer1_bounds.join(mock_layer1->paint_bounds());
SkRect layer1_child_bounds = layer2->paint_bounds();
layer1_child_bounds.join(mock_layer1->paint_bounds());
SkRect expected_layer1_bounds = layer1_child_bounds;
layer1_transform.mapRect(&expected_layer1_bounds);
EXPECT_EQ(mock_layer2->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer2->paint_bounds(),
layer2_transform.mapRect(mock_layer2->paint_bounds()));
EXPECT_EQ(layer2->child_paint_bounds(), mock_layer2->paint_bounds());
EXPECT_EQ(mock_layer1->paint_bounds(), child_path.getBounds());
EXPECT_EQ(layer1->paint_bounds(), expected_layer1_bounds);
EXPECT_EQ(layer1->child_paint_bounds(), layer1_child_bounds);
EXPECT_TRUE(mock_layer2->needs_painting(paint_context()));
EXPECT_TRUE(layer2->needs_painting(paint_context()));
EXPECT_TRUE(mock_layer1->needs_painting(paint_context()));

View File

@ -7,6 +7,7 @@
#include <vector>
#include "flutter/common/constants.h"
#include "flutter/flow/layers/container_layer.h"
#include "flutter/flow/layers/layer.h"
#include "flutter/flow/paint_utils.h"
#include "flutter/fml/logging.h"
@ -176,25 +177,59 @@ std::unique_ptr<RasterCacheResult> RasterCache::RasterizeDisplayList(
void RasterCache::Prepare(PrerollContext* context,
Layer* layer,
const SkMatrix& ctm) {
RasterCacheKey cache_key(layer->unique_id(), RasterCacheKeyType::kLayer, ctm);
Entry& entry = cache_[cache_key];
const SkMatrix& ctm,
RasterCacheLayerStrategy strategy) {
auto cache_key_optional =
TryToMakeRasterCacheKeyForLayer(layer, strategy, ctm);
if (!cache_key_optional) {
return;
}
Entry& entry = cache_[cache_key_optional.value()];
entry.access_count++;
entry.used_this_frame = true;
if (!entry.image) {
entry.image = RasterizeLayer(context, layer, ctm, checkerboard_images_);
entry.image =
RasterizeLayer(context, layer, strategy, ctm, checkerboard_images_);
}
}
std::optional<RasterCacheKey> RasterCache::TryToMakeRasterCacheKeyForLayer(
const Layer* layer,
RasterCacheLayerStrategy strategy,
const SkMatrix& ctm) const {
switch (strategy) {
case RasterCacheLayerStrategy::kLayer:
return RasterCacheKey(layer->unique_id(), RasterCacheKeyType::kLayer,
ctm);
case RasterCacheLayerStrategy::kLayerChildren:
FML_DCHECK(layer->as_container_layer());
auto& children_layers = layer->as_container_layer()->layers();
auto children_count = children_layers.size();
if (children_count == 0) {
return std::nullopt;
}
std::vector<uint64_t> ids;
std::transform(children_layers.begin(), children_layers.end(),
std::back_inserter(ids), [](auto& layer) -> uint64_t {
return layer->unique_id();
});
return RasterCacheKey(RasterCacheKeyID(std::move(ids)),
RasterCacheKeyType::kLayerChildren, ctm);
}
}
std::unique_ptr<RasterCacheResult> RasterCache::RasterizeLayer(
PrerollContext* context,
Layer* layer,
RasterCacheLayerStrategy strategy,
const SkMatrix& ctm,
bool checkerboard) const {
const SkRect& paint_bounds = GetPaintBoundsFromLayer(layer, strategy);
return Rasterize(
context->gr_context, ctm, context->dst_color_space, checkerboard,
layer->paint_bounds(), "RasterCacheFlow::Layer",
[layer, context](SkCanvas* canvas) {
paint_bounds, "RasterCacheFlow::Layer",
[layer, context, strategy](SkCanvas* canvas) {
SkISize canvas_size = canvas->getBaseLayerSize();
SkNWayCanvas internal_nodes_canvas(canvas_size.width(),
canvas_size.height());
@ -212,12 +247,32 @@ std::unique_ptr<RasterCacheResult> RasterCache::RasterizeLayer(
context->has_platform_view ? nullptr : context->raster_cache,
context->checkerboard_offscreen_layers,
context->frame_device_pixel_ratio};
if (layer->needs_painting(paintContext)) {
layer->Paint(paintContext);
switch (strategy) {
case RasterCacheLayerStrategy::kLayer:
if (layer->needs_painting(paintContext)) {
layer->Paint(paintContext);
}
break;
case RasterCacheLayerStrategy::kLayerChildren:
FML_DCHECK(layer->as_container_layer());
layer->as_container_layer()->PaintChildren(paintContext);
break;
}
});
}
const SkRect& RasterCache::GetPaintBoundsFromLayer(
Layer* layer,
RasterCacheLayerStrategy strategy) const {
switch (strategy) {
case RasterCacheLayerStrategy::kLayer:
return layer->paint_bounds();
case RasterCacheLayerStrategy::kLayerChildren:
FML_DCHECK(layer->as_container_layer());
return layer->as_container_layer()->child_paint_bounds();
}
}
bool RasterCache::Prepare(PrerollContext* context,
SkPicture* picture,
bool is_complex,
@ -321,9 +376,15 @@ bool RasterCache::Prepare(PrerollContext* context,
return true;
}
void RasterCache::Touch(Layer* layer, const SkMatrix& ctm) {
RasterCacheKey cache_key(layer->unique_id(), RasterCacheKeyType::kLayer, ctm);
Touch(cache_key);
void RasterCache::Touch(Layer* layer,
const SkMatrix& ctm,
RasterCacheLayerStrategy strategey) {
auto cache_key_optional =
TryToMakeRasterCacheKeyForLayer(layer, strategey, ctm);
if (!cache_key_optional) {
return;
}
Touch(cache_key_optional.value());
}
void RasterCache::Touch(SkPicture* picture,
@ -368,10 +429,14 @@ bool RasterCache::Draw(const DisplayList& display_list,
bool RasterCache::Draw(const Layer* layer,
SkCanvas& canvas,
RasterCacheLayerStrategy strategy,
const SkPaint* paint) const {
RasterCacheKey cache_key(layer->unique_id(), RasterCacheKeyType::kLayer,
canvas.getTotalMatrix());
return Draw(cache_key, canvas, paint);
auto cache_key_optional =
TryToMakeRasterCacheKeyForLayer(layer, strategy, canvas.getTotalMatrix());
if (!cache_key_optional) {
return false;
}
return Draw(cache_key_optional.value(), canvas, paint);
}
bool RasterCache::Draw(const RasterCacheKey& cache_key,

View File

@ -21,6 +21,8 @@ class SkColorSpace;
namespace flutter {
enum class RasterCacheLayerStrategy { kLayer, kLayerChildren };
class RasterCacheResult {
public:
RasterCacheResult(sk_sp<SkImage> image,
@ -143,6 +145,7 @@ class RasterCache {
virtual std::unique_ptr<RasterCacheResult> RasterizeLayer(
PrerollContext* context,
Layer* layer,
RasterCacheLayerStrategy strategy,
const SkMatrix& ctm,
bool checkerboard) const;
@ -202,9 +205,16 @@ class RasterCache {
// away.
void Touch(SkPicture* picture, const SkMatrix& transformation_matrix);
void Touch(DisplayList* display_list, const SkMatrix& transformation_matrix);
void Touch(Layer* layer, const SkMatrix& ctm);
void Touch(
Layer* layer,
const SkMatrix& ctm,
RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer);
void Prepare(PrerollContext* context, Layer* layer, const SkMatrix& ctm);
void Prepare(
PrerollContext* context,
Layer* layer,
const SkMatrix& ctm,
RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer);
// Find the raster cache for the picture and draw it to the canvas.
//
@ -226,9 +236,11 @@ class RasterCache {
// (e.g., draw the raster cache with some opacity).
//
// Return true if the layer raster cache is found and drawn.
bool Draw(const Layer* layer,
SkCanvas& canvas,
const SkPaint* paint = nullptr) const;
bool Draw(
const Layer* layer,
SkCanvas& canvas,
RasterCacheLayerStrategy strategey = RasterCacheLayerStrategy::kLayer,
const SkPaint* paint = nullptr) const;
void PrepareNewFrame();
void CleanupAfterFrame();
@ -310,6 +322,15 @@ class RasterCache {
picture_and_display_list_cache_limit_per_frame_;
}
std::optional<RasterCacheKey> TryToMakeRasterCacheKeyForLayer(
const Layer* layer,
RasterCacheLayerStrategy strategy,
const SkMatrix& ctm) const;
const SkRect& GetPaintBoundsFromLayer(
Layer* layer,
RasterCacheLayerStrategy strategy) const;
const size_t access_threshold_;
const size_t picture_and_display_list_cache_limit_per_frame_;
size_t picture_cached_this_frame_ = 0;

View File

@ -6,6 +6,8 @@
#define FLUTTER_FLOW_RASTER_CACHE_KEY_H_
#include <unordered_map>
#include <utility>
#include <vector>
#include "flutter/fml/hash_combine.h"
#include "flutter/fml/logging.h"
@ -13,19 +15,55 @@
namespace flutter {
enum class RasterCacheKeyType { kLayer, kPicture, kDisplayList };
class RasterCacheKeyID {
public:
RasterCacheKeyID(const std::vector<uint64_t> ids) : ids_(ids) {}
const std::vector<uint64_t>& ids() { return ids_; }
std::size_t GetHash() const {
std::size_t seed = fml::HashCombine();
for (auto id : ids_) {
fml::HashCombineSeed(seed, id);
}
return seed;
}
bool operator==(const RasterCacheKeyID& other) const {
return ids_ == other.ids_;
}
bool operator!=(const RasterCacheKeyID& other) const {
return !operator==(other);
}
private:
const std::vector<uint64_t> ids_;
};
enum class RasterCacheKeyType {
kLayer,
kPicture,
kDisplayList,
kLayerChildren
};
enum class RasterCacheKeyKind { kLayerMetrics, kPictureMetrics };
class RasterCacheKey {
public:
RasterCacheKey(uint64_t id, RasterCacheKeyType type, const SkMatrix& ctm)
: id_(id), type_(type), matrix_(ctm) {
: RasterCacheKey(RasterCacheKeyID({id}), type, ctm) {}
RasterCacheKey(RasterCacheKeyID id,
RasterCacheKeyType type,
const SkMatrix& ctm)
: id_(std::move(id)), type_(type), matrix_(ctm) {
matrix_[SkMatrix::kMTransX] = 0;
matrix_[SkMatrix::kMTransY] = 0;
}
uint64_t id() const { return id_; }
const RasterCacheKeyID& id() const { return id_; }
RasterCacheKeyType type() const { return type_; }
const SkMatrix& matrix() const { return matrix_; }
@ -35,13 +73,14 @@ class RasterCacheKey {
case RasterCacheKeyType::kDisplayList:
return RasterCacheKeyKind::kPictureMetrics;
case RasterCacheKeyType::kLayer:
case RasterCacheKeyType::kLayerChildren:
return RasterCacheKeyKind::kLayerMetrics;
}
}
struct Hash {
std::size_t operator()(RasterCacheKey const& key) const {
return fml::HashCombine(key.id_, key.type_);
return fml::HashCombine(key.id_.GetHash(), key.type_);
}
};
@ -57,7 +96,7 @@ class RasterCacheKey {
using Map = std::unordered_map<RasterCacheKey, Value, Hash, Equal>;
private:
uint64_t id_;
RasterCacheKeyID id_;
RasterCacheKeyType type_;

View File

@ -555,17 +555,27 @@ TEST(RasterCache, RasterCacheKeyHashFunction) {
RasterCacheKey layer_key(id, RasterCacheKeyType::kLayer, matrix);
RasterCacheKey picture_key(id, RasterCacheKeyType::kPicture, matrix);
RasterCacheKey display_list_key(id, RasterCacheKeyType::kDisplayList, matrix);
RasterCacheKey layer_children_key(id, RasterCacheKeyType::kLayerChildren,
matrix);
auto raster_cache_key_id = RasterCacheKeyID({id});
auto layer_hash_code = hash_function(layer_key);
ASSERT_EQ(layer_hash_code, fml::HashCombine(id, RasterCacheKeyType::kLayer));
ASSERT_EQ(layer_hash_code, fml::HashCombine(raster_cache_key_id.GetHash(),
RasterCacheKeyType::kLayer));
auto picture_hash_code = hash_function(picture_key);
ASSERT_EQ(picture_hash_code,
fml::HashCombine(id, RasterCacheKeyType::kPicture));
ASSERT_EQ(picture_hash_code, fml::HashCombine(raster_cache_key_id.GetHash(),
RasterCacheKeyType::kPicture));
auto display_list_hash_code = hash_function(display_list_key);
ASSERT_EQ(display_list_hash_code,
fml::HashCombine(id, RasterCacheKeyType::kDisplayList));
fml::HashCombine(raster_cache_key_id.GetHash(),
RasterCacheKeyType::kDisplayList));
auto layer_children_hash_code = hash_function(layer_children_key);
ASSERT_EQ(layer_children_hash_code,
fml::HashCombine(raster_cache_key_id.GetHash(),
RasterCacheKeyType::kLayerChildren));
}
TEST(RasterCache, RasterCacheKeySameID) {
@ -575,13 +585,17 @@ TEST(RasterCache, RasterCacheKeySameID) {
RasterCacheKey layer_key(id, RasterCacheKeyType::kLayer, matrix);
RasterCacheKey picture_key(id, RasterCacheKeyType::kPicture, matrix);
RasterCacheKey display_list_key(id, RasterCacheKeyType::kDisplayList, matrix);
RasterCacheKey layer_children_key(id, RasterCacheKeyType::kLayerChildren,
matrix);
map[layer_key] = 100;
map[picture_key] = 200;
map[display_list_key] = 300;
map[layer_children_key] = 400;
ASSERT_EQ(map[layer_key], 100);
ASSERT_EQ(map[picture_key], 200);
ASSERT_EQ(map[display_list_key], 300);
ASSERT_EQ(map[layer_children_key], 400);
}
TEST(RasterCache, RasterCacheKeySameType) {
@ -620,6 +634,46 @@ TEST(RasterCache, RasterCacheKeySameType) {
ASSERT_EQ(map[display_list_first_key], 350);
ASSERT_EQ(map[display_list_second_key], 400);
ASSERT_EQ(map[display_list_third_key], 450);
type = RasterCacheKeyType::kLayerChildren;
RasterCacheKey layer_children_first_key(RasterCacheKeyID({1, 2, 3}), type,
matrix);
RasterCacheKey layer_children_second_key(RasterCacheKeyID({2, 3, 1}), type,
matrix);
RasterCacheKey layer_children_third_key(RasterCacheKeyID({3, 2, 1}), type,
matrix);
map[layer_children_first_key] = 100;
map[layer_children_second_key] = 200;
map[layer_children_third_key] = 300;
ASSERT_EQ(map[layer_children_first_key], 100);
ASSERT_EQ(map[layer_children_second_key], 200);
ASSERT_EQ(map[layer_children_third_key], 300);
}
TEST(RasterCache, RasterCacheKeyID_Equal) {
RasterCacheKeyID first = RasterCacheKeyID({1});
RasterCacheKeyID second = RasterCacheKeyID({1});
RasterCacheKeyID third = RasterCacheKeyID({2});
ASSERT_EQ(first, second);
ASSERT_NE(first, third);
RasterCacheKeyID fourth = RasterCacheKeyID({1, 2});
RasterCacheKeyID fifth = RasterCacheKeyID({1, 2});
RasterCacheKeyID sixth = RasterCacheKeyID({2, 1});
ASSERT_EQ(fourth, fifth);
ASSERT_NE(fourth, sixth);
}
TEST(RasterCache, RasterCacheKeyID_HashCode) {
uint64_t foo = 1;
uint64_t bar = 2;
RasterCacheKeyID first = RasterCacheKeyID({foo});
RasterCacheKeyID second = RasterCacheKeyID({foo, bar});
RasterCacheKeyID third = RasterCacheKeyID({bar, foo});
ASSERT_EQ(first.GetHash(), fml::HashCombine(foo));
ASSERT_EQ(second.GetHash(), fml::HashCombine(foo, bar));
ASSERT_EQ(third.GetHash(), fml::HashCombine(bar, foo));
}
} // namespace testing

View File

@ -28,6 +28,7 @@ std::unique_ptr<RasterCacheResult> MockRasterCache::RasterizePicture(
std::unique_ptr<RasterCacheResult> MockRasterCache::RasterizeLayer(
PrerollContext* context,
Layer* layer,
RasterCacheLayerStrategy strategy,
const SkMatrix& ctm,
bool checkerboard) const {
SkRect logical_rect = layer->paint_bounds();

View File

@ -66,6 +66,7 @@ class MockRasterCache : public RasterCache {
std::unique_ptr<RasterCacheResult> RasterizeLayer(
PrerollContext* context,
Layer* layer,
RasterCacheLayerStrategy stategy,
const SkMatrix& ctm,
bool checkerboard) const override;