mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Conditionally use offscreen root surface only when needed
Currently helps primarily on iOS when no BackdropFilter is present by lowering energy usage
This commit is contained in:
parent
80d80ff6e6
commit
85953615eb
@ -36,10 +36,11 @@ std::unique_ptr<CompositorContext::ScopedFrame> CompositorContext::AcquireFrame(
|
||||
ExternalViewEmbedder* view_embedder,
|
||||
const SkMatrix& root_surface_transformation,
|
||||
bool instrumentation_enabled,
|
||||
bool surface_supports_readback,
|
||||
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger) {
|
||||
return std::make_unique<ScopedFrame>(
|
||||
*this, gr_context, canvas, view_embedder, root_surface_transformation,
|
||||
instrumentation_enabled, gpu_thread_merger);
|
||||
instrumentation_enabled, surface_supports_readback, gpu_thread_merger);
|
||||
}
|
||||
|
||||
CompositorContext::ScopedFrame::ScopedFrame(
|
||||
@ -49,6 +50,7 @@ CompositorContext::ScopedFrame::ScopedFrame(
|
||||
ExternalViewEmbedder* view_embedder,
|
||||
const SkMatrix& root_surface_transformation,
|
||||
bool instrumentation_enabled,
|
||||
bool surface_supports_readback,
|
||||
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger)
|
||||
: context_(context),
|
||||
gr_context_(gr_context),
|
||||
@ -56,6 +58,7 @@ CompositorContext::ScopedFrame::ScopedFrame(
|
||||
view_embedder_(view_embedder),
|
||||
root_surface_transformation_(root_surface_transformation),
|
||||
instrumentation_enabled_(instrumentation_enabled),
|
||||
surface_supports_readback_(surface_supports_readback),
|
||||
gpu_thread_merger_(gpu_thread_merger) {
|
||||
context_.BeginFrame(*this, instrumentation_enabled_);
|
||||
}
|
||||
@ -67,7 +70,8 @@ CompositorContext::ScopedFrame::~ScopedFrame() {
|
||||
RasterStatus CompositorContext::ScopedFrame::Raster(
|
||||
flutter::LayerTree& layer_tree,
|
||||
bool ignore_raster_cache) {
|
||||
layer_tree.Preroll(*this, ignore_raster_cache);
|
||||
bool root_needs_readback = layer_tree.Preroll(*this, ignore_raster_cache);
|
||||
bool needs_save_layer = root_needs_readback && !surface_supports_readback();
|
||||
PostPrerollResult post_preroll_result = PostPrerollResult::kSuccess;
|
||||
if (view_embedder_ && gpu_thread_merger_) {
|
||||
post_preroll_result = view_embedder_->PostPrerollAction(gpu_thread_merger_);
|
||||
@ -79,9 +83,19 @@ RasterStatus CompositorContext::ScopedFrame::Raster(
|
||||
// Clearing canvas after preroll reduces one render target switch when preroll
|
||||
// paints some raster cache.
|
||||
if (canvas()) {
|
||||
if (needs_save_layer) {
|
||||
FML_LOG(INFO) << "Using SaveLayer to protect non-readback surface";
|
||||
SkRect bounds = SkRect::Make(layer_tree.frame_size());
|
||||
SkPaint paint;
|
||||
paint.setBlendMode(SkBlendMode::kSrc);
|
||||
canvas()->saveLayer(&bounds, &paint);
|
||||
}
|
||||
canvas()->clear(SK_ColorTRANSPARENT);
|
||||
}
|
||||
layer_tree.Paint(*this, ignore_raster_cache);
|
||||
if (canvas() && needs_save_layer) {
|
||||
canvas()->restore();
|
||||
}
|
||||
return RasterStatus::kSuccess;
|
||||
}
|
||||
|
||||
|
||||
@ -45,6 +45,7 @@ class CompositorContext {
|
||||
ExternalViewEmbedder* view_embedder,
|
||||
const SkMatrix& root_surface_transformation,
|
||||
bool instrumentation_enabled,
|
||||
bool surface_supports_readback,
|
||||
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger);
|
||||
|
||||
virtual ~ScopedFrame();
|
||||
@ -59,6 +60,8 @@ class CompositorContext {
|
||||
return root_surface_transformation_;
|
||||
}
|
||||
|
||||
bool surface_supports_readback() { return surface_supports_readback_; }
|
||||
|
||||
GrContext* gr_context() const { return gr_context_; }
|
||||
|
||||
virtual RasterStatus Raster(LayerTree& layer_tree,
|
||||
@ -71,6 +74,7 @@ class CompositorContext {
|
||||
ExternalViewEmbedder* view_embedder_;
|
||||
const SkMatrix& root_surface_transformation_;
|
||||
const bool instrumentation_enabled_;
|
||||
const bool surface_supports_readback_;
|
||||
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(ScopedFrame);
|
||||
@ -86,6 +90,7 @@ class CompositorContext {
|
||||
ExternalViewEmbedder* view_embedder,
|
||||
const SkMatrix& root_surface_transformation,
|
||||
bool instrumentation_enabled,
|
||||
bool surface_supports_readback,
|
||||
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger);
|
||||
|
||||
void OnGrContextCreated();
|
||||
|
||||
@ -9,6 +9,13 @@ namespace flutter {
|
||||
BackdropFilterLayer::BackdropFilterLayer(sk_sp<SkImageFilter> filter)
|
||||
: filter_(std::move(filter)) {}
|
||||
|
||||
void BackdropFilterLayer::Preroll(PrerollContext* context,
|
||||
const SkMatrix& matrix) {
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_));
|
||||
ContainerLayer::Preroll(context, matrix);
|
||||
}
|
||||
|
||||
void BackdropFilterLayer::Paint(PaintContext& context) const {
|
||||
TRACE_EVENT0("flutter", "BackdropFilterLayer::Paint");
|
||||
FML_DCHECK(needs_painting());
|
||||
|
||||
@ -15,6 +15,8 @@ class BackdropFilterLayer : public ContainerLayer {
|
||||
public:
|
||||
BackdropFilterLayer(sk_sp<SkImageFilter> filter);
|
||||
|
||||
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
|
||||
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
private:
|
||||
|
||||
@ -182,5 +182,36 @@ TEST_F(BackdropFilterLayerTest, Nested) {
|
||||
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
|
||||
}
|
||||
|
||||
TEST_F(BackdropFilterLayerTest, Readback) {
|
||||
sk_sp<SkImageFilter> no_filter;
|
||||
auto layer_filter = SkImageFilters::Paint(SkPaint(SkColors::kMagenta));
|
||||
auto initial_transform = SkMatrix();
|
||||
|
||||
// BDF with filter always reads from surface
|
||||
auto layer1 = std::make_shared<BackdropFilterLayer>(layer_filter);
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer1->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_TRUE(preroll_context()->surface_needs_readback);
|
||||
|
||||
// BDF with no filter does not read from surface itself
|
||||
auto layer2 = std::make_shared<BackdropFilterLayer>(no_filter);
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer2->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_FALSE(preroll_context()->surface_needs_readback);
|
||||
|
||||
// BDF with no filter does not block prior readback value
|
||||
preroll_context()->surface_needs_readback = true;
|
||||
layer2->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_TRUE(preroll_context()->surface_needs_readback);
|
||||
|
||||
// BDF with no filter blocks child with readback
|
||||
auto mock_layer =
|
||||
std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true);
|
||||
layer2->Add(mock_layer);
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer2->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_FALSE(preroll_context()->surface_needs_readback);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -22,6 +22,8 @@ void ClipPathLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
SkRect clip_path_bounds = clip_path_.getBounds();
|
||||
children_inside_clip_ = context->cull_rect.intersect(clip_path_bounds);
|
||||
if (children_inside_clip_) {
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
|
||||
context->mutators_stack.PushClipPath(clip_path_);
|
||||
SkRect child_paint_bounds = SkRect::MakeEmpty();
|
||||
PrerollChildren(context, matrix, &child_paint_bounds);
|
||||
@ -57,11 +59,11 @@ void ClipPathLayer::Paint(PaintContext& context) const {
|
||||
context.internal_nodes_canvas->clipPath(clip_path_,
|
||||
clip_behavior_ != Clip::hardEdge);
|
||||
|
||||
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
|
||||
if (UsesSaveLayer()) {
|
||||
context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr);
|
||||
}
|
||||
PaintChildren(context);
|
||||
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
|
||||
if (UsesSaveLayer()) {
|
||||
context.internal_nodes_canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,10 @@ class ClipPathLayer : public ContainerLayer {
|
||||
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
bool UsesSaveLayer() const {
|
||||
return clip_behavior_ == Clip::antiAliasWithSaveLayer;
|
||||
}
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
void UpdateScene(SceneUpdateContext& context) override;
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
@ -192,5 +192,65 @@ TEST_F(ClipPathLayerTest, PartiallyContainedChild) {
|
||||
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
|
||||
}
|
||||
|
||||
static bool ReadbackResult(PrerollContext* context,
|
||||
Clip clip_behavior,
|
||||
std::shared_ptr<Layer> child,
|
||||
bool before) {
|
||||
const SkMatrix initial_matrix = SkMatrix();
|
||||
const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
|
||||
const SkPath layer_path = SkPath().addRect(layer_bounds);
|
||||
auto layer = std::make_shared<ClipPathLayer>(layer_path, clip_behavior);
|
||||
if (child != nullptr) {
|
||||
layer->Add(child);
|
||||
}
|
||||
context->surface_needs_readback = before;
|
||||
layer->Preroll(context, initial_matrix);
|
||||
return context->surface_needs_readback;
|
||||
}
|
||||
|
||||
TEST_F(ClipPathLayerTest, Readback) {
|
||||
PrerollContext* context = preroll_context();
|
||||
SkPath path;
|
||||
SkPaint paint;
|
||||
|
||||
const Clip hard = Clip::hardEdge;
|
||||
const Clip soft = Clip::antiAlias;
|
||||
const Clip save_layer = Clip::antiAliasWithSaveLayer;
|
||||
|
||||
std::shared_ptr<MockLayer> nochild;
|
||||
auto reader = std::make_shared<MockLayer>(path, paint, false, false, true);
|
||||
auto nonreader = std::make_shared<MockLayer>(path, paint);
|
||||
|
||||
// No children, no prior readback -> no readback after
|
||||
EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
|
||||
|
||||
// No children, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
|
||||
|
||||
// Non readback child, no prior readback -> no readback after
|
||||
EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
|
||||
|
||||
// Non readback child, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
|
||||
|
||||
// Readback child, no prior readback -> readback after unless SaveLayer
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
|
||||
|
||||
// Readback child, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -15,6 +15,8 @@ void ClipRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
SkRect previous_cull_rect = context->cull_rect;
|
||||
children_inside_clip_ = context->cull_rect.intersect(clip_rect_);
|
||||
if (children_inside_clip_) {
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
|
||||
context->mutators_stack.PushClipRect(clip_rect_);
|
||||
SkRect child_paint_bounds = SkRect::MakeEmpty();
|
||||
PrerollChildren(context, matrix, &child_paint_bounds);
|
||||
@ -50,11 +52,11 @@ void ClipRectLayer::Paint(PaintContext& context) const {
|
||||
context.internal_nodes_canvas->clipRect(clip_rect_,
|
||||
clip_behavior_ != Clip::hardEdge);
|
||||
|
||||
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
|
||||
if (UsesSaveLayer()) {
|
||||
context.internal_nodes_canvas->saveLayer(clip_rect_, nullptr);
|
||||
}
|
||||
PaintChildren(context);
|
||||
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
|
||||
if (UsesSaveLayer()) {
|
||||
context.internal_nodes_canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,10 @@ class ClipRectLayer : public ContainerLayer {
|
||||
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
bool UsesSaveLayer() const {
|
||||
return clip_behavior_ == Clip::antiAliasWithSaveLayer;
|
||||
}
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
void UpdateScene(SceneUpdateContext& context) override;
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
@ -190,5 +190,64 @@ TEST_F(ClipRectLayerTest, PartiallyContainedChild) {
|
||||
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
|
||||
}
|
||||
|
||||
static bool ReadbackResult(PrerollContext* context,
|
||||
Clip clip_behavior,
|
||||
std::shared_ptr<Layer> child,
|
||||
bool before) {
|
||||
const SkMatrix initial_matrix = SkMatrix();
|
||||
const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
|
||||
auto layer = std::make_shared<ClipRectLayer>(layer_bounds, clip_behavior);
|
||||
if (child != nullptr) {
|
||||
layer->Add(child);
|
||||
}
|
||||
context->surface_needs_readback = before;
|
||||
layer->Preroll(context, initial_matrix);
|
||||
return context->surface_needs_readback;
|
||||
}
|
||||
|
||||
TEST_F(ClipRectLayerTest, Readback) {
|
||||
PrerollContext* context = preroll_context();
|
||||
SkPath path;
|
||||
SkPaint paint;
|
||||
|
||||
const Clip hard = Clip::hardEdge;
|
||||
const Clip soft = Clip::antiAlias;
|
||||
const Clip save_layer = Clip::antiAliasWithSaveLayer;
|
||||
|
||||
std::shared_ptr<MockLayer> nochild;
|
||||
auto reader = std::make_shared<MockLayer>(path, paint, false, false, true);
|
||||
auto nonreader = std::make_shared<MockLayer>(path, paint);
|
||||
|
||||
// No children, no prior readback -> no readback after
|
||||
EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
|
||||
|
||||
// No children, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
|
||||
|
||||
// Non readback child, no prior readback -> no readback after
|
||||
EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
|
||||
|
||||
// Non readback child, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
|
||||
|
||||
// Readback child, no prior readback -> readback after unless SaveLayer
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
|
||||
|
||||
// Readback child, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -16,6 +16,8 @@ void ClipRRectLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
SkRect clip_rrect_bounds = clip_rrect_.getBounds();
|
||||
children_inside_clip_ = context->cull_rect.intersect(clip_rrect_bounds);
|
||||
if (children_inside_clip_) {
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
|
||||
context->mutators_stack.PushClipRRect(clip_rrect_);
|
||||
SkRect child_paint_bounds = SkRect::MakeEmpty();
|
||||
PrerollChildren(context, matrix, &child_paint_bounds);
|
||||
@ -51,11 +53,11 @@ void ClipRRectLayer::Paint(PaintContext& context) const {
|
||||
context.internal_nodes_canvas->clipRRect(clip_rrect_,
|
||||
clip_behavior_ != Clip::hardEdge);
|
||||
|
||||
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
|
||||
if (UsesSaveLayer()) {
|
||||
context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr);
|
||||
}
|
||||
PaintChildren(context);
|
||||
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
|
||||
if (UsesSaveLayer()) {
|
||||
context.internal_nodes_canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,10 @@ class ClipRRectLayer : public ContainerLayer {
|
||||
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
bool UsesSaveLayer() const {
|
||||
return clip_behavior_ == Clip::antiAliasWithSaveLayer;
|
||||
}
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
void UpdateScene(SceneUpdateContext& context) override;
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
@ -195,5 +195,65 @@ TEST_F(ClipRRectLayerTest, PartiallyContainedChild) {
|
||||
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
|
||||
}
|
||||
|
||||
static bool ReadbackResult(PrerollContext* context,
|
||||
Clip clip_behavior,
|
||||
std::shared_ptr<Layer> child,
|
||||
bool before) {
|
||||
const SkMatrix initial_matrix = SkMatrix();
|
||||
const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
|
||||
const SkRRect layer_rrect = SkRRect::MakeRect(layer_bounds);
|
||||
auto layer = std::make_shared<ClipRRectLayer>(layer_rrect, clip_behavior);
|
||||
if (child != nullptr) {
|
||||
layer->Add(child);
|
||||
}
|
||||
context->surface_needs_readback = before;
|
||||
layer->Preroll(context, initial_matrix);
|
||||
return context->surface_needs_readback;
|
||||
}
|
||||
|
||||
TEST_F(ClipRRectLayerTest, Readback) {
|
||||
PrerollContext* context = preroll_context();
|
||||
SkPath path;
|
||||
SkPaint paint;
|
||||
|
||||
const Clip hard = Clip::hardEdge;
|
||||
const Clip soft = Clip::antiAlias;
|
||||
const Clip save_layer = Clip::antiAliasWithSaveLayer;
|
||||
|
||||
std::shared_ptr<MockLayer> nochild;
|
||||
auto reader = std::make_shared<MockLayer>(path, paint, false, false, true);
|
||||
auto nonreader = std::make_shared<MockLayer>(path, paint);
|
||||
|
||||
// No children, no prior readback -> no readback after
|
||||
EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
|
||||
|
||||
// No children, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
|
||||
|
||||
// Non readback child, no prior readback -> no readback after
|
||||
EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
|
||||
|
||||
// Non readback child, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
|
||||
|
||||
// Readback child, no prior readback -> readback after unless SaveLayer
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
|
||||
|
||||
// Readback child, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -9,6 +9,13 @@ namespace flutter {
|
||||
ColorFilterLayer::ColorFilterLayer(sk_sp<SkColorFilter> filter)
|
||||
: filter_(std::move(filter)) {}
|
||||
|
||||
void ColorFilterLayer::Preroll(PrerollContext* context,
|
||||
const SkMatrix& matrix) {
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context);
|
||||
ContainerLayer::Preroll(context, matrix);
|
||||
}
|
||||
|
||||
void ColorFilterLayer::Paint(PaintContext& context) const {
|
||||
TRACE_EVENT0("flutter", "ColorFilterLayer::Paint");
|
||||
FML_DCHECK(needs_painting());
|
||||
|
||||
@ -15,6 +15,8 @@ class ColorFilterLayer : public ContainerLayer {
|
||||
public:
|
||||
ColorFilterLayer(sk_sp<SkColorFilter> filter);
|
||||
|
||||
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
|
||||
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
private:
|
||||
|
||||
@ -195,5 +195,24 @@ TEST_F(ColorFilterLayerTest, Nested) {
|
||||
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
|
||||
}
|
||||
|
||||
TEST_F(ColorFilterLayerTest, Readback) {
|
||||
auto layer_filter = SkColorFilters::LinearToSRGBGamma();
|
||||
auto initial_transform = SkMatrix();
|
||||
|
||||
// ColorFilterLayer does not read from surface
|
||||
auto layer = std::make_shared<ColorFilterLayer>(layer_filter);
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_FALSE(preroll_context()->surface_needs_readback);
|
||||
|
||||
// ColorFilterLayer blocks child with readback
|
||||
auto mock_layer =
|
||||
std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true);
|
||||
layer->Add(mock_layer);
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_FALSE(preroll_context()->surface_needs_readback);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -27,6 +27,34 @@ uint64_t Layer::NextUniqueID() {
|
||||
|
||||
void Layer::Preroll(PrerollContext* context, const SkMatrix& matrix) {}
|
||||
|
||||
Layer::AutoPrerollSaveLayerState::AutoPrerollSaveLayerState(
|
||||
PrerollContext* preroll_context,
|
||||
bool save_layer_is_active,
|
||||
bool layer_itself_performs_readback)
|
||||
: preroll_context_(preroll_context),
|
||||
save_layer_is_active_(save_layer_is_active),
|
||||
layer_itself_performs_readback_(layer_itself_performs_readback) {
|
||||
if (save_layer_is_active_) {
|
||||
prev_surface_needs_readback_ = preroll_context_->surface_needs_readback;
|
||||
preroll_context_->surface_needs_readback = false;
|
||||
}
|
||||
}
|
||||
|
||||
Layer::AutoPrerollSaveLayerState Layer::AutoPrerollSaveLayerState::Create(
|
||||
PrerollContext* preroll_context,
|
||||
bool save_layer_is_active,
|
||||
bool layer_itself_performs_readback) {
|
||||
return Layer::AutoPrerollSaveLayerState(preroll_context, save_layer_is_active,
|
||||
layer_itself_performs_readback);
|
||||
}
|
||||
|
||||
Layer::AutoPrerollSaveLayerState::~AutoPrerollSaveLayerState() {
|
||||
if (save_layer_is_active_) {
|
||||
preroll_context_->surface_needs_readback =
|
||||
(prev_surface_needs_readback_ || layer_itself_performs_readback_);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
void Layer::UpdateScene(SceneUpdateContext& context) {}
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
@ -49,6 +49,7 @@ struct PrerollContext {
|
||||
MutatorsStack& mutators_stack;
|
||||
SkColorSpace* dst_color_space;
|
||||
SkRect cull_rect;
|
||||
bool surface_needs_readback;
|
||||
|
||||
// These allow us to paint in the end of subtree Preroll.
|
||||
const Stopwatch& raster_time;
|
||||
@ -76,6 +77,32 @@ class Layer {
|
||||
|
||||
virtual void Preroll(PrerollContext* context, const SkMatrix& matrix);
|
||||
|
||||
// Used during Preroll by layers that employ a saveLayer to manage the
|
||||
// PrerollContext settings with values affected by the saveLayer mechanism.
|
||||
// This object must be created before calling Preroll on the children to
|
||||
// set up the state for the children and then restore the state upon
|
||||
// destruction.
|
||||
class AutoPrerollSaveLayerState {
|
||||
public:
|
||||
FML_WARN_UNUSED_RESULT static AutoPrerollSaveLayerState Create(
|
||||
PrerollContext* preroll_context,
|
||||
bool save_layer_is_active = true,
|
||||
bool layer_itself_performs_readback = false);
|
||||
|
||||
~AutoPrerollSaveLayerState();
|
||||
|
||||
private:
|
||||
AutoPrerollSaveLayerState(PrerollContext* preroll_context,
|
||||
bool save_layer_is_active,
|
||||
bool layer_itself_performs_readback);
|
||||
|
||||
PrerollContext* preroll_context_;
|
||||
bool save_layer_is_active_;
|
||||
bool layer_itself_performs_readback_;
|
||||
|
||||
bool prev_surface_needs_readback_;
|
||||
};
|
||||
|
||||
struct PaintContext {
|
||||
// When splitting the scene into multiple canvases (e.g when embedding
|
||||
// a platform view on iOS) during the paint traversal we apply the non leaf
|
||||
|
||||
@ -26,13 +26,13 @@ void LayerTree::RecordBuildTime(fml::TimePoint start) {
|
||||
build_finish_ = fml::TimePoint::Now();
|
||||
}
|
||||
|
||||
void LayerTree::Preroll(CompositorContext::ScopedFrame& frame,
|
||||
bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame,
|
||||
bool ignore_raster_cache) {
|
||||
TRACE_EVENT0("flutter", "LayerTree::Preroll");
|
||||
|
||||
if (!root_layer_) {
|
||||
FML_LOG(ERROR) << "The scene did not specify any layers.";
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
SkColorSpace* color_space =
|
||||
@ -47,6 +47,7 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame,
|
||||
stack,
|
||||
color_space,
|
||||
kGiantRect,
|
||||
false,
|
||||
frame.context().raster_time(),
|
||||
frame.context().ui_time(),
|
||||
frame.context().texture_registry(),
|
||||
@ -55,6 +56,7 @@ void LayerTree::Preroll(CompositorContext::ScopedFrame& frame,
|
||||
frame_device_pixel_ratio_};
|
||||
|
||||
root_layer_->Preroll(&context, frame.root_surface_transformation());
|
||||
return context.surface_needs_readback;
|
||||
}
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
@ -152,6 +154,7 @@ sk_sp<SkPicture> LayerTree::Flatten(const SkRect& bounds) {
|
||||
unused_stack, // mutator stack
|
||||
nullptr, // SkColorSpace* dst_color_space
|
||||
kGiantRect, // SkRect cull_rect
|
||||
false, // layer reads from surface
|
||||
unused_stopwatch, // frame time (dont care)
|
||||
unused_stopwatch, // engine time (dont care)
|
||||
unused_texture_registry, // texture registry (not supported)
|
||||
|
||||
@ -23,7 +23,14 @@ class LayerTree {
|
||||
float frame_physical_depth,
|
||||
float frame_device_pixel_ratio);
|
||||
|
||||
void Preroll(CompositorContext::ScopedFrame& frame,
|
||||
// Perform a preroll pass on the tree and return information about
|
||||
// the tree that affects rendering this frame.
|
||||
//
|
||||
// Returns:
|
||||
// - a boolean indicating whether or not the top level of the
|
||||
// layer tree performs any operations that require readback
|
||||
// from the root surface.
|
||||
bool Preroll(CompositorContext::ScopedFrame& frame,
|
||||
bool ignore_raster_cache = false);
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
|
||||
@ -26,6 +26,7 @@ class LayerTreeTest : public CanvasTest {
|
||||
nullptr,
|
||||
root_transform_,
|
||||
false,
|
||||
true,
|
||||
nullptr)) {}
|
||||
|
||||
LayerTree& layer_tree() { return layer_tree_; }
|
||||
|
||||
@ -58,6 +58,8 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
child_matrix.postTranslate(offset_.fX, offset_.fY);
|
||||
context->mutators_stack.PushTransform(
|
||||
SkMatrix::MakeTrans(offset_.fX, offset_.fY));
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context);
|
||||
OpacityLayerBase::Preroll(context, child_matrix);
|
||||
context->mutators_stack.Pop();
|
||||
|
||||
|
||||
@ -308,5 +308,24 @@ TEST_F(OpacityLayerTest, Nested) {
|
||||
EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls);
|
||||
}
|
||||
|
||||
TEST_F(OpacityLayerTest, Readback) {
|
||||
auto initial_transform = SkMatrix();
|
||||
auto layer = std::make_shared<OpacityLayer>(kOpaque_SkAlphaType, SkPoint());
|
||||
layer->Add(std::make_shared<MockLayer>(SkPath()));
|
||||
|
||||
// OpacityLayer does not read from surface
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_FALSE(preroll_context()->surface_needs_readback);
|
||||
|
||||
// OpacityLayer blocks child with readback
|
||||
auto mock_layer =
|
||||
std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true);
|
||||
layer->Add(mock_layer);
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_FALSE(preroll_context()->surface_needs_readback);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -49,6 +49,8 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context,
|
||||
const SkMatrix& matrix) {
|
||||
TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll");
|
||||
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context, UsesSaveLayer());
|
||||
PhysicalShapeLayerBase::Preroll(context, matrix);
|
||||
|
||||
if (elevation() == 0) {
|
||||
@ -100,7 +102,7 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const {
|
||||
break;
|
||||
}
|
||||
|
||||
if (clip_behavior_ == Clip::antiAliasWithSaveLayer) {
|
||||
if (UsesSaveLayer()) {
|
||||
// If we want to avoid the bleeding edge artifact
|
||||
// (https://github.com/flutter/flutter/issues/18057#issue-328003931)
|
||||
// using saveLayer, we have to call drawPaint instead of drawPath as
|
||||
|
||||
@ -46,6 +46,10 @@ class PhysicalShapeLayer : public PhysicalShapeLayerBase {
|
||||
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
bool UsesSaveLayer() const {
|
||||
return clip_behavior_ == Clip::antiAliasWithSaveLayer;
|
||||
}
|
||||
|
||||
private:
|
||||
SkColor shadow_color_;
|
||||
SkPath path_;
|
||||
|
||||
@ -202,5 +202,68 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) {
|
||||
0, MockCanvas::DrawPathData{layer_path, layer_paint}}}));
|
||||
}
|
||||
|
||||
static bool ReadbackResult(PrerollContext* context,
|
||||
Clip clip_behavior,
|
||||
std::shared_ptr<Layer> child,
|
||||
bool before) {
|
||||
const SkMatrix initial_matrix = SkMatrix();
|
||||
const SkRect layer_bounds = SkRect::MakeXYWH(0.5, 1.0, 5.0, 6.0);
|
||||
const SkPath layer_path = SkPath().addRect(layer_bounds);
|
||||
auto layer =
|
||||
std::make_shared<PhysicalShapeLayer>(SK_ColorGREEN, SK_ColorBLACK,
|
||||
0.0f, // elevation
|
||||
layer_path, clip_behavior);
|
||||
if (child != nullptr) {
|
||||
layer->Add(child);
|
||||
}
|
||||
context->surface_needs_readback = before;
|
||||
layer->Preroll(context, initial_matrix);
|
||||
return context->surface_needs_readback;
|
||||
}
|
||||
|
||||
TEST_F(PhysicalShapeLayerTest, Readback) {
|
||||
PrerollContext* context = preroll_context();
|
||||
SkPath path;
|
||||
SkPaint paint;
|
||||
|
||||
const Clip hard = Clip::hardEdge;
|
||||
const Clip soft = Clip::antiAlias;
|
||||
const Clip save_layer = Clip::antiAliasWithSaveLayer;
|
||||
|
||||
std::shared_ptr<MockLayer> nochild;
|
||||
auto reader = std::make_shared<MockLayer>(path, paint, false, false, true);
|
||||
auto nonreader = std::make_shared<MockLayer>(path, paint);
|
||||
|
||||
// No children, no prior readback -> no readback after
|
||||
EXPECT_FALSE(ReadbackResult(context, hard, nochild, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, soft, nochild, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, nochild, false));
|
||||
|
||||
// No children, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, nochild, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, nochild, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, nochild, true));
|
||||
|
||||
// Non readback child, no prior readback -> no readback after
|
||||
EXPECT_FALSE(ReadbackResult(context, hard, nonreader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, soft, nonreader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, nonreader, false));
|
||||
|
||||
// Non readback child, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, nonreader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, nonreader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, nonreader, true));
|
||||
|
||||
// Readback child, no prior readback -> readback after unless SaveLayer
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, reader, false));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, reader, false));
|
||||
EXPECT_FALSE(ReadbackResult(context, save_layer, reader, false));
|
||||
|
||||
// Readback child, prior readback -> readback after
|
||||
EXPECT_TRUE(ReadbackResult(context, hard, reader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, soft, reader, true));
|
||||
EXPECT_TRUE(ReadbackResult(context, save_layer, reader, true));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -11,6 +11,12 @@ ShaderMaskLayer::ShaderMaskLayer(sk_sp<SkShader> shader,
|
||||
SkBlendMode blend_mode)
|
||||
: shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) {}
|
||||
|
||||
void ShaderMaskLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
Layer::AutoPrerollSaveLayerState save =
|
||||
Layer::AutoPrerollSaveLayerState::Create(context);
|
||||
ContainerLayer::Preroll(context, matrix);
|
||||
}
|
||||
|
||||
void ShaderMaskLayer::Paint(PaintContext& context) const {
|
||||
TRACE_EVENT0("flutter", "ShaderMaskLayer::Paint");
|
||||
FML_DCHECK(needs_painting());
|
||||
|
||||
@ -17,6 +17,8 @@ class ShaderMaskLayer : public ContainerLayer {
|
||||
const SkRect& mask_rect,
|
||||
SkBlendMode blend_mode);
|
||||
|
||||
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
|
||||
|
||||
void Paint(PaintContext& context) const override;
|
||||
|
||||
private:
|
||||
|
||||
@ -253,5 +253,27 @@ TEST_F(ShaderMaskLayerTest, Nested) {
|
||||
MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}));
|
||||
}
|
||||
|
||||
TEST_F(ShaderMaskLayerTest, Readback) {
|
||||
auto initial_transform = SkMatrix();
|
||||
const SkRect layer_bounds = SkRect::MakeLTRB(2.0f, 4.0f, 20.5f, 20.5f);
|
||||
auto layer_filter =
|
||||
SkPerlinNoiseShader::MakeImprovedNoise(1.0f, 1.0f, 1, 1.0f);
|
||||
auto layer = std::make_shared<ShaderMaskLayer>(layer_filter, layer_bounds,
|
||||
SkBlendMode::kSrc);
|
||||
|
||||
// ShaderMaskLayer does not read from surface
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_FALSE(preroll_context()->surface_needs_readback);
|
||||
|
||||
// ShaderMaskLayer blocks child with readback
|
||||
auto mock_layer =
|
||||
std::make_shared<MockLayer>(SkPath(), SkPaint(), false, false, true);
|
||||
layer->Add(mock_layer);
|
||||
preroll_context()->surface_needs_readback = false;
|
||||
layer->Preroll(preroll_context(), initial_transform);
|
||||
EXPECT_FALSE(preroll_context()->surface_needs_readback);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace flutter
|
||||
|
||||
@ -37,6 +37,7 @@ class LayerTestBase : public CanvasTestBase<BaseT> {
|
||||
nullptr, /* external_view_embedder */
|
||||
mutators_stack_, TestT::mock_canvas().imageInfo().colorSpace(),
|
||||
kGiantRect, /* cull_rect */
|
||||
false, /* layer reads from surface */
|
||||
raster_time_, ui_time_, texture_registry_,
|
||||
false, /* checkerboard_offscreen_layers */
|
||||
100.0f, /* frame_physical_depth */
|
||||
|
||||
@ -10,11 +10,13 @@ namespace testing {
|
||||
MockLayer::MockLayer(SkPath path,
|
||||
SkPaint paint,
|
||||
bool fake_has_platform_view,
|
||||
bool fake_needs_system_composite)
|
||||
bool fake_needs_system_composite,
|
||||
bool fake_reads_surface)
|
||||
: fake_paint_path_(path),
|
||||
fake_paint_(paint),
|
||||
fake_has_platform_view_(fake_has_platform_view),
|
||||
fake_needs_system_composite_(fake_needs_system_composite) {}
|
||||
fake_needs_system_composite_(fake_needs_system_composite),
|
||||
fake_reads_surface_(fake_reads_surface) {}
|
||||
|
||||
void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
parent_mutators_ = context->mutators_stack;
|
||||
@ -26,6 +28,9 @@ void MockLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
|
||||
context->has_platform_view = fake_has_platform_view_;
|
||||
set_paint_bounds(fake_paint_path_.getBounds());
|
||||
set_needs_system_composite(fake_needs_system_composite_);
|
||||
if (fake_reads_surface_) {
|
||||
context->surface_needs_readback = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MockLayer::Paint(PaintContext& context) const {
|
||||
|
||||
@ -19,7 +19,8 @@ class MockLayer : public Layer {
|
||||
MockLayer(SkPath path,
|
||||
SkPaint paint = SkPaint(),
|
||||
bool fake_has_platform_view = false,
|
||||
bool fake_needs_system_composite = false);
|
||||
bool fake_needs_system_composite = false,
|
||||
bool fake_reads_surface = false);
|
||||
|
||||
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
|
||||
void Paint(PaintContext& context) const override;
|
||||
@ -40,6 +41,7 @@ class MockLayer : public Layer {
|
||||
bool parent_has_platform_view_ = false;
|
||||
bool fake_has_platform_view_ = false;
|
||||
bool fake_needs_system_composite_ = false;
|
||||
bool fake_reads_surface_ = false;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(MockLayer);
|
||||
};
|
||||
|
||||
@ -333,6 +333,7 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) {
|
||||
external_view_embedder, // external view embedder
|
||||
root_surface_transformation, // root surface transformation
|
||||
true, // instrumentation enabled
|
||||
frame->supports_readback(), // surface supports pixel reads
|
||||
gpu_thread_merger_ // thread merger
|
||||
);
|
||||
|
||||
@ -377,7 +378,7 @@ static sk_sp<SkData> ScreenshotLayerTreeAsPicture(
|
||||
// https://github.com/flutter/flutter/issues/23435
|
||||
auto frame = compositor_context.AcquireFrame(
|
||||
nullptr, recorder.getRecordingCanvas(), nullptr,
|
||||
root_surface_transformation, false, nullptr);
|
||||
root_surface_transformation, false, true, nullptr);
|
||||
|
||||
frame->Raster(*tree, true);
|
||||
|
||||
@ -434,7 +435,7 @@ static sk_sp<SkData> ScreenshotLayerTreeAsImage(
|
||||
// surface if we don't call the base method.
|
||||
auto frame = compositor_context.flutter::CompositorContext::AcquireFrame(
|
||||
surface_context, canvas, nullptr, root_surface_transformation, false,
|
||||
nullptr);
|
||||
true, nullptr);
|
||||
canvas->clear(SK_ColorTRANSPARENT);
|
||||
frame->Raster(*tree, true);
|
||||
canvas->flush();
|
||||
|
||||
@ -10,8 +10,12 @@
|
||||
namespace flutter {
|
||||
|
||||
SurfaceFrame::SurfaceFrame(sk_sp<SkSurface> surface,
|
||||
bool supports_readback,
|
||||
const SubmitCallback& submit_callback)
|
||||
: submitted_(false), surface_(surface), submit_callback_(submit_callback) {
|
||||
: submitted_(false),
|
||||
surface_(surface),
|
||||
supports_readback_(supports_readback),
|
||||
submit_callback_(submit_callback) {
|
||||
FML_DCHECK(submit_callback_);
|
||||
}
|
||||
|
||||
|
||||
@ -21,7 +21,9 @@ class SurfaceFrame {
|
||||
using SubmitCallback =
|
||||
std::function<bool(const SurfaceFrame& surface_frame, SkCanvas* canvas)>;
|
||||
|
||||
SurfaceFrame(sk_sp<SkSurface> surface, const SubmitCallback& submit_callback);
|
||||
SurfaceFrame(sk_sp<SkSurface> surface,
|
||||
bool supports_readback,
|
||||
const SubmitCallback& submit_callback);
|
||||
|
||||
~SurfaceFrame();
|
||||
|
||||
@ -31,9 +33,12 @@ class SurfaceFrame {
|
||||
|
||||
sk_sp<SkSurface> SkiaSurface() const;
|
||||
|
||||
bool supports_readback() { return supports_readback_; }
|
||||
|
||||
private:
|
||||
bool submitted_;
|
||||
sk_sp<SkSurface> surface_;
|
||||
bool supports_readback_;
|
||||
SubmitCallback submit_callback_;
|
||||
|
||||
bool PerformSubmit();
|
||||
|
||||
@ -180,19 +180,6 @@ static sk_sp<SkSurface> WrapOnscreenSurface(GrContext* context,
|
||||
);
|
||||
}
|
||||
|
||||
static sk_sp<SkSurface> CreateOffscreenSurface(GrContext* context,
|
||||
const SkISize& size) {
|
||||
const SkImageInfo image_info = SkImageInfo::MakeN32(
|
||||
size.fWidth, size.fHeight, kOpaque_SkAlphaType, SkColorSpace::MakeSRGB());
|
||||
|
||||
const SkSurfaceProps surface_props(
|
||||
SkSurfaceProps::InitType::kLegacyFontHost_InitType);
|
||||
|
||||
return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, image_info, 0,
|
||||
kBottomLeft_GrSurfaceOrigin,
|
||||
&surface_props);
|
||||
}
|
||||
|
||||
bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
|
||||
if (onscreen_surface_ != nullptr &&
|
||||
size == SkISize::Make(onscreen_surface_->width(),
|
||||
@ -206,14 +193,13 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
|
||||
|
||||
// Either way, we need to get rid of previous surface.
|
||||
onscreen_surface_ = nullptr;
|
||||
offscreen_surface_ = nullptr;
|
||||
|
||||
if (size.isEmpty()) {
|
||||
FML_LOG(ERROR) << "Cannot create surfaces of empty size.";
|
||||
return false;
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> onscreen_surface, offscreen_surface;
|
||||
sk_sp<SkSurface> onscreen_surface;
|
||||
|
||||
onscreen_surface =
|
||||
WrapOnscreenSurface(context_.get(), // GL context
|
||||
@ -228,16 +214,7 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delegate_->UseOffscreenSurface()) {
|
||||
offscreen_surface = CreateOffscreenSurface(context_.get(), size);
|
||||
if (offscreen_surface == nullptr) {
|
||||
FML_LOG(ERROR) << "Could not create offscreen surface.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
onscreen_surface_ = std::move(onscreen_surface);
|
||||
offscreen_surface_ = std::move(offscreen_surface);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -263,7 +240,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
|
||||
// external view embedder may want to render to the root surface.
|
||||
if (!render_to_surface_) {
|
||||
return std::make_unique<SurfaceFrame>(
|
||||
nullptr, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
|
||||
nullptr, true, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -285,7 +262,8 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
|
||||
return weak ? weak->PresentSurface(canvas) : false;
|
||||
};
|
||||
|
||||
return std::make_unique<SurfaceFrame>(surface, submit_callback);
|
||||
return std::make_unique<SurfaceFrame>(
|
||||
surface, delegate_->SurfaceSupportsReadback(), submit_callback);
|
||||
}
|
||||
|
||||
bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
|
||||
@ -293,15 +271,6 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offscreen_surface_ != nullptr) {
|
||||
TRACE_EVENT0("flutter", "CopyTextureOnscreen");
|
||||
SkPaint paint;
|
||||
SkCanvas* onscreen_canvas = onscreen_surface_->getCanvas();
|
||||
onscreen_canvas->clear(SK_ColorTRANSPARENT);
|
||||
onscreen_canvas->drawImage(offscreen_surface_->makeImageSnapshot(), 0, 0,
|
||||
&paint);
|
||||
}
|
||||
|
||||
{
|
||||
TRACE_EVENT0("flutter", "SkCanvas::Flush");
|
||||
onscreen_surface_->getCanvas()->flush();
|
||||
@ -346,7 +315,7 @@ sk_sp<SkSurface> GPUSurfaceGL::AcquireRenderSurface(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return offscreen_surface_ != nullptr ? offscreen_surface_ : onscreen_surface_;
|
||||
return onscreen_surface_;
|
||||
}
|
||||
|
||||
// |Surface|
|
||||
|
||||
@ -50,7 +50,6 @@ class GPUSurfaceGL : public Surface {
|
||||
GPUSurfaceGLDelegate* delegate_;
|
||||
sk_sp<GrContext> context_;
|
||||
sk_sp<SkSurface> onscreen_surface_;
|
||||
sk_sp<SkSurface> offscreen_surface_;
|
||||
bool context_owner_;
|
||||
// TODO(38466): Refactor GPU surface APIs take into account the fact that an
|
||||
// external view embedder may want to render to the root surface. This is a
|
||||
|
||||
@ -12,8 +12,8 @@ bool GPUSurfaceGLDelegate::GLContextFBOResetAfterPresent() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GPUSurfaceGLDelegate::UseOffscreenSurface() const {
|
||||
return false;
|
||||
bool GPUSurfaceGLDelegate::SurfaceSupportsReadback() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
SkMatrix GPUSurfaceGLDelegate::GLContextSurfaceTransformation() const {
|
||||
|
||||
@ -36,8 +36,9 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate {
|
||||
// subsequent frames.
|
||||
virtual bool GLContextFBOResetAfterPresent() const;
|
||||
|
||||
// Create an offscreen surface to render into before onscreen composition.
|
||||
virtual bool UseOffscreenSurface() const;
|
||||
// Indicates whether or not the surface supports pixel readback as used in
|
||||
// circumstances such as a BackdropFilter.
|
||||
virtual bool SurfaceSupportsReadback() const;
|
||||
|
||||
// A transformation applied to the onscreen surface before the canvas is
|
||||
// flushed.
|
||||
|
||||
@ -152,7 +152,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceMetal::AcquireFrame(const SkISize& size)
|
||||
return true;
|
||||
};
|
||||
|
||||
return std::make_unique<SurfaceFrame>(std::move(surface), submit_callback);
|
||||
return std::make_unique<SurfaceFrame>(std::move(surface), true, submit_callback);
|
||||
}
|
||||
|
||||
// |Surface|
|
||||
|
||||
@ -29,7 +29,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceSoftware::AcquireFrame(
|
||||
// external view embedder may want to render to the root surface.
|
||||
if (!render_to_surface_) {
|
||||
return std::make_unique<SurfaceFrame>(
|
||||
nullptr, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
|
||||
nullptr, true, [](const SurfaceFrame& surface_frame, SkCanvas* canvas) {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -69,7 +69,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceSoftware::AcquireFrame(
|
||||
return self->delegate_->PresentBackingStore(surface_frame.SkiaSurface());
|
||||
};
|
||||
|
||||
return std::make_unique<SurfaceFrame>(backing_store, on_submit);
|
||||
return std::make_unique<SurfaceFrame>(backing_store, true, on_submit);
|
||||
}
|
||||
|
||||
// |Surface|
|
||||
|
||||
@ -40,7 +40,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceVulkan::AcquireFrame(
|
||||
}
|
||||
return weak_this->window_.SwapBuffers();
|
||||
};
|
||||
return std::make_unique<SurfaceFrame>(std::move(surface),
|
||||
return std::make_unique<SurfaceFrame>(std::move(surface), true,
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ class IOSSurfaceGL final : public IOSSurface,
|
||||
|
||||
intptr_t GLContextFBO() const override;
|
||||
|
||||
bool UseOffscreenSurface() const override;
|
||||
bool SurfaceSupportsReadback() const override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
ExternalViewEmbedder* GetExternalViewEmbedder() override;
|
||||
|
||||
@ -49,11 +49,13 @@ intptr_t IOSSurfaceGL::GLContextFBO() const {
|
||||
return IsValid() ? render_target_->framebuffer() : GL_NONE;
|
||||
}
|
||||
|
||||
bool IOSSurfaceGL::UseOffscreenSurface() const {
|
||||
// The onscreen surface wraps a GL renderbuffer, which is extremely slow to read.
|
||||
// Certain filter effects require making a copy of the current destination, so we
|
||||
// always render to an offscreen surface, which will be much quicker to read/copy.
|
||||
return true;
|
||||
bool IOSSurfaceGL::SurfaceSupportsReadback() const {
|
||||
// The onscreen surface wraps a GL renderbuffer, which is extremely slow to read on iOS.
|
||||
// Certain filter effects, in particular BackdropFilter, require making a copy of
|
||||
// the current destination. For performance, the iOS surface will specify that it
|
||||
// does not support readback so that the engine compositor can implement a workaround
|
||||
// such as rendering the scene to an offscreen surface or Skia saveLayer.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IOSSurfaceGL::GLContextMakeCurrent() {
|
||||
|
||||
@ -20,6 +20,7 @@ class ScopedFrame final : public flutter::CompositorContext::ScopedFrame {
|
||||
nullptr,
|
||||
root_surface_transformation,
|
||||
instrumentation_enabled,
|
||||
true,
|
||||
nullptr),
|
||||
session_connection_(session_connection) {}
|
||||
|
||||
@ -96,6 +97,7 @@ CompositorContext::AcquireFrame(
|
||||
flutter::ExternalViewEmbedder* view_embedder,
|
||||
const SkMatrix& root_surface_transformation,
|
||||
bool instrumentation_enabled,
|
||||
bool surface_supports_readback,
|
||||
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger) {
|
||||
// TODO: The AcquireFrame interface is too broad and must be refactored to get
|
||||
// rid of the context and canvas arguments as those seem to be only used for
|
||||
|
||||
@ -45,6 +45,7 @@ class CompositorContext final : public flutter::CompositorContext {
|
||||
flutter::ExternalViewEmbedder* view_embedder,
|
||||
const SkMatrix& root_surface_transformation,
|
||||
bool instrumentation_enabled,
|
||||
bool surface_supports_readback,
|
||||
fml::RefPtr<fml::GpuThreadMerger> gpu_thread_merger) override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(CompositorContext);
|
||||
|
||||
@ -26,8 +26,10 @@ bool Surface::IsValid() {
|
||||
std::unique_ptr<flutter::SurfaceFrame> Surface::AcquireFrame(
|
||||
const SkISize& size) {
|
||||
return std::make_unique<flutter::SurfaceFrame>(
|
||||
nullptr, [](const flutter::SurfaceFrame& surface_frame,
|
||||
SkCanvas* canvas) { return true; });
|
||||
nullptr, true,
|
||||
[](const flutter::SurfaceFrame& surface_frame, SkCanvas* canvas) {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// |flutter::Surface|
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user