Skip to content

Commit 8595361

Browse files
authored
Conditionally use offscreen root surface only when needed
Currently helps primarily on iOS when no BackdropFilter is present by lowering energy usage
1 parent 80d80ff commit 8595361

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+536
-72
lines changed

flow/compositor_context.cc

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ std::unique_ptr<CompositorContext::ScopedFrame> CompositorContext::AcquireFrame(
3636
ExternalViewEmbedder* view_embedder,
3737
const SkMatrix& root_surface_transformation,
3838
bool instrumentation_enabled,
39+
bool surface_supports_readback,
3940
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger) {
4041
return std::make_unique<ScopedFrame>(
4142
*this, gr_context, canvas, view_embedder, root_surface_transformation,
42-
instrumentation_enabled, gpu_thread_merger);
43+
instrumentation_enabled, surface_supports_readback, gpu_thread_merger);
4344
}
4445

4546
CompositorContext::ScopedFrame::ScopedFrame(
@@ -49,13 +50,15 @@ CompositorContext::ScopedFrame::ScopedFrame(
4950
ExternalViewEmbedder* view_embedder,
5051
const SkMatrix& root_surface_transformation,
5152
bool instrumentation_enabled,
53+
bool surface_supports_readback,
5254
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger)
5355
: context_(context),
5456
gr_context_(gr_context),
5557
canvas_(canvas),
5658
view_embedder_(view_embedder),
5759
root_surface_transformation_(root_surface_transformation),
5860
instrumentation_enabled_(instrumentation_enabled),
61+
surface_supports_readback_(surface_supports_readback),
5962
gpu_thread_merger_(gpu_thread_merger) {
6063
context_.BeginFrame(*this, instrumentation_enabled_);
6164
}
@@ -67,7 +70,8 @@ CompositorContext::ScopedFrame::~ScopedFrame() {
6770
RasterStatus CompositorContext::ScopedFrame::Raster(
6871
flutter::LayerTree& layer_tree,
6972
bool ignore_raster_cache) {
70-
layer_tree.Preroll(*this, ignore_raster_cache);
73+
bool root_needs_readback = layer_tree.Preroll(*this, ignore_raster_cache);
74+
bool needs_save_layer = root_needs_readback && !surface_supports_readback();
7175
PostPrerollResult post_preroll_result = PostPrerollResult::kSuccess;
7276
if (view_embedder_ && gpu_thread_merger_) {
7377
post_preroll_result = view_embedder_->PostPrerollAction(gpu_thread_merger_);
@@ -79,9 +83,19 @@ RasterStatus CompositorContext::ScopedFrame::Raster(
7983
// Clearing canvas after preroll reduces one render target switch when preroll
8084
// paints some raster cache.
8185
if (canvas()) {
86+
if (needs_save_layer) {
87+
FML_LOG(INFO) << "Using SaveLayer to protect non-readback surface";
88+
SkRect bounds = SkRect::Make(layer_tree.frame_size());
89+
SkPaint paint;
90+
paint.setBlendMode(SkBlendMode::kSrc);
91+
canvas()->saveLayer(&bounds, &paint);
92+
}
8293
canvas()->clear(SK_ColorTRANSPARENT);
8394
}
8495
layer_tree.Paint(*this, ignore_raster_cache);
96+
if (canvas() && needs_save_layer) {
97+
canvas()->restore();
98+
}
8599
return RasterStatus::kSuccess;
86100
}
87101

flow/compositor_context.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class CompositorContext {
4545
ExternalViewEmbedder* view_embedder,
4646
const SkMatrix& root_surface_transformation,
4747
bool instrumentation_enabled,
48+
bool surface_supports_readback,
4849
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger);
4950

5051
virtual ~ScopedFrame();
@@ -59,6 +60,8 @@ class CompositorContext {
5960
return root_surface_transformation_;
6061
}
6162

63+
bool surface_supports_readback() { return surface_supports_readback_; }
64+
6265
GrContext* gr_context() const { return gr_context_; }
6366

6467
virtual RasterStatus Raster(LayerTree& layer_tree,
@@ -71,6 +74,7 @@ class CompositorContext {
7174
ExternalViewEmbedder* view_embedder_;
7275
const SkMatrix& root_surface_transformation_;
7376
const bool instrumentation_enabled_;
77+
const bool surface_supports_readback_;
7478
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger_;
7579

7680
FML_DISALLOW_COPY_AND_ASSIGN(ScopedFrame);
@@ -86,6 +90,7 @@ class CompositorContext {
8690
ExternalViewEmbedder* view_embedder,
8791
const SkMatrix& root_surface_transformation,
8892
bool instrumentation_enabled,
93+
bool surface_supports_readback,
8994
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger);
9095

9196
void OnGrContextCreated();

flow/layers/backdrop_filter_layer.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ namespace flutter {
99
BackdropFilterLayer::BackdropFilterLayer(sk_sp<SkImageFilter> filter)
1010
: filter_(std::move(filter)) {}
1111

12+
void BackdropFilterLayer::Preroll(PrerollContext* context,
13+
const SkMatrix& matrix) {
14+
Layer::AutoPrerollSaveLayerState save =
15+
Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_));
16+
ContainerLayer::Preroll(context, matrix);
17+
}
18+
1219
void BackdropFilterLayer::Paint(PaintContext& context) const {
1320
TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint");
1421
FML_DCHECK(needs_painting());

flow/layers/backdrop_filter_layer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class BackdropFilterLayer : public ContainerLayer {
1515
public:
1616
BackdropFilterLayer(sk_sp<SkImageFilter> filter);
1717

18+
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
19+
1820
void Paint(PaintContext& context) const override;
1921

2022
private:

flow/layers/backdrop_filter_layer_unittests.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,36 @@ TEST_F(BackdropFilterLayerTest, Nested) {
182182
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
183183
}
184184

185+
TEST_F(BackdropFilterLayerTest, Readback) {
186+
sk_sp<SkImageFilter> no_filter;
187+
auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
188+
auto initial_transform = SkMatrix();
189+
190+
// BDF with filter always reads from surface
191+
auto layer1 = std::make_shared<BackdropFilterLayer>(layer_filter);
192+
preroll_context()->surface_needs_readback = false;
193+
layer1->Preroll(preroll_context(), initial_transform);
194+
EXPECT_TRUE(preroll_context()->surface_needs_readback);
195+
196+
// BDF with no filter does not read from surface itself
197+
auto layer2 = std::make_shared<BackdropFilterLayer>(no_filter);
198+
preroll_context()->surface_needs_readback = false;
199+
layer2->Preroll(preroll_context(), initial_transform);
200+
EXPECT_FALSE(preroll_context()->surface_needs_readback);
201+
202+
// BDF with no filter does not block prior readback value
203+
preroll_context()->surface_needs_readback = true;
204+
layer2->Preroll(preroll_context(), initial_transform);
205+
EXPECT_TRUE(preroll_context()->surface_needs_readback);
206+
207+
// BDF with no filter blocks child with readback
208+
auto mock_layer =
209+
std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true);
210+
layer2->Add(mock_layer);
211+
preroll_context()->surface_needs_readback = false;
212+
layer2->Preroll(preroll_context(), initial_transform);
213+
EXPECT_FALSE(preroll_context()->surface_needs_readback);
214+
}
215+
185216
} // namespace testing
186217
} // namespace flutter

flow/layers/clip_path_layer.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
2222
SkRect clip_path_bounds = clip_path_.getBounds();
2323
children_inside_clip_ = context->cull_rect.intersect(clip_path_bounds);
2424
if (children_inside_clip_) {
25+
Layer::AutoPrerollSaveLayerState save =
26+
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
2527
context->mutators_stack.PushClipPath(clip_path_);
2628
SkRect child_paint_bounds = SkRect::MakeEmpty();
2729
PrerollChildren(context, matrix, &child_paint_bounds);
@@ -57,11 +59,11 @@ void ClipPathLayer::Paint(PaintContext& context) const {
5759
context.internal_nodes_canvas->clipPath(clip_path_,
5860
clip_behavior_ != Clip::hardEdge);
5961

60-
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
62+
if (UsesSaveLayer()) {
6163
context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr);
6264
}
6365
PaintChildren(context);
64-
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
66+
if (UsesSaveLayer()) {
6567
context.internal_nodes_canvas->restore();
6668
}
6769
}

flow/layers/clip_path_layer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ class ClipPathLayer : public ContainerLayer {
1717

1818
void Paint(PaintContext& context) const override;
1919

20+
bool UsesSaveLayer() const {
21+
return clip_behavior_ == Clip::antiAliasWithSaveLayer;
22+
}
23+
2024
#if defined(OS_FUCHSIA)
2125
void UpdateScene(SceneUpdateContext& context) override;
2226
#endif // defined(OS_FUCHSIA)

flow/layers/clip_path_layer_unittests.cc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,5 +192,65 @@ TEST_F(ClipPathLayerTest, PartiallyContainedChild) {
192192
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
193193
}
194194

195+
static bool ReadbackResult(PrerollContext* context,
196+
Clip clip_behavior,
197+
std::shared_ptr<Layer> child,
198+
bool before) {
199+
const SkMatrix initial_matrix = SkMatrix();
200+
const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
201+
const SkPath layer_path = SkPath().addRect(layer_bounds);
202+
auto layer = std::make_shared<ClipPathLayer>(layer_path, clip_behavior);
203+
if (child != nullptr) {
204+
layer->Add(child);
205+
}
206+
context->surface_needs_readback = before;
207+
layer->Preroll(context, initial_matrix);
208+
return context->surface_needs_readback;
209+
}
210+
211+
TEST_F(ClipPathLayerTest, Readback) {
212+
PrerollContext* context = preroll_context();
213+
SkPath path;
214+
SkPaint paint;
215+
216+
const Clip hard = Clip::hardEdge;
217+
const Clip soft = Clip::antiAlias;
218+
const Clip save_layer = Clip::antiAliasWithSaveLayer;
219+
220+
std::shared_ptr<MockLayer> nochild;
221+
auto reader = std::make_shared<MockLayer>(path, paint, false, false, true);
222+
auto nonreader = std::make_shared<MockLayer>(path, paint);
223+
224+
// No children, no prior readback -> no readback after
225+
EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
226+
EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
227+
EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
228+
229+
// No children, prior readback -> readback after
230+
EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
231+
EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
232+
EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
233+
234+
// Non readback child, no prior readback -> no readback after
235+
EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
236+
EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
237+
EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
238+
239+
// Non readback child, prior readback -> readback after
240+
EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
241+
EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
242+
EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
243+
244+
// Readback child, no prior readback -> readback after unless SaveLayer
245+
EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
246+
EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
247+
EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
248+
249+
// Readback child, prior readback -> readback after
250+
EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
251+
EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
252+
EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
253+
}
254+
195255
} // namespace testing
196256
} // namespace flutter

flow/layers/clip_rect_layer.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
1515
SkRect previous_cull_rect = context->cull_rect;
1616
children_inside_clip_ = context->cull_rect.intersect(clip_rect_);
1717
if (children_inside_clip_) {
18+
Layer::AutoPrerollSaveLayerState save =
19+
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
1820
context->mutators_stack.PushClipRect(clip_rect_);
1921
SkRect child_paint_bounds = SkRect::MakeEmpty();
2022
PrerollChildren(context, matrix, &child_paint_bounds);
@@ -50,11 +52,11 @@ void ClipRectLayer::Paint(PaintContext& context) const {
5052
context.internal_nodes_canvas->clipRect(clip_rect_,
5153
clip_behavior_ != Clip::hardEdge);
5254

53-
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
55+
if (UsesSaveLayer()) {
5456
context.internal_nodes_canvas->saveLayer(clip_rect_, nullptr);
5557
}
5658
PaintChildren(context);
57-
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
59+
if (UsesSaveLayer()) {
5860
context.internal_nodes_canvas->restore();
5961
}
6062
}

flow/layers/clip_rect_layer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ class ClipRectLayer : public ContainerLayer {
1616
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
1717
void Paint(PaintContext& context) const override;
1818

19+
bool UsesSaveLayer() const {
20+
return clip_behavior_ == Clip::antiAliasWithSaveLayer;
21+
}
22+
1923
#if defined(OS_FUCHSIA)
2024
void UpdateScene(SceneUpdateContext& context) override;
2125
#endif // defined(OS_FUCHSIA)

0 commit comments

Comments
 (0)