From ad1dfb3d7bddfb5744dc0ace1e4f1c2d8fa5e653 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 16 Nov 2021 16:45:03 -0800 Subject: [PATCH 01/13] pass opacity into display list layers that can render it directly --- flow/display_list_canvas.h | 4 +++- flow/display_list_utils.cc | 5 +++++ flow/display_list_utils.h | 8 ++++++++ flow/layers/container_layer.cc | 17 ++++++++++++++++- flow/layers/container_layer.h | 12 ++++++++++++ flow/layers/display_list_layer.cc | 4 +++- flow/layers/display_list_layer.h | 5 +++++ flow/layers/layer.h | 14 ++++++++++++++ flow/layers/opacity_layer.cc | 29 ++++++++++++++++++++++++----- flow/layers/opacity_layer.h | 5 +++++ 10 files changed, 95 insertions(+), 8 deletions(-) diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index 6c4d0f77275db..48e251da4e130 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -27,7 +27,9 @@ namespace flutter { class DisplayListCanvasDispatcher : public virtual Dispatcher, public SkPaintDispatchHelper { public: - explicit DisplayListCanvasDispatcher(SkCanvas* canvas) : canvas_(canvas) {} + explicit DisplayListCanvasDispatcher(SkCanvas* canvas, + SkAlpha extra_alpha = SK_AlphaOPAQUE) + : SkPaintDispatchHelper(extra_alpha), canvas_(canvas) {} void save() override; void restore() override; diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index 5532390cfb575..163c21bc79bf4 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -52,6 +52,11 @@ void SkPaintDispatchHelper::setStrokeMiter(SkScalar limit) { paint_.setStrokeMiter(limit); } void SkPaintDispatchHelper::setColor(SkColor color) { + if (extra_alpha_ < SK_AlphaOPAQUE) { + SkAlpha alpha = (SkColorGetA(color) * extra_alpha_ + SK_AlphaOPAQUE / 2) / + SK_AlphaOPAQUE; + color = SkColorSetA(color, alpha); + } paint_.setColor(color); } void SkPaintDispatchHelper::setBlendMode(SkBlendMode mode) { diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index db03a56511bb7..6ca9e0f7949d4 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -96,6 +96,13 @@ class IgnoreTransformDispatchHelper : public virtual Dispatcher { // which can be accessed at any time via paint(). class SkPaintDispatchHelper : public virtual Dispatcher { public: + SkPaintDispatchHelper(SkAlpha extra_alpha = SK_AlphaOPAQUE) + : extra_alpha_(extra_alpha) { + if (extra_alpha < SK_AlphaOPAQUE) { + paint_.setAlpha(extra_alpha); + } + } + void setAntiAlias(bool aa) override; void setDither(bool dither) override; void setStyle(SkPaint::Style style) override; @@ -118,6 +125,7 @@ class SkPaintDispatchHelper : public virtual Dispatcher { private: SkPaint paint_; + SkAlpha extra_alpha_; bool invert_colors_ = false; sk_sp color_filter_; diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 552d413f26f37..7070be3b05d8b 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -129,6 +129,13 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, FML_DCHECK(!context->has_platform_view); bool child_has_platform_view = false; bool child_has_texture_layer = false; + bool subtree_can_accept_opacity = + container_can_pass_opacity_to_children() && layers_.size() < 2; + + // FML_LOG(ERROR) << "container can pass opacity: " << container_can_pass_opacity_to_children(); + // FML_LOG(ERROR) << "container child layers: " << layers_.size(); + // FML_LOG(ERROR) << "subtree_opacity start = " << subtree_can_accept_opacity; + for (auto& layer : layers_) { // Reset context->has_platform_view to false so that layers aren't treated // as if they have a platform view based on one being previously found in a @@ -142,10 +149,16 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, child_has_platform_view || context->has_platform_view; child_has_texture_layer = child_has_texture_layer || context->has_texture_layer; + // FML_LOG(ERROR) << " layer_opacity = " << layer->layer_can_accept_opacity(); + if (subtree_can_accept_opacity) { + subtree_can_accept_opacity = layer->layer_can_accept_opacity(); + } } context->has_platform_view = child_has_platform_view; context->has_texture_layer = child_has_texture_layer; + // FML_LOG(ERROR) << "subtree_opacity end = " << subtree_can_accept_opacity; + context->subtree_can_accept_opacity = subtree_can_accept_opacity; set_subtree_has_platform_view(child_has_platform_view); } @@ -178,6 +191,8 @@ void ContainerLayer::TryToPrepareRasterCache(PrerollContext* context, } } +OpacityPassingContainerLayer::OpacityPassingContainerLayer() {} + MergedContainerLayer::MergedContainerLayer() { // Ensure the layer has only one direct child. // @@ -188,7 +203,7 @@ MergedContainerLayer::MergedContainerLayer() { // 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::Add(std::make_shared()); } void MergedContainerLayer::DiffChildren(DiffContext* context, diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index dfea4011a770b..64945dfc64872 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -28,6 +28,11 @@ class ContainerLayer : public Layer { virtual void DiffChildren(DiffContext* context, const ContainerLayer* old_layer); + virtual bool container_can_pass_opacity_to_children() { return false; } + bool layer_can_accept_opacity() override { + return container_can_pass_opacity_to_children(); + } + protected: void PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, @@ -54,6 +59,13 @@ class ContainerLayer : public Layer { FML_DISALLOW_COPY_AND_ASSIGN(ContainerLayer); }; +class OpacityPassingContainerLayer : public ContainerLayer { + public: + OpacityPassingContainerLayer(); + + bool container_can_pass_opacity_to_children() override { return true; } +}; + //------------------------------------------------------------------------------ /// Some ContainerLayer objects perform a rendering operation or filter on /// the rendered output of their children. Often that operation is changed diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index 8a9e562661670..7b81d02649c69 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -122,7 +122,9 @@ void DisplayListLayer::Paint(PaintContext& context) const { return; } - display_list()->RenderTo(context.leaf_nodes_canvas); + DisplayListCanvasDispatcher dispatcher(context.leaf_nodes_canvas, context.inherited_alpha); + display_list()->Dispatch(dispatcher); + // display_list()->RenderTo(context.leaf_nodes_canvas); } } // namespace flutter diff --git a/flow/layers/display_list_layer.h b/flow/layers/display_list_layer.h index cf48fc489fd5d..fbd600b7ab413 100644 --- a/flow/layers/display_list_layer.h +++ b/flow/layers/display_list_layer.h @@ -36,6 +36,11 @@ class DisplayListLayer : public Layer { void Paint(PaintContext& context) const override; + bool layer_can_accept_opacity() override { + // FML_LOG(ERROR) << "display list ops: " << display_list()->op_count(); + return display_list()->op_count() == 1; + } + private: SkPoint offset_; flutter::SkiaGPUObject display_list_; diff --git a/flow/layers/layer.h b/flow/layers/layer.h index f62cc3beb9835..fbaad9ee45db1 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -61,6 +61,13 @@ struct PrerollContext { // These allow us to track properties like elevation, opacity, and the // prescence of a texture layer during Preroll. bool has_texture_layer = false; + + // A layer that can accept an opacity value from its parent can set this + // field to true so that an OpacityLayer can pass the alpha value down + // to it during Paint rather than using a saveLayer. Leaving the value + // untouched will be compliant since the ContainerLayer will set it to + // false before calling each child. + bool subtree_can_accept_opacity = false; }; class PictureLayer; @@ -147,6 +154,7 @@ class Layer { const RasterCache* raster_cache; const bool checkerboard_offscreen_layers; const float frame_device_pixel_ratio; + SkAlpha inherited_alpha = SK_AlphaOPAQUE; }; // Calls SkCanvas::saveLayer and restores the layer upon destruction. Also @@ -216,6 +224,12 @@ class Layer { subtree_has_platform_view_ = value; } + // Returns true if the layer can render with an added opacity value + // inherited from an OpacityLayer ancestor. These values will be + // accumulated during Preroll to inform OpacityLayer if it can avoid + // its implicit saveLayer by passing down the opacity during Paint. + virtual bool layer_can_accept_opacity() { return false; } + // Returns the paint bounds in the layer's local coordinate system // as determined during Preroll(). The bounds should include any // transform, clip or distortions performed by the layer itself, diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 022dea79592c6..8e292ee07bc03 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -41,6 +41,8 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { // reverse transformation to the cull rect to properly cull child layers. context->cull_rect = context->cull_rect.makeOffset(-offset_.fX, -offset_.fY); + // FML_LOG(ERROR) << "Opacity layer prerolling children: "; + context->mutators_stack.PushTransform( SkMatrix::Translate(offset_.fX, offset_.fY)); context->mutators_stack.PushOpacity(alpha_); @@ -50,8 +52,14 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->mutators_stack.Pop(); context->mutators_stack.Pop(); - { - set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); + subtree_can_accept_opacity_ = context->subtree_can_accept_opacity; + context->subtree_can_accept_opacity = true; + + set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); + + // FML_LOG(ERROR) << "Opacity layer can pass opacity: " << subtree_can_accept_opacity_; + + if (!subtree_can_accept_opacity_) { #ifndef SUPPORT_FRACTIONAL_TRANSLATION child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); #endif @@ -66,12 +74,23 @@ void OpacityLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "OpacityLayer::Paint"); FML_DCHECK(needs_painting(context)); - SkPaint paint; - paint.setAlpha(alpha_); - SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); + SkAlpha inherited_alpha = context.inherited_alpha; + SkAlpha alpha = + (alpha_ * inherited_alpha + SK_AlphaOPAQUE / 2) / SK_AlphaOPAQUE; + + if (subtree_can_accept_opacity_) { + context.inherited_alpha = alpha; + PaintChildren(context); + context.inherited_alpha = inherited_alpha; + return; + } + + SkPaint paint; + paint.setAlpha(alpha); + #ifndef SUPPORT_FRACTIONAL_TRANSLATION context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( context.leaf_nodes_canvas->getTotalMatrix())); diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index b3ad2392c456b..ab68ece32de8d 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -33,9 +33,14 @@ class OpacityLayer : public MergedContainerLayer { void Paint(PaintContext& context) const override; + bool layer_can_accept_opacity() override { return true; } + + bool container_can_pass_opacity_to_children() override { return true; } + private: SkAlpha alpha_; SkPoint offset_; + bool subtree_can_accept_opacity_; FML_DISALLOW_COPY_AND_ASSIGN(OpacityLayer); }; From 1d78ae59b3b1899e4c488f8329b338b7a930b924 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 16 Nov 2021 16:57:36 -0800 Subject: [PATCH 02/13] fix formatting --- flow/display_list_canvas.h | 2 +- flow/display_list_utils.cc | 2 +- flow/layers/container_layer.cc | 6 ------ flow/layers/display_list_layer.cc | 4 ++-- flow/layers/display_list_layer.h | 1 - flow/layers/opacity_layer.cc | 4 ---- 6 files changed, 4 insertions(+), 15 deletions(-) diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index 48e251da4e130..e81d4d132adf5 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -29,7 +29,7 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, public: explicit DisplayListCanvasDispatcher(SkCanvas* canvas, SkAlpha extra_alpha = SK_AlphaOPAQUE) - : SkPaintDispatchHelper(extra_alpha), canvas_(canvas) {} + : SkPaintDispatchHelper(extra_alpha), canvas_(canvas) {} void save() override; void restore() override; diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index 163c21bc79bf4..f4f5f85285333 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -54,7 +54,7 @@ void SkPaintDispatchHelper::setStrokeMiter(SkScalar limit) { void SkPaintDispatchHelper::setColor(SkColor color) { if (extra_alpha_ < SK_AlphaOPAQUE) { SkAlpha alpha = (SkColorGetA(color) * extra_alpha_ + SK_AlphaOPAQUE / 2) / - SK_AlphaOPAQUE; + SK_AlphaOPAQUE; color = SkColorSetA(color, alpha); } paint_.setColor(color); diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 7070be3b05d8b..8270b1bae73e8 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -132,10 +132,6 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, bool subtree_can_accept_opacity = container_can_pass_opacity_to_children() && layers_.size() < 2; - // FML_LOG(ERROR) << "container can pass opacity: " << container_can_pass_opacity_to_children(); - // FML_LOG(ERROR) << "container child layers: " << layers_.size(); - // FML_LOG(ERROR) << "subtree_opacity start = " << subtree_can_accept_opacity; - for (auto& layer : layers_) { // Reset context->has_platform_view to false so that layers aren't treated // as if they have a platform view based on one being previously found in a @@ -149,7 +145,6 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, child_has_platform_view || context->has_platform_view; child_has_texture_layer = child_has_texture_layer || context->has_texture_layer; - // FML_LOG(ERROR) << " layer_opacity = " << layer->layer_can_accept_opacity(); if (subtree_can_accept_opacity) { subtree_can_accept_opacity = layer->layer_can_accept_opacity(); } @@ -157,7 +152,6 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, context->has_platform_view = child_has_platform_view; context->has_texture_layer = child_has_texture_layer; - // FML_LOG(ERROR) << "subtree_opacity end = " << subtree_can_accept_opacity; context->subtree_can_accept_opacity = subtree_can_accept_opacity; set_subtree_has_platform_view(child_has_platform_view); } diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index 7b81d02649c69..ef4a9167165e0 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -122,9 +122,9 @@ void DisplayListLayer::Paint(PaintContext& context) const { return; } - DisplayListCanvasDispatcher dispatcher(context.leaf_nodes_canvas, context.inherited_alpha); + DisplayListCanvasDispatcher dispatcher(context.leaf_nodes_canvas, + context.inherited_alpha); display_list()->Dispatch(dispatcher); - // display_list()->RenderTo(context.leaf_nodes_canvas); } } // namespace flutter diff --git a/flow/layers/display_list_layer.h b/flow/layers/display_list_layer.h index fbd600b7ab413..273408ed748ba 100644 --- a/flow/layers/display_list_layer.h +++ b/flow/layers/display_list_layer.h @@ -37,7 +37,6 @@ class DisplayListLayer : public Layer { void Paint(PaintContext& context) const override; bool layer_can_accept_opacity() override { - // FML_LOG(ERROR) << "display list ops: " << display_list()->op_count(); return display_list()->op_count() == 1; } diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 8e292ee07bc03..606a496796c35 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -41,8 +41,6 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { // reverse transformation to the cull rect to properly cull child layers. context->cull_rect = context->cull_rect.makeOffset(-offset_.fX, -offset_.fY); - // FML_LOG(ERROR) << "Opacity layer prerolling children: "; - context->mutators_stack.PushTransform( SkMatrix::Translate(offset_.fX, offset_.fY)); context->mutators_stack.PushOpacity(alpha_); @@ -57,8 +55,6 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); - // FML_LOG(ERROR) << "Opacity layer can pass opacity: " << subtree_can_accept_opacity_; - if (!subtree_can_accept_opacity_) { #ifndef SUPPORT_FRACTIONAL_TRANSLATION child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); From eef641242b6bd440c6e95d20c1aa25c06f06353d Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Sun, 21 Nov 2021 23:39:03 -0800 Subject: [PATCH 03/13] simplify layer_supports_opacity mechanism and use it in many more cases --- common/graphics/texture.h | 3 +- flow/display_list.cc | 138 +++++++++++++++--- flow/display_list.h | 71 +++++++-- flow/display_list_canvas.cc | 33 +++-- flow/display_list_canvas.h | 3 + flow/display_list_utils.h | 1 + flow/layers/clip_path_layer.cc | 21 ++- flow/layers/clip_rect_layer.cc | 21 ++- flow/layers/clip_rrect_layer.cc | 21 ++- flow/layers/container_layer.cc | 38 +++-- flow/layers/container_layer.h | 12 -- flow/layers/display_list_layer.cc | 29 ++-- flow/layers/display_list_layer.h | 4 - flow/layers/layer.cc | 3 +- flow/layers/layer.h | 75 ++++++++-- flow/layers/opacity_layer.cc | 26 ++-- flow/layers/opacity_layer.h | 14 +- flow/layers/picture_layer.cc | 19 ++- flow/layers/shader_mask_layer.cc | 9 +- flow/layers/texture_layer.cc | 7 +- flow/layers/transform_layer.cc | 1 + flow/raster_cache.cc | 13 +- flow/raster_cache.h | 10 +- flow/testing/mock_texture.cc | 9 +- flow/testing/mock_texture.h | 4 +- shell/common/shell_unittests.cc | 3 +- .../android/android_external_texture_gl.cc | 5 +- .../android/android_external_texture_gl.h | 3 +- .../embedder/embedder_external_texture_gl.cc | 7 +- .../embedder/embedder_external_texture_gl.h | 3 +- .../embedder_external_texture_metal.h | 3 +- .../embedder_external_texture_metal.mm | 7 +- 32 files changed, 449 insertions(+), 167 deletions(-) diff --git a/common/graphics/texture.h b/common/graphics/texture.h index 5ed8dafa668f4..57afa60a6361f 100644 --- a/common/graphics/texture.h +++ b/common/graphics/texture.h @@ -26,7 +26,8 @@ class Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) = 0; + const SkSamplingOptions& sampling, + const SkPaint* paint = nullptr) = 0; // Called from raster thread. virtual void OnGrContextCreated() = 0; diff --git a/flow/display_list.cc b/flow/display_list.cc index 4ed97a8766ef8..beea2dc8aca9e 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -978,8 +978,8 @@ static bool CompareOps(uint8_t* ptrA, return true; } -void DisplayList::RenderTo(SkCanvas* canvas) const { - DisplayListCanvasDispatcher dispatcher(canvas); +void DisplayList::RenderTo(SkCanvas* canvas, SkAlpha extra_alpha) const { + DisplayListCanvasDispatcher dispatcher(canvas, extra_alpha); Dispatch(dispatcher); } @@ -995,19 +995,31 @@ bool DisplayList::Equals(const DisplayList& other) const { return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other.byte_count_); } +DisplayList::DisplayList() + : byte_count_(0), + op_count_(0), + nested_byte_count_(0), + nested_op_count_(0), + unique_id_(0), + bounds_({0, 0, 0, 0}), + bounds_cull_({0, 0, 0, 0}), + can_apply_group_opacity_(true) {} + DisplayList::DisplayList(uint8_t* ptr, size_t byte_count, int op_count, size_t nested_byte_count, int nested_op_count, - const SkRect& cull_rect) + const SkRect& cull_rect, + bool can_apply_group_opacity) : storage_(ptr), byte_count_(byte_count), op_count_(op_count), nested_byte_count_(nested_byte_count), nested_op_count_(nested_op_count), bounds_({0, 0, -1, -1}), - bounds_cull_(cull_rect) { + bounds_cull_(cull_rect), + can_apply_group_opacity_(can_apply_group_opacity) { static std::atomic nextID{1}; do { unique_id_ = nextID.fetch_add(+1, std::memory_order_relaxed); @@ -1057,7 +1069,7 @@ void* DisplayListBuilder::Push(size_t pod, int op_inc, Args&&... args) { } sk_sp DisplayListBuilder::Build() { - while (save_level_ > 0) { + while (layer_stack_.size() > 1) { restore(); } size_t bytes = used_; @@ -1067,13 +1079,16 @@ sk_sp DisplayListBuilder::Build() { used_ = allocated_ = op_count_ = 0; nested_bytes_ = nested_op_count_ = 0; storage_.realloc(bytes); + bool compatible = layer_stack_.back().compatible_with_group_opacity(); return sk_sp(new DisplayList(storage_.release(), bytes, count, nested_bytes, nested_count, - cull_rect_)); + cull_rect_, compatible)); } DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect) - : cull_rect_(cull_rect) {} + : cull_rect_(cull_rect) { + layer_stack_.emplace_back(); +} DisplayListBuilder::~DisplayListBuilder() { uint8_t* ptr = storage_.get(); @@ -1227,22 +1242,69 @@ void DisplayListBuilder::setAttributesFromPaint( } } +void DisplayListBuilder::CheckOptimizations( + const DisplayListAttributeFlags& flags) { + LayerInfo& info = layer_stack_.back(); + if (flags.ignores_paint()) { + if (flags.supports_optional_paint()) { + info.add_compatible_op(); + } else { + info.mark_incompatible(); + } + return; + } + if (flags.applies_image_filter() && current_image_filter_) { + info.mark_incompatible(); + return; + } + if (flags.applies_color_filter() && + (current_color_filter_ || current_invert_colors_)) { + info.mark_incompatible(); + return; + } + if (flags.applies_blend() && + (current_blender_ || current_blend_mode_ != SkBlendMode::kSrcOver)) { + info.mark_incompatible(); + return; + } + if (!flags.applies_alpha_or_color()) { + info.mark_incompatible(); + return; + } + info.add_compatible_op(); +} + void DisplayListBuilder::save() { - save_level_++; Push(0, 1); + layer_stack_.emplace_back(); } void DisplayListBuilder::restore() { - if (save_level_ > 0) { + if (layer_stack_.size() > 1) { + // Grab the current layer info before we push the restore + // on the stack. + LayerInfo layer_info = layer_stack_.back(); + layer_stack_.pop_back(); Push(0, 1); - save_level_--; + if (!layer_info.has_layer) { + // For regular save() ops there was no protecting layer so we have to + // accumulate the values into the enclosing layer. + LayerInfo& info = layer_stack_.back(); + if (layer_info.cannot_inherit_opacity) { + info.mark_incompatible(); + } else if (layer_info.has_compatible_op) { + info.add_compatible_op(); + } + } } } void DisplayListBuilder::saveLayer(const SkRect* bounds, bool restore_with_paint) { - save_level_++; bounds // ? Push(0, 1, *bounds, restore_with_paint) : Push(0, 1, restore_with_paint); + CheckOptimizations(restore_with_paint ? kSaveLayerFlags + : kSaveLayerWithPaintFlags); + layer_stack_.emplace_back(true); } void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { @@ -1356,21 +1418,31 @@ void DisplayListBuilder::clipPath(const SkPath& path, void DisplayListBuilder::drawPaint() { Push(0, 1); + CheckOptimizations(kDrawPaintFlags); } void DisplayListBuilder::drawColor(SkColor color, SkBlendMode mode) { Push(0, 1, color, mode); + if (mode != SkBlendMode::kSrcOver) { + layer_stack_.back().mark_incompatible(); + } else { + layer_stack_.back().add_compatible_op(); + } } void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { Push(0, 1, p0, p1); + CheckOptimizations(kDrawLineFlags); } void DisplayListBuilder::drawRect(const SkRect& rect) { Push(0, 1, rect); + CheckOptimizations(kDrawRectFlags); } void DisplayListBuilder::drawOval(const SkRect& bounds) { Push(0, 1, bounds); + CheckOptimizations(kDrawOvalFlags); } void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { Push(0, 1, center, radius); + CheckOptimizations(kDrawCircleFlags); } void DisplayListBuilder::drawRRect(const SkRRect& rrect) { if (rrect.isRect()) { @@ -1379,14 +1451,17 @@ void DisplayListBuilder::drawRRect(const SkRRect& rrect) { drawOval(rrect.rect()); } else { Push(0, 1, rrect); + CheckOptimizations(kDrawRRectFlags); } } void DisplayListBuilder::drawDRRect(const SkRRect& outer, const SkRRect& inner) { Push(0, 1, outer, inner); + CheckOptimizations(kDrawDRRectFlags); } void DisplayListBuilder::drawPath(const SkPath& path) { Push(0, 1, path); + CheckOptimizations(kDrawPathFlags); } void DisplayListBuilder::drawArc(const SkRect& bounds, @@ -1394,6 +1469,8 @@ void DisplayListBuilder::drawArc(const SkRect& bounds, SkScalar sweep, bool useCenter) { Push(0, 1, bounds, start, sweep, useCenter); + CheckOptimizations(useCenter ? kDrawArcWithCenterFlags + : kDrawArcNoCenterFlags); } void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, uint32_t count, @@ -1404,12 +1481,15 @@ void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, switch (mode) { case SkCanvas::PointMode::kPoints_PointMode: data_ptr = Push(bytes, 1, count); + CheckOptimizations(kDrawPointsAsPointsFlags); break; case SkCanvas::PointMode::kLines_PointMode: data_ptr = Push(bytes, 1, count); + CheckOptimizations(kDrawPointsAsLinesFlags); break; case SkCanvas::PointMode::kPolygon_PointMode: data_ptr = Push(bytes, 1, count); + CheckOptimizations(kDrawPointsAsPolygonFlags); break; default: FML_DCHECK(false); @@ -1420,6 +1500,7 @@ void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, void DisplayListBuilder::drawVertices(const sk_sp vertices, SkBlendMode mode) { Push(0, 1, std::move(vertices), mode); + CheckOptimizations(kDrawVerticesFlags); } void DisplayListBuilder::drawImage(const sk_sp image, @@ -1429,6 +1510,8 @@ void DisplayListBuilder::drawImage(const sk_sp image, render_with_attributes ? Push(0, 1, std::move(image), point, sampling) : Push(0, 1, std::move(image), point, sampling); + CheckOptimizations(render_with_attributes ? kDrawImageWithPaintFlags + : kDrawImageFlags); } void DisplayListBuilder::drawImageRect(const sk_sp image, const SkRect& src, @@ -1438,6 +1521,8 @@ void DisplayListBuilder::drawImageRect(const sk_sp image, SkCanvas::SrcRectConstraint constraint) { Push(0, 1, std::move(image), src, dst, sampling, render_with_attributes, constraint); + CheckOptimizations(render_with_attributes ? kDrawImageRectWithPaintFlags + : kDrawImageRectFlags); } void DisplayListBuilder::drawImageNine(const sk_sp image, const SkIRect& center, @@ -1448,6 +1533,8 @@ void DisplayListBuilder::drawImageNine(const sk_sp image, ? Push(0, 1, std::move(image), center, dst, filter) : Push(0, 1, std::move(image), center, dst, filter); + CheckOptimizations(render_with_attributes ? kDrawImageNineWithPaintFlags + : kDrawImageNineFlags); } void DisplayListBuilder::drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, @@ -1469,6 +1556,8 @@ void DisplayListBuilder::drawImageLattice(const sk_sp image, filter, render_with_attributes); CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, lattice.fColors, cellCount, lattice.fRectTypes, cellCount); + CheckOptimizations(render_with_attributes ? kDrawImageLatticeWithPaintFlags + : kDrawImageLatticeFlags); } void DisplayListBuilder::drawAtlas(const sk_sp atlas, const SkRSXform xform[], @@ -1503,6 +1592,8 @@ void DisplayListBuilder::drawAtlas(const sk_sp atlas, } CopyV(data_ptr, xform, count, tex, count); } + CheckOptimizations(render_with_attributes ? kDrawAtlasWithPaintFlags + : kDrawAtlasFlags); } void DisplayListBuilder::drawPicture(const sk_sp picture, @@ -1520,6 +1611,8 @@ void DisplayListBuilder::drawPicture(const sk_sp picture, // This behavior is identical to the way SkPicture computes nested op counts. nested_op_count_ += picture->approximateOpCount(true) - 1; nested_bytes_ += picture->approximateBytesUsed(); + CheckOptimizations(render_with_attributes ? kDrawPictureWithPaintFlags + : kDrawPictureFlags); } void DisplayListBuilder::drawDisplayList( const sk_sp display_list) { @@ -1532,11 +1625,17 @@ void DisplayListBuilder::drawDisplayList( // This behavior is identical to the way SkPicture computes nested op counts. nested_op_count_ += display_list->op_count(true) - 1; nested_bytes_ += display_list->bytes(true); + if (display_list->can_apply_group_opacity()) { + layer_stack_.back().add_compatible_op(); + } else { + layer_stack_.back().mark_incompatible(); + } } void DisplayListBuilder::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { Push(0, 1, std::move(blob), x, y); + CheckOptimizations(kDrawTextBlobFlags); } void DisplayListBuilder::drawShadow(const SkPath& path, const SkColor color, @@ -1546,6 +1645,7 @@ void DisplayListBuilder::drawShadow(const SkPath& path, transparent_occluder // ? Push(0, 1, path, color, elevation, dpr) : Push(0, 1, path, color, elevation, dpr); + CheckOptimizations(kDrawShadowFlags); } // clang-format off @@ -1580,7 +1680,7 @@ void DisplayListBuilder::drawShadow(const SkPath& path, // clang-format on const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerFlags = - DisplayListAttributeFlags(kIgnoresPaint_); + DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerWithPaintFlags = DisplayListAttributeFlags(kIsNonGeometric_ | // @@ -1653,14 +1753,14 @@ const DisplayListAttributeFlags DisplayListOpFlags::kDrawVerticesFlags = kUsesImageFilter_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageFlags = - DisplayListAttributeFlags(kIgnoresPaint_); + DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE | // kUsesAntiAlias_ | kUsesMaskFilter_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageRectFlags = - DisplayListAttributeFlags(kIgnoresPaint_); + DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageRectWithPaintFlags = @@ -1668,33 +1768,33 @@ const DisplayListAttributeFlags kUsesAntiAlias_ | kUsesMaskFilter_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageNineFlags = - DisplayListAttributeFlags(kIgnoresPaint_); + DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageNineWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageLatticeFlags = - DisplayListAttributeFlags(kIgnoresPaint_); + DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageLatticeWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE); const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasFlags = - DisplayListAttributeFlags(kIgnoresPaint_); + DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE); const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureFlags = - DisplayListAttributeFlags(kIgnoresPaint_); + DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureWithPaintFlags = kSaveLayerWithPaintFlags; const DisplayListAttributeFlags DisplayListOpFlags::kDrawDisplayListFlags = - DisplayListAttributeFlags(kIgnoresPaint_); + DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawTextBlobFlags = DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | diff --git a/flow/display_list.h b/flow/display_list.h index d7b439face475..8b711180868a6 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -171,15 +171,7 @@ class DisplayList : public SkRefCnt { static const SkSamplingOptions MipmapSampling; static const SkSamplingOptions CubicSampling; - DisplayList() - : byte_count_(0), - op_count_(0), - nested_byte_count_(0), - nested_op_count_(0), - unique_id_(0), - bounds_({0, 0, 0, 0}), - bounds_cull_({0, 0, 0, 0}) {} - + DisplayList(); ~DisplayList(); void Dispatch(Dispatcher& ctx) const { @@ -187,7 +179,7 @@ class DisplayList : public SkRefCnt { Dispatch(ctx, ptr, ptr + byte_count_); } - void RenderTo(SkCanvas* canvas) const; + void RenderTo(SkCanvas* canvas, SkAlpha extra_alpha = SK_AlphaOPAQUE) const; // SkPicture always includes nested bytes, but nested ops are // only included if requested. The defaults used here for these @@ -212,13 +204,16 @@ class DisplayList : public SkRefCnt { bool Equals(const DisplayList& other) const; + bool can_apply_group_opacity() { return can_apply_group_opacity_; } + private: DisplayList(uint8_t* ptr, size_t byte_count, int op_count, size_t nested_byte_count, int nested_op_count, - const SkRect& cull_rect); + const SkRect& cull_rect, + bool can_apply_group_opacity); std::unique_ptr> storage_; size_t byte_count_; @@ -233,6 +228,8 @@ class DisplayList : public SkRefCnt { // Only used for drawPaint() and drawColor() SkRect bounds_cull_; + bool can_apply_group_opacity_; + void ComputeBounds(); void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const; @@ -548,6 +545,14 @@ class DisplayListFlags { static constexpr int kUsesMaskFilter_ = 1 << 18; static constexpr int kUsesImageFilter_ = 1 << 19; + // Some ops have an optional paint argument. If the version + // stored in the DisplayList ignores the paint, but there + // is an option to render the same op with a paint then + // both of the following flags are set to indicate that + // a default paint object can be constructed when rendering + // the op to carry information imposed from outside the + // DisplayList (for example, the extra_alpha override). + static constexpr int kHasOptionalPaint_ = 1 << 29; static constexpr int kIgnoresPaint_ = 1 << 30; // clang-format on @@ -631,6 +636,7 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { } bool ignores_paint() const { return has_any(kIgnoresPaint_); } + bool supports_optional_paint() const { return has_any(kHasOptionalPaint_); } bool applies_anti_alias() const { return has_any(kUsesAntiAlias_); } bool applies_dither() const { return has_any(kUsesDither_); } @@ -746,7 +752,9 @@ class DisplayListOpFlags : DisplayListFlags { // If there is some code that already renders to an SkCanvas object, // those rendering commands can be captured into a DisplayList using // the DisplayListCanvasRecorder class. -class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { +class DisplayListBuilder final : public virtual Dispatcher, + public SkRefCnt, + DisplayListOpFlags { public: explicit DisplayListBuilder(const SkRect& cull_rect = kMaxCullRect_); ~DisplayListBuilder(); @@ -880,7 +888,7 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { void save() override; void saveLayer(const SkRect* bounds, bool restore_with_paint) override; void restore() override; - int getSaveCount() { return save_level_ + 1; } + int getSaveCount() { return layer_stack_.size(); } void translate(SkScalar tx, SkScalar ty) override; void scale(SkScalar sx, SkScalar sy) override; @@ -977,7 +985,6 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { size_t used_ = 0; size_t allocated_ = 0; int op_count_ = 0; - int save_level_ = 0; // bytes and ops from |drawPicture| and |drawDisplayList| size_t nested_bytes_ = 0; @@ -996,6 +1003,42 @@ class DisplayListBuilder final : public virtual Dispatcher, public SkRefCnt { return SkScalarIsFinite(sigma) && sigma > 0.0; } + struct LayerInfo { + LayerInfo(bool has_layer = false) + : has_layer(has_layer), + cannot_inherit_opacity(false), + has_compatible_op(false) {} + + bool has_layer; + bool cannot_inherit_opacity; + bool has_compatible_op; + + bool compatible_with_group_opacity() const { + return !cannot_inherit_opacity; + } + + void mark_incompatible() { cannot_inherit_opacity = true; } + + // For now this only allows a single compatible op to mark the + // layer as being compatible with group opacity. If we start + // computing bounds of ops in the Builder methods then we + // can upgrade this to checking for overlapping ops. + // See https://github.com/flutter/flutter/issues/93899 + void add_compatible_op() { + if (!cannot_inherit_opacity) { + if (has_compatible_op) { + cannot_inherit_opacity = true; + } else { + has_compatible_op = true; + } + } + } + }; + + std::vector layer_stack_; + + void CheckOptimizations(const DisplayListAttributeFlags& flags); + void onSetAntiAlias(bool aa); void onSetDither(bool dither); void onSetInvertColors(bool invert); diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index 7f66a37515ec6..813a333d39d55 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -11,6 +11,19 @@ namespace flutter { +const SkPaint* DisplayListCanvasDispatcher::safe_paint(bool use_attributes) { + if (use_attributes) { + // The accumulated SkPaint object will already have incorporated + // any attribute overrides. + return &paint(); + } else if (extra_alpha() < SK_AlphaOPAQUE) { + temp_paint_.setAlpha(extra_alpha()); + return &temp_paint_; + } else { + return nullptr; + } +} + void DisplayListCanvasDispatcher::save() { canvas_->save(); } @@ -20,7 +33,7 @@ void DisplayListCanvasDispatcher::restore() { void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds, bool restore_with_paint) { TRACE_EVENT0("flutter", "Canvas::saveLayer"); - canvas_->saveLayer(bounds, restore_with_paint ? &paint() : nullptr); + canvas_->saveLayer(bounds, safe_paint(restore_with_paint)); } void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) { @@ -133,7 +146,7 @@ void DisplayListCanvasDispatcher::drawImage(const sk_sp image, const SkSamplingOptions& sampling, bool render_with_attributes) { canvas_->drawImage(image, point.fX, point.fY, sampling, - render_with_attributes ? &paint() : nullptr); + safe_paint(render_with_attributes)); } void DisplayListCanvasDispatcher::drawImageRect( const sk_sp image, @@ -143,8 +156,7 @@ void DisplayListCanvasDispatcher::drawImageRect( bool render_with_attributes, SkCanvas::SrcRectConstraint constraint) { canvas_->drawImageRect(image, src, dst, sampling, - render_with_attributes ? &paint() : nullptr, - constraint); + safe_paint(render_with_attributes), constraint); } void DisplayListCanvasDispatcher::drawImageNine(const sk_sp image, const SkIRect& center, @@ -152,7 +164,7 @@ void DisplayListCanvasDispatcher::drawImageNine(const sk_sp image, SkFilterMode filter, bool render_with_attributes) { canvas_->drawImageNine(image.get(), center, dst, filter, - render_with_attributes ? &paint() : nullptr); + safe_paint(render_with_attributes)); } void DisplayListCanvasDispatcher::drawImageLattice( const sk_sp image, @@ -161,7 +173,7 @@ void DisplayListCanvasDispatcher::drawImageLattice( SkFilterMode filter, bool render_with_attributes) { canvas_->drawImageLattice(image.get(), lattice, dst, filter, - render_with_attributes ? &paint() : nullptr); + safe_paint(render_with_attributes)); } void DisplayListCanvasDispatcher::drawAtlas(const sk_sp atlas, const SkRSXform xform[], @@ -173,15 +185,16 @@ void DisplayListCanvasDispatcher::drawAtlas(const sk_sp atlas, const SkRect* cullRect, bool render_with_attributes) { canvas_->drawAtlas(atlas.get(), xform, tex, colors, count, mode, sampling, - cullRect, render_with_attributes ? &paint() : nullptr); + cullRect, safe_paint(render_with_attributes)); } void DisplayListCanvasDispatcher::drawPicture(const sk_sp picture, const SkMatrix* matrix, bool render_with_attributes) { - if (render_with_attributes) { + const SkPaint* paint = safe_paint(render_with_attributes); + if (paint) { // drawPicture does an implicit saveLayer if an SkPaint is supplied. TRACE_EVENT0("flutter", "Canvas::saveLayer"); - canvas_->drawPicture(picture, matrix, &paint()); + canvas_->drawPicture(picture, matrix, paint); } else { canvas_->drawPicture(picture, matrix, nullptr); } @@ -190,7 +203,7 @@ void DisplayListCanvasDispatcher::drawDisplayList( const sk_sp display_list) { int save_count = canvas_->save(); { - DisplayListCanvasDispatcher dispatcher(canvas_); + DisplayListCanvasDispatcher dispatcher(canvas_, extra_alpha()); display_list->Dispatch(dispatcher); } canvas_->restoreToCount(save_count); diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index e81d4d132adf5..950985777a483 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -31,6 +31,8 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, SkAlpha extra_alpha = SK_AlphaOPAQUE) : SkPaintDispatchHelper(extra_alpha), canvas_(canvas) {} + const SkPaint* safe_paint(bool use_attributes); + void save() override; void restore() override; void saveLayer(const SkRect* bounds, bool restore_with_paint) override; @@ -117,6 +119,7 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, private: SkCanvas* canvas_; + SkPaint temp_paint_; }; // Receives all methods on SkCanvas and sends them to a DisplayListBuilder diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 6ca9e0f7949d4..0c0bdf27eacdd 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -122,6 +122,7 @@ class SkPaintDispatchHelper : public virtual Dispatcher { void setImageFilter(sk_sp filter) override; const SkPaint& paint() { return paint_; } + SkAlpha extra_alpha() { return extra_alpha_; } private: SkPaint paint_; diff --git a/flow/layers/clip_path_layer.cc b/flow/layers/clip_path_layer.cc index ca30baf2ac8cc..5a4ad3044142e 100644 --- a/flow/layers/clip_path_layer.cc +++ b/flow/layers/clip_path_layer.cc @@ -10,6 +10,7 @@ namespace flutter { ClipPathLayer::ClipPathLayer(const SkPath& clip_path, Clip clip_behavior) : clip_path_(clip_path), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); + set_layer_can_inherit_opacity(true); } void ClipPathLayer::Diff(DiffContext* context, const Layer* old_layer) { @@ -58,16 +59,20 @@ void ClipPathLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->clipPath(clip_path_, clip_behavior_ != Clip::hardEdge); - if (UsesSaveLayer()) { - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); + if (!UsesSaveLayer()) { + PaintChildren(context); + return; } + + AutoCachePaint cache_paint(context); + TRACE_EVENT0("flutter", "Canvas::saveLayer"); + context.internal_nodes_canvas->saveLayer(paint_bounds(), cache_paint.paint()); + PaintChildren(context); - if (UsesSaveLayer()) { - context.internal_nodes_canvas->restore(); - if (context.checkerboard_offscreen_layers) { - DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); - } + + context.internal_nodes_canvas->restore(); + if (context.checkerboard_offscreen_layers) { + DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); } } diff --git a/flow/layers/clip_rect_layer.cc b/flow/layers/clip_rect_layer.cc index 8504d41ed15b4..cdb5531283bde 100644 --- a/flow/layers/clip_rect_layer.cc +++ b/flow/layers/clip_rect_layer.cc @@ -10,6 +10,7 @@ namespace flutter { ClipRectLayer::ClipRectLayer(const SkRect& clip_rect, Clip clip_behavior) : clip_rect_(clip_rect), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); + set_layer_can_inherit_opacity(true); } void ClipRectLayer::Diff(DiffContext* context, const Layer* old_layer) { @@ -57,16 +58,20 @@ void ClipRectLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->clipRect(clip_rect_, clip_behavior_ != Clip::hardEdge); - if (UsesSaveLayer()) { - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - context.internal_nodes_canvas->saveLayer(clip_rect_, nullptr); + if (!UsesSaveLayer()) { + PaintChildren(context); + return; } + + AutoCachePaint cache_paint(context); + TRACE_EVENT0("flutter", "Canvas::saveLayer"); + context.internal_nodes_canvas->saveLayer(clip_rect_, cache_paint.paint()); + PaintChildren(context); - if (UsesSaveLayer()) { - context.internal_nodes_canvas->restore(); - if (context.checkerboard_offscreen_layers) { - DrawCheckerboard(context.internal_nodes_canvas, clip_rect_); - } + + context.internal_nodes_canvas->restore(); + if (context.checkerboard_offscreen_layers) { + DrawCheckerboard(context.internal_nodes_canvas, clip_rect_); } } diff --git a/flow/layers/clip_rrect_layer.cc b/flow/layers/clip_rrect_layer.cc index 511b3da5526da..52377ae984507 100644 --- a/flow/layers/clip_rrect_layer.cc +++ b/flow/layers/clip_rrect_layer.cc @@ -10,6 +10,7 @@ namespace flutter { ClipRRectLayer::ClipRRectLayer(const SkRRect& clip_rrect, Clip clip_behavior) : clip_rrect_(clip_rrect), clip_behavior_(clip_behavior) { FML_DCHECK(clip_behavior != Clip::none); + set_layer_can_inherit_opacity(true); } void ClipRRectLayer::Diff(DiffContext* context, const Layer* old_layer) { @@ -58,16 +59,20 @@ void ClipRRectLayer::Paint(PaintContext& context) const { context.internal_nodes_canvas->clipRRect(clip_rrect_, clip_behavior_ != Clip::hardEdge); - if (UsesSaveLayer()) { - TRACE_EVENT0("flutter", "Canvas::saveLayer"); - context.internal_nodes_canvas->saveLayer(paint_bounds(), nullptr); + if (!UsesSaveLayer()) { + PaintChildren(context); + return; } + + AutoCachePaint cache_paint(context); + TRACE_EVENT0("flutter", "Canvas::saveLayer"); + context.internal_nodes_canvas->saveLayer(paint_bounds(), cache_paint.paint()); + PaintChildren(context); - if (UsesSaveLayer()) { - context.internal_nodes_canvas->restore(); - if (context.checkerboard_offscreen_layers) { - DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); - } + + context.internal_nodes_canvas->restore(); + if (context.checkerboard_offscreen_layers) { + DrawCheckerboard(context.internal_nodes_canvas, paint_bounds()); } } diff --git a/flow/layers/container_layer.cc b/flow/layers/container_layer.cc index 8270b1bae73e8..6035ef56e83aa 100644 --- a/flow/layers/container_layer.cc +++ b/flow/layers/container_layer.cc @@ -121,6 +121,13 @@ void ContainerLayer::Paint(PaintContext& context) const { PaintChildren(context); } +static bool safe_intersection_test(const SkRect* rect1, const SkRect& rect2) { + if (rect1->isEmpty() || rect2.isEmpty()) { + return false; + } + return rect1->intersects(rect2); +} + void ContainerLayer::PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, SkRect* child_paint_bounds) { @@ -129,30 +136,39 @@ void ContainerLayer::PrerollChildren(PrerollContext* context, FML_DCHECK(!context->has_platform_view); bool child_has_platform_view = false; bool child_has_texture_layer = false; - bool subtree_can_accept_opacity = - container_can_pass_opacity_to_children() && layers_.size() < 2; + bool subtree_can_inherit_opacity = layer_can_inherit_opacity(); for (auto& layer : layers_) { // Reset context->has_platform_view to false so that layers aren't treated // as if they have a platform view based on one being previously found in a // sibling tree. context->has_platform_view = false; + // Initialize the "inherit opacity" flag to the value recorded in the layer + // and allow it to override the answer during its |Preroll| + context->subtree_can_inherit_opacity = layer->layer_can_inherit_opacity(); layer->Preroll(context, child_matrix); + + subtree_can_inherit_opacity = + subtree_can_inherit_opacity && context->subtree_can_inherit_opacity; + if (subtree_can_inherit_opacity && + safe_intersection_test(child_paint_bounds, layer->paint_bounds())) { + // This will allow inheritance by a linear sequence of non-overlapping + // children, but will fail with a grid or other arbitrary 2D layout. + // See https://github.com/flutter/flutter/issues/93899 + subtree_can_inherit_opacity = false; + } child_paint_bounds->join(layer->paint_bounds()); child_has_platform_view = child_has_platform_view || context->has_platform_view; child_has_texture_layer = child_has_texture_layer || context->has_texture_layer; - if (subtree_can_accept_opacity) { - subtree_can_accept_opacity = layer->layer_can_accept_opacity(); - } } context->has_platform_view = child_has_platform_view; context->has_texture_layer = child_has_texture_layer; - context->subtree_can_accept_opacity = subtree_can_accept_opacity; + context->subtree_can_inherit_opacity = subtree_can_inherit_opacity; set_subtree_has_platform_view(child_has_platform_view); } @@ -185,8 +201,6 @@ void ContainerLayer::TryToPrepareRasterCache(PrerollContext* context, } } -OpacityPassingContainerLayer::OpacityPassingContainerLayer() {} - MergedContainerLayer::MergedContainerLayer() { // Ensure the layer has only one direct child. // @@ -197,7 +211,13 @@ MergedContainerLayer::MergedContainerLayer() { // 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::Add(std::make_shared()); + + // 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, diff --git a/flow/layers/container_layer.h b/flow/layers/container_layer.h index 64945dfc64872..dfea4011a770b 100644 --- a/flow/layers/container_layer.h +++ b/flow/layers/container_layer.h @@ -28,11 +28,6 @@ class ContainerLayer : public Layer { virtual void DiffChildren(DiffContext* context, const ContainerLayer* old_layer); - virtual bool container_can_pass_opacity_to_children() { return false; } - bool layer_can_accept_opacity() override { - return container_can_pass_opacity_to_children(); - } - protected: void PrerollChildren(PrerollContext* context, const SkMatrix& child_matrix, @@ -59,13 +54,6 @@ class ContainerLayer : public Layer { FML_DISALLOW_COPY_AND_ASSIGN(ContainerLayer); }; -class OpacityPassingContainerLayer : public ContainerLayer { - public: - OpacityPassingContainerLayer(); - - bool container_can_pass_opacity_to_children() override { return true; } -}; - //------------------------------------------------------------------------------ /// Some ContainerLayer objects perform a rendering operation or filter on /// the rendered output of their children. Often that operation is changed diff --git a/flow/layers/display_list_layer.cc b/flow/layers/display_list_layer.cc index ef4a9167165e0..66f278c7729d7 100644 --- a/flow/layers/display_list_layer.cc +++ b/flow/layers/display_list_layer.cc @@ -15,7 +15,12 @@ DisplayListLayer::DisplayListLayer(const SkPoint& offset, : offset_(offset), display_list_(std::move(display_list)), is_complex_(is_complex), - will_change_(will_change) {} + will_change_(will_change) { + if (display_list_.skia_object()) { + set_layer_can_inherit_opacity( + display_list_.skia_object()->can_apply_group_opacity()); + } +} bool DisplayListLayer::IsReplacing(DiffContext* context, const Layer* layer) const { @@ -94,8 +99,10 @@ void DisplayListLayer::Preroll(PrerollContext* context, if (auto* cache = context->raster_cache) { TRACE_EVENT0("flutter", "DisplayListLayer::RasterCache (Preroll)"); if (context->cull_rect.intersects(bounds)) { - cache->Prepare(context, disp_list, is_complex_, will_change_, matrix, - offset_); + if (cache->Prepare(context, disp_list, is_complex_, will_change_, matrix, + offset_)) { + context->subtree_can_inherit_opacity = true; + } } else { // Don't evict raster cache entry during partial repaint cache->Touch(disp_list, matrix); @@ -116,15 +123,17 @@ void DisplayListLayer::Paint(PaintContext& context) const { context.leaf_nodes_canvas->getTotalMatrix())); #endif - if (context.raster_cache && - context.raster_cache->Draw(*display_list(), *context.leaf_nodes_canvas)) { - TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); - return; + if (context.raster_cache) { + AutoCachePaint cache_paint(context); + if (context.raster_cache->Draw(*display_list(), *context.leaf_nodes_canvas, + cache_paint.paint())) { + TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); + return; + } } - DisplayListCanvasDispatcher dispatcher(context.leaf_nodes_canvas, - context.inherited_alpha); - display_list()->Dispatch(dispatcher); + display_list()->RenderTo(context.leaf_nodes_canvas, + context.inherited_opacity); } } // namespace flutter diff --git a/flow/layers/display_list_layer.h b/flow/layers/display_list_layer.h index 273408ed748ba..cf48fc489fd5d 100644 --- a/flow/layers/display_list_layer.h +++ b/flow/layers/display_list_layer.h @@ -36,10 +36,6 @@ class DisplayListLayer : public Layer { void Paint(PaintContext& context) const override; - bool layer_can_accept_opacity() override { - return display_list()->op_count() == 1; - } - private: SkPoint offset_; flutter::SkiaGPUObject display_list_; diff --git a/flow/layers/layer.cc b/flow/layers/layer.cc index 8a34b775ef766..621ae585701e5 100644 --- a/flow/layers/layer.cc +++ b/flow/layers/layer.cc @@ -13,7 +13,8 @@ Layer::Layer() : paint_bounds_(SkRect::MakeEmpty()), unique_id_(NextUniqueID()), original_layer_id_(unique_id_), - subtree_has_platform_view_(false) {} + subtree_has_platform_view_(false), + layer_can_inherit_opacity_(false) {} Layer::~Layer() = default; diff --git a/flow/layers/layer.h b/flow/layers/layer.h index fbaad9ee45db1..6eb308fa21e5c 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -62,12 +62,24 @@ struct PrerollContext { // prescence of a texture layer during Preroll. bool has_texture_layer = false; - // A layer that can accept an opacity value from its parent can set this - // field to true so that an OpacityLayer can pass the alpha value down - // to it during Paint rather than using a saveLayer. Leaving the value - // untouched will be compliant since the ContainerLayer will set it to - // false before calling each child. - bool subtree_can_accept_opacity = false; + // This value indicates that the entire subtree below the layer can inherit + // an opacity value and modulate its own visibility accordingly. + // For Layers which cannot either apply such an inherited opacity or pass + // it along to their children, they can ignore this value as its default + // behavior is "opt-in". + // For Layers that support this condition, it can be recorded in their + // constructor using the |set_layer_can_inherit_opacity| method and the + // value will be accumulated and recorded by the |PrerollChidren| method + // automatically. + // If the property is more dynamic then the Layer can dynamically set this + // flag before returning from the |Preroll| method. + // For ContainerLayers that need to know if their children can inherit + // the value, the |PrerollChildren| method will have set this value in + // the context before it returns. If the container can support it as long + // as the subtree can support it, no further work needs to be done other + // than to remember the value so that it can choose the right strategy + // for its |Paint| method. + bool subtree_can_inherit_opacity = false; }; class PictureLayer; @@ -154,7 +166,36 @@ class Layer { const RasterCache* raster_cache; const bool checkerboard_offscreen_layers; const float frame_device_pixel_ratio; - SkAlpha inherited_alpha = SK_AlphaOPAQUE; + + // The following value should be used to modulate the opacity of the + // layer during |Paint|. If the layer does not set the corresponding + // |layer_can_inherit_opacity()| flag, then this value should always + // be |SK_AlphaOPAQUE|. The value is supplied using the |SkAlpha| type + // but the name is meant to clearly indicate that it needs to be + // interpreted by the |Paint| method as specifically modulating the + // opacity of the resulting operations as if by using a |saveLayer| + // with this alpha value and a |kSrcOver| blend mode in the |SkPaint|. + SkAlpha inherited_opacity = SK_AlphaOPAQUE; + }; + + class AutoCachePaint { + public: + AutoCachePaint(PaintContext& context) : context_(context) { + needs_paint_ = context.inherited_opacity < SK_AlphaOPAQUE; + if (needs_paint_) { + paint_.setAlpha(context.inherited_opacity); + context.inherited_opacity = SK_AlphaOPAQUE; + } + } + + ~AutoCachePaint() { context_.inherited_opacity = paint_.getAlpha(); } + + const SkPaint* paint() { return needs_paint_ ? &paint_ : nullptr; } + + private: + PaintContext& context_; + SkPaint paint_; + bool needs_paint_; }; // Calls SkCanvas::saveLayer and restores the layer upon destruction. Also @@ -224,11 +265,17 @@ class Layer { subtree_has_platform_view_ = value; } - // Returns true if the layer can render with an added opacity value - // inherited from an OpacityLayer ancestor. These values will be - // accumulated during Preroll to inform OpacityLayer if it can avoid - // its implicit saveLayer by passing down the opacity during Paint. - virtual bool layer_can_accept_opacity() { return false; } + // Returns true if the layer can render with an added opacity value inherited + // from an OpacityLayer ancestor and delivered to its |Paint| method through + // the |PaintContext.inherited_opacity| field. This flag can be set either + // in the Layer's constructor if it is a lifetime constant value, or during + // the |Preroll| method if it must determine the capability based on data + // only available when it is part of a tree. It must set this value before + // recursing to its children if it is a |ContainerLayer|. + bool layer_can_inherit_opacity() const { return layer_can_inherit_opacity_; } + void set_layer_can_inherit_opacity(bool value) { + layer_can_inherit_opacity_ = value; + } // Returns the paint bounds in the layer's local coordinate system // as determined during Preroll(). The bounds should include any @@ -267,6 +314,9 @@ class Layer { // See https://github.com/flutter/flutter/issues/81419 return true; } + if (context.inherited_opacity == 0) { + return false; + } // Workaround for Skia bug (quickReject does not reject empty bounds). // https://bugs.chromium.org/p/skia/issues/detail?id=10951 if (paint_bounds_.isEmpty()) { @@ -296,6 +346,7 @@ class Layer { uint64_t unique_id_; uint64_t original_layer_id_; bool subtree_has_platform_view_; + bool layer_can_inherit_opacity_; static uint64_t NextUniqueID(); diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 606a496796c35..b9ad45182e7c4 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -10,7 +10,12 @@ namespace flutter { OpacityLayer::OpacityLayer(SkAlpha alpha, const SkPoint& offset) - : alpha_(alpha), offset_(offset) {} + : alpha_(alpha), offset_(offset), children_can_accept_opacity_(false) { + // We can always inhert opacity even if we cannot pass it along to + // our children as we can accumulate the inherited opacity into our + // own opacity value before we recurse. + set_layer_can_inherit_opacity(true); +} void OpacityLayer::Diff(DiffContext* context, const Layer* old_layer) { DiffContext::AutoSubtreeRestore subtree(context); @@ -50,12 +55,11 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->mutators_stack.Pop(); context->mutators_stack.Pop(); - subtree_can_accept_opacity_ = context->subtree_can_accept_opacity; - context->subtree_can_accept_opacity = true; + set_children_can_accept_opacity(context->subtree_can_inherit_opacity); set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); - if (!subtree_can_accept_opacity_) { + if (!children_can_accept_opacity()) { #ifndef SUPPORT_FRACTIONAL_TRANSLATION child_matrix = RasterCache::GetIntegralTransCTM(child_matrix); #endif @@ -73,19 +77,19 @@ void OpacityLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); - SkAlpha inherited_alpha = context.inherited_alpha; - SkAlpha alpha = - (alpha_ * inherited_alpha + SK_AlphaOPAQUE / 2) / SK_AlphaOPAQUE; + SkAlpha inherited_opacity = context.inherited_opacity; + SkAlpha subtree_opacity = + (alpha_ * inherited_opacity + SK_AlphaOPAQUE / 2) / SK_AlphaOPAQUE; - if (subtree_can_accept_opacity_) { - context.inherited_alpha = alpha; + if (children_can_accept_opacity()) { + context.inherited_opacity = subtree_opacity; PaintChildren(context); - context.inherited_alpha = inherited_alpha; + context.inherited_opacity = inherited_opacity; return; } SkPaint paint; - paint.setAlpha(alpha); + paint.setAlpha(subtree_opacity); #ifndef SUPPORT_FRACTIONAL_TRANSLATION context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index ab68ece32de8d..e67c361b7f9ca 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -33,14 +33,20 @@ class OpacityLayer : public MergedContainerLayer { void Paint(PaintContext& context) const override; - bool layer_can_accept_opacity() override { return true; } - - bool container_can_pass_opacity_to_children() override { return true; } + // Returns whether the children are capable of inheriting an opacity value + // and modifying their rendering accordingly. This value is only guaranteed + // to be valid after the local |Preroll| method is called. + bool children_can_accept_opacity() const { + return children_can_accept_opacity_; + } + void set_children_can_accept_opacity(bool value) { + children_can_accept_opacity_ = value; + } private: SkAlpha alpha_; SkPoint offset_; - bool subtree_can_accept_opacity_; + bool children_can_accept_opacity_; FML_DISALLOW_COPY_AND_ASSIGN(OpacityLayer); }; diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 4103aadae0aff..90d15b1a88fc7 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -117,8 +117,10 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { if (auto* cache = context->raster_cache) { TRACE_EVENT0("flutter", "PictureLayer::RasterCache (Preroll)"); if (context->cull_rect.intersects(bounds)) { - cache->Prepare(context, sk_picture, is_complex_, will_change_, matrix, - offset_); + if (cache->Prepare(context, sk_picture, is_complex_, will_change_, matrix, + offset_)) { + context->subtree_can_inherit_opacity = true; + } } else { // Don't evict raster cache entry during partial repaint cache->Touch(sk_picture, matrix); @@ -140,11 +142,16 @@ void PictureLayer::Paint(PaintContext& context) const { context.leaf_nodes_canvas->getTotalMatrix())); #endif - if (context.raster_cache && - context.raster_cache->Draw(*picture(), *context.leaf_nodes_canvas)) { - TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); - return; + if (context.raster_cache) { + AutoCachePaint cache_paint(context); + if (context.raster_cache->Draw(*picture(), *context.leaf_nodes_canvas, + cache_paint.paint())) { + TRACE_EVENT_INSTANT0("flutter", "raster cache hit"); + return; + } } + + FML_DCHECK(context.inherited_opacity == SK_AlphaOPAQUE); picture()->playback(context.leaf_nodes_canvas); } diff --git a/flow/layers/shader_mask_layer.cc b/flow/layers/shader_mask_layer.cc index e007ede2145df..521029f06ef1e 100644 --- a/flow/layers/shader_mask_layer.cc +++ b/flow/layers/shader_mask_layer.cc @@ -9,7 +9,9 @@ namespace flutter { ShaderMaskLayer::ShaderMaskLayer(sk_sp shader, const SkRect& mask_rect, SkBlendMode blend_mode) - : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) {} + : shader_(shader), mask_rect_(mask_rect), blend_mode_(blend_mode) { + set_layer_can_inherit_opacity(true); +} void ShaderMaskLayer::Diff(DiffContext* context, const Layer* old_layer) { DiffContext::AutoSubtreeRestore subtree(context); @@ -37,8 +39,9 @@ void ShaderMaskLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ShaderMaskLayer::Paint"); FML_DCHECK(needs_painting(context)); - Layer::AutoSaveLayer save = - Layer::AutoSaveLayer::Create(context, paint_bounds(), nullptr); + AutoCachePaint cache_paint(context); + Layer::AutoSaveLayer save = Layer::AutoSaveLayer::Create( + context, paint_bounds(), cache_paint.paint()); PaintChildren(context); SkPaint paint; diff --git a/flow/layers/texture_layer.cc b/flow/layers/texture_layer.cc index e360b50d55467..63194b0cd5cb2 100644 --- a/flow/layers/texture_layer.cc +++ b/flow/layers/texture_layer.cc @@ -17,7 +17,9 @@ TextureLayer::TextureLayer(const SkPoint& offset, size_(size), texture_id_(texture_id), freeze_(freeze), - sampling_(sampling) {} + sampling_(sampling) { + set_layer_can_inherit_opacity(true); +} void TextureLayer::Diff(DiffContext* context, const Layer* old_layer) { DiffContext::AutoSubtreeRestore subtree(context); @@ -58,8 +60,9 @@ void TextureLayer::Paint(PaintContext& context) const { TRACE_EVENT_INSTANT0("flutter", "null texture"); return; } + AutoCachePaint cache_paint(context); texture->Paint(*context.leaf_nodes_canvas, paint_bounds(), freeze_, - context.gr_context, sampling_); + context.gr_context, sampling_, cache_paint.paint()); } } // namespace flutter diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 60b1737add8dd..68cf4e8e3192f 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -24,6 +24,7 @@ TransformLayer::TransformLayer(const SkMatrix& transform) FML_LOG(ERROR) << "TransformLayer is constructed with an invalid matrix."; transform_.setIdentity(); } + set_layer_can_inherit_opacity(true); } void TransformLayer::Diff(DiffContext* context, const Layer* old_layer) { diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 110974b4bca65..0d95c17af5b38 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -341,7 +341,9 @@ void RasterCache::Touch(DisplayList* display_list, } } -bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { +bool RasterCache::Draw(const SkPicture& picture, + SkCanvas& canvas, + const SkPaint* paint) const { PictureRasterCacheKey cache_key(picture.uniqueID(), canvas.getTotalMatrix()); auto it = picture_cache_.find(cache_key); if (it == picture_cache_.end()) { @@ -353,7 +355,7 @@ bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { entry.used_this_frame = true; if (entry.image) { - entry.image->draw(canvas, nullptr); + entry.image->draw(canvas, paint); return true; } @@ -361,7 +363,8 @@ bool RasterCache::Draw(const SkPicture& picture, SkCanvas& canvas) const { } bool RasterCache::Draw(const DisplayList& display_list, - SkCanvas& canvas) const { + SkCanvas& canvas, + const SkPaint* paint) const { DisplayListRasterCacheKey cache_key(display_list.unique_id(), canvas.getTotalMatrix()); auto it = display_list_cache_.find(cache_key); @@ -374,7 +377,7 @@ bool RasterCache::Draw(const DisplayList& display_list, entry.used_this_frame = true; if (entry.image) { - entry.image->draw(canvas, nullptr); + entry.image->draw(canvas, paint); return true; } @@ -383,7 +386,7 @@ bool RasterCache::Draw(const DisplayList& display_list, bool RasterCache::Draw(const Layer* layer, SkCanvas& canvas, - SkPaint* paint) const { + const SkPaint* paint) const { LayerRasterCacheKey cache_key(layer->unique_id(), canvas.getTotalMatrix()); auto it = layer_cache_.find(cache_key); if (it == layer_cache_.end()) { diff --git a/flow/raster_cache.h b/flow/raster_cache.h index 2dc052bab67ea..bcc88cbec88f3 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -205,12 +205,16 @@ class RasterCache { // Find the raster cache for the picture and draw it to the canvas. // // Return true if it's found and drawn. - bool Draw(const SkPicture& picture, SkCanvas& canvas) const; + bool Draw(const SkPicture& picture, + SkCanvas& canvas, + const SkPaint* paint = nullptr) const; // Find the raster cache for the display list and draw it to the canvas. // // Return true if it's found and drawn. - bool Draw(const DisplayList& display_list, SkCanvas& canvas) const; + bool Draw(const DisplayList& display_list, + SkCanvas& canvas, + const SkPaint* paint = nullptr) const; // Find the raster cache for the layer and draw it to the canvas. // @@ -220,7 +224,7 @@ class RasterCache { // Return true if the layer raster cache is found and drawn. bool Draw(const Layer* layer, SkCanvas& canvas, - SkPaint* paint = nullptr) const; + const SkPaint* paint = nullptr) const; void PrepareNewFrame(); void CleanupAfterFrame(); diff --git a/flow/testing/mock_texture.cc b/flow/testing/mock_texture.cc index cb5b92c8caaf8..6808bbf6fcc61 100644 --- a/flow/testing/mock_texture.cc +++ b/flow/testing/mock_texture.cc @@ -13,21 +13,22 @@ void MockTexture::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) { + const SkSamplingOptions& sampling, + const SkPaint* paint) { paint_calls_.emplace_back( - PaintCall{canvas, bounds, freeze, context, sampling}); + PaintCall{canvas, bounds, freeze, context, sampling, paint}); } bool operator==(const MockTexture::PaintCall& a, const MockTexture::PaintCall& b) { return &a.canvas == &b.canvas && a.bounds == b.bounds && a.context == b.context && a.freeze == b.freeze && - a.sampling == b.sampling; + a.sampling == b.sampling && a.paint == b.paint; } std::ostream& operator<<(std::ostream& os, const MockTexture::PaintCall& data) { return os << &data.canvas << " " << data.bounds << " " << data.context << " " - << data.freeze << " " << data.sampling; + << data.freeze << " " << data.sampling << " " << data.paint; } } // namespace testing diff --git a/flow/testing/mock_texture.h b/flow/testing/mock_texture.h index 09ce4c65422fb..ee4ba6d460a61 100644 --- a/flow/testing/mock_texture.h +++ b/flow/testing/mock_texture.h @@ -22,6 +22,7 @@ class MockTexture : public Texture { bool freeze; GrDirectContext* context; SkSamplingOptions sampling; + const SkPaint* paint; }; explicit MockTexture(int64_t textureId); @@ -31,7 +32,8 @@ class MockTexture : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) override; + const SkSamplingOptions& sampling, + const SkPaint* paint = nullptr) override; void OnGrContextCreated() override { gr_context_created_ = true; } void OnGrContextDestroyed() override { gr_context_destroyed_ = true; } diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 409ee2c327ecf..eb57ea7eaa966 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1719,7 +1719,8 @@ class MockTexture : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions&) override {} + const SkSamplingOptions&, + const SkPaint* paint) override {} void OnGrContextCreated() override {} diff --git a/shell/platform/android/android_external_texture_gl.cc b/shell/platform/android/android_external_texture_gl.cc index 3dd85946b4c1a..1dcaf848fa6d0 100644 --- a/shell/platform/android/android_external_texture_gl.cc +++ b/shell/platform/android/android_external_texture_gl.cc @@ -38,7 +38,8 @@ void AndroidExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) { + const SkSamplingOptions& sampling, + const SkPaint* paint) { if (state_ == AttachmentState::detached) { return; } @@ -69,7 +70,7 @@ void AndroidExternalTextureGL::Paint(SkCanvas& canvas, transformAroundCenter.postTranslate(0.5, 0.5); canvas.concat(transformAroundCenter); } - canvas.drawImage(image, 0, 0, sampling, nullptr); + canvas.drawImage(image, 0, 0, sampling, paint); } } diff --git a/shell/platform/android/android_external_texture_gl.h b/shell/platform/android/android_external_texture_gl.h index cbf24f11490bf..c9fc07fe6908f 100644 --- a/shell/platform/android/android_external_texture_gl.h +++ b/shell/platform/android/android_external_texture_gl.h @@ -25,7 +25,8 @@ class AndroidExternalTextureGL : public flutter::Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) override; + const SkSamplingOptions& sampling, + const SkPaint* paint) override; void OnGrContextCreated() override; diff --git a/shell/platform/embedder/embedder_external_texture_gl.cc b/shell/platform/embedder/embedder_external_texture_gl.cc index 55efc4bbc85cc..249f8d4f9a99d 100644 --- a/shell/platform/embedder/embedder_external_texture_gl.cc +++ b/shell/platform/embedder/embedder_external_texture_gl.cc @@ -26,7 +26,8 @@ void EmbedderExternalTextureGL::Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) { + const SkSamplingOptions& sampling, + const SkPaint* paint) { if (last_image_ == nullptr) { last_image_ = ResolveTexture(Id(), // @@ -37,9 +38,9 @@ void EmbedderExternalTextureGL::Paint(SkCanvas& canvas, if (last_image_) { if (bounds != SkRect::Make(last_image_->bounds())) { - canvas.drawImageRect(last_image_, bounds, sampling); + canvas.drawImageRect(last_image_, bounds, sampling, paint); } else { - canvas.drawImage(last_image_, bounds.x(), bounds.y(), sampling, nullptr); + canvas.drawImage(last_image_, bounds.x(), bounds.y(), sampling, paint); } } } diff --git a/shell/platform/embedder/embedder_external_texture_gl.h b/shell/platform/embedder/embedder_external_texture_gl.h index f1373405aec98..887a0e50fe026 100644 --- a/shell/platform/embedder/embedder_external_texture_gl.h +++ b/shell/platform/embedder/embedder_external_texture_gl.h @@ -36,7 +36,8 @@ class EmbedderExternalTextureGL : public flutter::Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) override; + const SkSamplingOptions& sampling, + const SkPaint* paint) override; // |flutter::Texture| void OnGrContextCreated() override; diff --git a/shell/platform/embedder/embedder_external_texture_metal.h b/shell/platform/embedder/embedder_external_texture_metal.h index fb34a13eade49..0587aa0f69ec5 100644 --- a/shell/platform/embedder/embedder_external_texture_metal.h +++ b/shell/platform/embedder/embedder_external_texture_metal.h @@ -36,7 +36,8 @@ class EmbedderExternalTextureMetal : public flutter::Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) override; + const SkSamplingOptions& sampling, + const SkPaint* paint) override; // |flutter::Texture| void OnGrContextCreated() override; diff --git a/shell/platform/embedder/embedder_external_texture_metal.mm b/shell/platform/embedder/embedder_external_texture_metal.mm index c2ac41f6cb7c1..4c880dc8b8aba 100644 --- a/shell/platform/embedder/embedder_external_texture_metal.mm +++ b/shell/platform/embedder/embedder_external_texture_metal.mm @@ -35,16 +35,17 @@ static bool ValidNumTextures(int expected, int actual) { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) { + const SkSamplingOptions& sampling, + const SkPaint* paint) { if (last_image_ == nullptr) { last_image_ = ResolveTexture(Id(), context, SkISize::Make(bounds.width(), bounds.height())); } if (last_image_) { if (bounds != SkRect::Make(last_image_->bounds())) { - canvas.drawImageRect(last_image_, bounds, sampling); + canvas.drawImageRect(last_image_, bounds, sampling, paint); } else { - canvas.drawImage(last_image_, bounds.x(), bounds.y(), sampling, nullptr); + canvas.drawImage(last_image_, bounds.x(), bounds.y(), sampling, paint); } } } From 57dbc16c7bc9dc020a62889b76eb05cee76408ab Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 22 Nov 2021 00:26:23 -0800 Subject: [PATCH 04/13] save/restore extra_alpha and prevent test pictures from triggering optimization --- flow/display_list_canvas.cc | 3 +++ flow/display_list_utils.cc | 20 ++++++++++++++++++++ flow/display_list_utils.h | 19 +++++++++++++++++-- shell/platform/embedder/fixtures/main.dart | 2 ++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index 813a333d39d55..bc86a510ef054 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -26,14 +26,17 @@ const SkPaint* DisplayListCanvasDispatcher::safe_paint(bool use_attributes) { void DisplayListCanvasDispatcher::save() { canvas_->save(); + save_extra_alpha(false); } void DisplayListCanvasDispatcher::restore() { canvas_->restore(); + restore_extra_alpha(); } void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds, bool restore_with_paint) { TRACE_EVENT0("flutter", "Canvas::saveLayer"); canvas_->saveLayer(bounds, safe_paint(restore_with_paint)); + save_extra_alpha(true); } void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) { diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index f4f5f85285333..341138a40742e 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -26,6 +26,25 @@ constexpr float invert_color_matrix[20] = { }; // clang-format on +void SkPaintDispatchHelper::save_extra_alpha(bool reset_and_restore) { + if (extra_alpha_ >= SK_AlphaOPAQUE) { + reset_and_restore = false; + } + save_stack_.emplace_back(extra_alpha_, reset_and_restore); + if (reset_and_restore) { + extra_alpha_ = SK_AlphaOPAQUE; + setColor(current_color_); + } +} +void SkPaintDispatchHelper::restore_extra_alpha() { + SaveInfo& info = save_stack_.back(); + if (info.restore_alpha) { + extra_alpha_ = info.extra_alpha; + setColor(current_color_); + } + save_stack_.pop_back(); +} + void SkPaintDispatchHelper::setAntiAlias(bool aa) { paint_.setAntiAlias(aa); } @@ -52,6 +71,7 @@ void SkPaintDispatchHelper::setStrokeMiter(SkScalar limit) { paint_.setStrokeMiter(limit); } void SkPaintDispatchHelper::setColor(SkColor color) { + current_color_ = color; if (extra_alpha_ < SK_AlphaOPAQUE) { SkAlpha alpha = (SkColorGetA(color) * extra_alpha_ + SK_AlphaOPAQUE / 2) / SK_AlphaOPAQUE; diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 0c0bdf27eacdd..48baadb779dcc 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -97,7 +97,7 @@ class IgnoreTransformDispatchHelper : public virtual Dispatcher { class SkPaintDispatchHelper : public virtual Dispatcher { public: SkPaintDispatchHelper(SkAlpha extra_alpha = SK_AlphaOPAQUE) - : extra_alpha_(extra_alpha) { + : current_color_(SK_ColorBLACK), extra_alpha_(extra_alpha) { if (extra_alpha < SK_AlphaOPAQUE) { paint_.setAlpha(extra_alpha); } @@ -124,13 +124,28 @@ class SkPaintDispatchHelper : public virtual Dispatcher { const SkPaint& paint() { return paint_; } SkAlpha extra_alpha() { return extra_alpha_; } + protected: + void save_extra_alpha(bool reset_and_restore); + void restore_extra_alpha(); + private: SkPaint paint_; - SkAlpha extra_alpha_; bool invert_colors_ = false; sk_sp color_filter_; sk_sp makeColorFilter(); + + struct SaveInfo { + SaveInfo(SkAlpha extra_alpha, bool restore_alpha) + : extra_alpha(extra_alpha), restore_alpha(restore_alpha) {} + + SkAlpha extra_alpha; + bool restore_alpha; + }; + std::vector save_stack_; + + SkColor current_color_; + SkAlpha extra_alpha_; }; class SkMatrixSource { diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index 5196f37727597..39c6a471bff3a 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -301,9 +301,11 @@ void null_platform_messages() { Picture CreateSimplePicture() { Paint blackPaint = Paint(); + Paint whitePaint = Paint()..color = Color.fromARGB(255, 255, 255, 255); PictureRecorder baseRecorder = PictureRecorder(); Canvas canvas = Canvas(baseRecorder); canvas.drawRect(Rect.fromLTRB(0.0, 0.0, 1000.0, 1000.0), blackPaint); + canvas.drawRect(Rect.fromLTRB(10.0, 10.0, 990.0, 990.0), whitePaint); return baseRecorder.endRecording(); } From 597d6107e626caaff7ae1fad30844a47f8f5ddf8 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 24 Nov 2021 18:01:46 -0800 Subject: [PATCH 05/13] switch from alpha to opacity, fix IOS overrides, fix FRACT handling in opacity layer --- flow/display_list.cc | 4 +-- flow/display_list.h | 4 +-- flow/display_list_canvas.cc | 12 ++++----- flow/display_list_canvas.h | 4 +-- flow/display_list_utils.cc | 22 ++++++++-------- flow/display_list_utils.h | 25 ++++++++++--------- flow/layers/layer.h | 18 ++++++------- flow/layers/opacity_layer.cc | 17 ++++++------- flow/layers/opacity_layer.h | 2 ++ flow/layers/picture_layer.cc | 2 +- .../FlutterDarwinExternalTextureMetal.h | 11 ++++---- .../FlutterDarwinExternalTextureMetal.mm | 13 +++++----- .../darwin/ios/ios_external_texture_gl.h | 3 ++- .../darwin/ios/ios_external_texture_gl.mm | 5 ++-- .../darwin/ios/ios_external_texture_metal.h | 3 ++- .../darwin/ios/ios_external_texture_metal.mm | 14 ++++++----- 16 files changed, 82 insertions(+), 77 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index beea2dc8aca9e..ec5c446ac587f 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -978,8 +978,8 @@ static bool CompareOps(uint8_t* ptrA, return true; } -void DisplayList::RenderTo(SkCanvas* canvas, SkAlpha extra_alpha) const { - DisplayListCanvasDispatcher dispatcher(canvas, extra_alpha); +void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity) const { + DisplayListCanvasDispatcher dispatcher(canvas, opacity); Dispatch(dispatcher); } diff --git a/flow/display_list.h b/flow/display_list.h index 8b711180868a6..538dbdc1d5846 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -179,7 +179,7 @@ class DisplayList : public SkRefCnt { Dispatch(ctx, ptr, ptr + byte_count_); } - void RenderTo(SkCanvas* canvas, SkAlpha extra_alpha = SK_AlphaOPAQUE) const; + void RenderTo(SkCanvas* canvas, SkScalar opacity = SK_Scalar1) const; // SkPicture always includes nested bytes, but nested ops are // only included if requested. The defaults used here for these @@ -551,7 +551,7 @@ class DisplayListFlags { // both of the following flags are set to indicate that // a default paint object can be constructed when rendering // the op to carry information imposed from outside the - // DisplayList (for example, the extra_alpha override). + // DisplayList (for example, the opacity override). static constexpr int kHasOptionalPaint_ = 1 << 29; static constexpr int kIgnoresPaint_ = 1 << 30; // clang-format on diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index bc86a510ef054..de71177199fa8 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -16,8 +16,8 @@ const SkPaint* DisplayListCanvasDispatcher::safe_paint(bool use_attributes) { // The accumulated SkPaint object will already have incorporated // any attribute overrides. return &paint(); - } else if (extra_alpha() < SK_AlphaOPAQUE) { - temp_paint_.setAlpha(extra_alpha()); + } else if (has_opacity()) { + temp_paint_.setAlphaf(opacity()); return &temp_paint_; } else { return nullptr; @@ -26,17 +26,17 @@ const SkPaint* DisplayListCanvasDispatcher::safe_paint(bool use_attributes) { void DisplayListCanvasDispatcher::save() { canvas_->save(); - save_extra_alpha(false); + save_opacity(false); } void DisplayListCanvasDispatcher::restore() { canvas_->restore(); - restore_extra_alpha(); + restore_opacity(); } void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds, bool restore_with_paint) { TRACE_EVENT0("flutter", "Canvas::saveLayer"); canvas_->saveLayer(bounds, safe_paint(restore_with_paint)); - save_extra_alpha(true); + save_opacity(true); } void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) { @@ -206,7 +206,7 @@ void DisplayListCanvasDispatcher::drawDisplayList( const sk_sp display_list) { int save_count = canvas_->save(); { - DisplayListCanvasDispatcher dispatcher(canvas_, extra_alpha()); + DisplayListCanvasDispatcher dispatcher(canvas_, opacity()); display_list->Dispatch(dispatcher); } canvas_->restoreToCount(save_count); diff --git a/flow/display_list_canvas.h b/flow/display_list_canvas.h index 950985777a483..fd813e777a880 100644 --- a/flow/display_list_canvas.h +++ b/flow/display_list_canvas.h @@ -28,8 +28,8 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher, public SkPaintDispatchHelper { public: explicit DisplayListCanvasDispatcher(SkCanvas* canvas, - SkAlpha extra_alpha = SK_AlphaOPAQUE) - : SkPaintDispatchHelper(extra_alpha), canvas_(canvas) {} + SkScalar opacity = SK_Scalar1) + : SkPaintDispatchHelper(opacity), canvas_(canvas) {} const SkPaint* safe_paint(bool use_attributes); diff --git a/flow/display_list_utils.cc b/flow/display_list_utils.cc index 341138a40742e..6fcf72cd27870 100644 --- a/flow/display_list_utils.cc +++ b/flow/display_list_utils.cc @@ -26,20 +26,20 @@ constexpr float invert_color_matrix[20] = { }; // clang-format on -void SkPaintDispatchHelper::save_extra_alpha(bool reset_and_restore) { - if (extra_alpha_ >= SK_AlphaOPAQUE) { +void SkPaintDispatchHelper::save_opacity(bool reset_and_restore) { + if (opacity_ >= SK_Scalar1) { reset_and_restore = false; } - save_stack_.emplace_back(extra_alpha_, reset_and_restore); + save_stack_.emplace_back(opacity_, reset_and_restore); if (reset_and_restore) { - extra_alpha_ = SK_AlphaOPAQUE; + opacity_ = SK_Scalar1; setColor(current_color_); } } -void SkPaintDispatchHelper::restore_extra_alpha() { +void SkPaintDispatchHelper::restore_opacity() { SaveInfo& info = save_stack_.back(); - if (info.restore_alpha) { - extra_alpha_ = info.extra_alpha; + if (info.restore_opacity) { + opacity_ = info.opacity; setColor(current_color_); } save_stack_.pop_back(); @@ -72,12 +72,10 @@ void SkPaintDispatchHelper::setStrokeMiter(SkScalar limit) { } void SkPaintDispatchHelper::setColor(SkColor color) { current_color_ = color; - if (extra_alpha_ < SK_AlphaOPAQUE) { - SkAlpha alpha = (SkColorGetA(color) * extra_alpha_ + SK_AlphaOPAQUE / 2) / - SK_AlphaOPAQUE; - color = SkColorSetA(color, alpha); - } paint_.setColor(color); + if (has_opacity()) { + paint_.setAlphaf(paint_.getAlphaf() * opacity()); + } } void SkPaintDispatchHelper::setBlendMode(SkBlendMode mode) { paint_.setBlendMode(mode); diff --git a/flow/display_list_utils.h b/flow/display_list_utils.h index 48baadb779dcc..434c82dab0be8 100644 --- a/flow/display_list_utils.h +++ b/flow/display_list_utils.h @@ -96,10 +96,10 @@ class IgnoreTransformDispatchHelper : public virtual Dispatcher { // which can be accessed at any time via paint(). class SkPaintDispatchHelper : public virtual Dispatcher { public: - SkPaintDispatchHelper(SkAlpha extra_alpha = SK_AlphaOPAQUE) - : current_color_(SK_ColorBLACK), extra_alpha_(extra_alpha) { - if (extra_alpha < SK_AlphaOPAQUE) { - paint_.setAlpha(extra_alpha); + SkPaintDispatchHelper(SkScalar opacity = SK_Scalar1) + : current_color_(SK_ColorBLACK), opacity_(opacity) { + if (opacity < SK_Scalar1) { + paint_.setAlphaf(opacity); } } @@ -122,11 +122,12 @@ class SkPaintDispatchHelper : public virtual Dispatcher { void setImageFilter(sk_sp filter) override; const SkPaint& paint() { return paint_; } - SkAlpha extra_alpha() { return extra_alpha_; } + SkScalar opacity() { return opacity_; } + bool has_opacity() { return opacity_ < SK_Scalar1; } protected: - void save_extra_alpha(bool reset_and_restore); - void restore_extra_alpha(); + void save_opacity(bool reset_and_restore); + void restore_opacity(); private: SkPaint paint_; @@ -136,16 +137,16 @@ class SkPaintDispatchHelper : public virtual Dispatcher { sk_sp makeColorFilter(); struct SaveInfo { - SaveInfo(SkAlpha extra_alpha, bool restore_alpha) - : extra_alpha(extra_alpha), restore_alpha(restore_alpha) {} + SaveInfo(SkScalar opacity, bool restore_opacity) + : opacity(opacity), restore_opacity(restore_opacity) {} - SkAlpha extra_alpha; - bool restore_alpha; + SkScalar opacity; + bool restore_opacity; }; std::vector save_stack_; SkColor current_color_; - SkAlpha extra_alpha_; + SkScalar opacity_; }; class SkMatrixSource { diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 6eb308fa21e5c..f2e9478f01836 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -170,25 +170,23 @@ class Layer { // The following value should be used to modulate the opacity of the // layer during |Paint|. If the layer does not set the corresponding // |layer_can_inherit_opacity()| flag, then this value should always - // be |SK_AlphaOPAQUE|. The value is supplied using the |SkAlpha| type - // but the name is meant to clearly indicate that it needs to be - // interpreted by the |Paint| method as specifically modulating the - // opacity of the resulting operations as if by using a |saveLayer| - // with this alpha value and a |kSrcOver| blend mode in the |SkPaint|. - SkAlpha inherited_opacity = SK_AlphaOPAQUE; + // be |SK_Scalar1|. The value is to be applied as if by using a + // |saveLayer| with an |SkPaint| initialized to this alphaf value and + // a |kSrcOver| blend mode. + SkScalar inherited_opacity = SK_Scalar1; }; class AutoCachePaint { public: AutoCachePaint(PaintContext& context) : context_(context) { - needs_paint_ = context.inherited_opacity < SK_AlphaOPAQUE; + needs_paint_ = context.inherited_opacity < SK_Scalar1; if (needs_paint_) { - paint_.setAlpha(context.inherited_opacity); - context.inherited_opacity = SK_AlphaOPAQUE; + paint_.setAlphaf(context.inherited_opacity); + context.inherited_opacity = SK_Scalar1; } } - ~AutoCachePaint() { context_.inherited_opacity = paint_.getAlpha(); } + ~AutoCachePaint() { context_.inherited_opacity = paint_.getAlphaf(); } const SkPaint* paint() { return needs_paint_ ? &paint_ : nullptr; } diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index b9ad45182e7c4..0eb47622cd846 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -77,9 +77,13 @@ void OpacityLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); - SkAlpha inherited_opacity = context.inherited_opacity; - SkAlpha subtree_opacity = - (alpha_ * inherited_opacity + SK_AlphaOPAQUE / 2) / SK_AlphaOPAQUE; +#ifndef SUPPORT_FRACTIONAL_TRANSLATION + context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( + context.leaf_nodes_canvas->getTotalMatrix())); +#endif + + SkScalar inherited_opacity = context.inherited_opacity; + SkScalar subtree_opacity = opacity() * inherited_opacity; if (children_can_accept_opacity()) { context.inherited_opacity = subtree_opacity; @@ -89,12 +93,7 @@ void OpacityLayer::Paint(PaintContext& context) const { } SkPaint paint; - paint.setAlpha(subtree_opacity); - -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( - context.leaf_nodes_canvas->getTotalMatrix())); -#endif + paint.setAlphaf(subtree_opacity); if (context.raster_cache && context.raster_cache->Draw(GetCacheableChild(), diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index e67c361b7f9ca..512d33c3f32a2 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -43,6 +43,8 @@ class OpacityLayer : public MergedContainerLayer { children_can_accept_opacity_ = value; } + SkScalar opacity() const { return alpha_ * 1.0 / SK_AlphaOPAQUE; } + private: SkAlpha alpha_; SkPoint offset_; diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 90d15b1a88fc7..f93437d993bdf 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -151,7 +151,7 @@ void PictureLayer::Paint(PaintContext& context) const { } } - FML_DCHECK(context.inherited_opacity == SK_AlphaOPAQUE); + FML_DCHECK(context.inherited_opacity == SK_Scalar1); picture()->playback(context.leaf_nodes_canvas); } diff --git a/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.h b/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.h index beadd1dfea0d1..042aec510abd8 100644 --- a/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.h +++ b/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.h @@ -30,11 +30,12 @@ textureID:(int64_t)textureID texture:(nonnull NSObject*)texture; -- (void)paint:(SkCanvas&)canvas - bounds:(const SkRect&)bounds - freeze:(BOOL)freeze - grContext:(nonnull GrDirectContext*)grContext - sampling:(const SkSamplingOptions&)sampling; +- (void)canvas:(SkCanvas&)canvas + bounds:(const SkRect&)bounds + freeze:(BOOL)freeze + grContext:(nonnull GrDirectContext*)grContext + sampling:(const SkSamplingOptions&)sampling + paint:(nullable const SkPaint*)paint; - (void)onGrContextCreated; diff --git a/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm b/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm index c789b1f3225e3..8d773a77337ad 100644 --- a/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm +++ b/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.mm @@ -46,11 +46,12 @@ - (void)dealloc { } } -- (void)paint:(SkCanvas&)canvas - bounds:(const SkRect&)bounds - freeze:(BOOL)freeze - grContext:(nonnull GrDirectContext*)grContext - sampling:(const SkSamplingOptions&)sampling { +- (void)canvas:(SkCanvas&)canvas + bounds:(const SkRect&)bounds + freeze:(BOOL)freeze + grContext:(nonnull GrDirectContext*)grContext + sampling:(const SkSamplingOptions&)sampling + paint:(nullable const SkPaint*)paint { const bool needsUpdatedTexture = (!freeze && _textureFrameAvailable) || !_externalImage; if (needsUpdatedTexture) { @@ -62,7 +63,7 @@ - (void)paint:(SkCanvas&)canvas SkRect::Make(_externalImage->bounds()), // source rect bounds, // destination rect sampling, // sampling - nullptr, // paint + paint, // paint SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint // constraint ); } diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.h b/shell/platform/darwin/ios/ios_external_texture_gl.h index 9e54dba5d246d..5318eb33a554f 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.h +++ b/shell/platform/darwin/ios/ios_external_texture_gl.h @@ -37,7 +37,8 @@ class IOSExternalTextureGL final : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) override; + const SkSamplingOptions& sampling, + const SkPaint* paint) override; // |Texture| void OnGrContextCreated() override; diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.mm b/shell/platform/darwin/ios/ios_external_texture_gl.mm index fd3e0d48aa303..e4f0b29947e73 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.mm +++ b/shell/platform/darwin/ios/ios_external_texture_gl.mm @@ -144,7 +144,8 @@ SkYUVAInfo yuvaInfo(textures[0].dimensions(), SkYUVAInfo::PlaneConfig::kY_UV, const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) { + const SkSamplingOptions& sampling, + const SkPaint* paint) { EnsureTextureCacheExists(); if (NeedUpdateTexture(freeze)) { auto pixelBuffer = [external_texture_.get() copyPixelBuffer]; @@ -169,7 +170,7 @@ SkYUVAInfo yuvaInfo(textures[0].dimensions(), SkYUVAInfo::PlaneConfig::kY_UV, FML_DCHECK(image) << "Failed to create SkImage from Texture."; if (image) { - canvas.drawImage(image, bounds.x(), bounds.y(), sampling, nullptr); + canvas.drawImage(image, bounds.x(), bounds.y(), sampling, paint); } } diff --git a/shell/platform/darwin/ios/ios_external_texture_metal.h b/shell/platform/darwin/ios/ios_external_texture_metal.h index 8aab36c676d2a..158497d336329 100644 --- a/shell/platform/darwin/ios/ios_external_texture_metal.h +++ b/shell/platform/darwin/ios/ios_external_texture_metal.h @@ -30,7 +30,8 @@ class IOSExternalTextureMetal final : public Texture { const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) override; + const SkSamplingOptions& sampling, + const SkPaint* paint) override; // |Texture| void OnGrContextCreated() override; diff --git a/shell/platform/darwin/ios/ios_external_texture_metal.mm b/shell/platform/darwin/ios/ios_external_texture_metal.mm index 52dac7b3a4723..50d1405a084bf 100644 --- a/shell/platform/darwin/ios/ios_external_texture_metal.mm +++ b/shell/platform/darwin/ios/ios_external_texture_metal.mm @@ -17,12 +17,14 @@ const SkRect& bounds, bool freeze, GrDirectContext* context, - const SkSamplingOptions& sampling) { - [darwin_external_texture_metal_ paint:canvas - bounds:bounds - freeze:freeze - grContext:context - sampling:sampling]; + const SkSamplingOptions& sampling, + const SkPaint* paint) { + [darwin_external_texture_metal_ canvas:canvas + bounds:bounds + freeze:freeze + grContext:context + sampling:sampling + paint:paint]; } void IOSExternalTextureMetal::OnGrContextCreated() { From 77329a7b2713e2eb09e2a3a3756334a612e4d37d Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 25 Nov 2021 12:55:48 -0800 Subject: [PATCH 06/13] adjust alpha calculations in OpacityLayer tests --- flow/layers/opacity_layer_unittests.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 5fa6690425005..acb837cf77594 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -262,8 +262,8 @@ TEST_F(OpacityLayerTest, HalfTransparent) { EXPECT_EQ(mock_layer->parent_mutators(), std::vector({Mutator(layer_transform), Mutator(alpha_half)})); - const SkPaint opacity_paint = - SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha_half))); + SkPaint opacity_paint; + opacity_paint.setAlphaf(alpha_half * (1.0 / SK_AlphaOPAQUE)); SkRect opacity_bounds; expected_layer_bounds.makeOffset(-layer_offset.fX, -layer_offset.fY) .roundOut(&opacity_bounds); @@ -352,10 +352,10 @@ TEST_F(OpacityLayerTest, Nested) { // EXPECT_EQ(mock_layer3->parent_mutators(), // std::vector({Mutator(layer1_transform), Mutator(alpha1)})); - const SkPaint opacity1_paint = - SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha1))); - const SkPaint opacity2_paint = - SkPaint(SkColor4f::FromColor(SkColorSetA(SK_ColorBLACK, alpha2))); + SkPaint opacity1_paint; + opacity1_paint.setAlphaf(alpha1 * (1.0 / SK_AlphaOPAQUE)); + SkPaint opacity2_paint; + opacity2_paint.setAlphaf(alpha2 * (1.0 / SK_AlphaOPAQUE)); SkRect opacity1_bounds, opacity2_bounds; expected_layer1_bounds.makeOffset(-layer1_offset.fX, -layer1_offset.fY) .roundOut(&opacity1_bounds); From 85bba9ae8c64a304409c76137d6b9867284d0132 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Thu, 25 Nov 2021 13:31:48 -0800 Subject: [PATCH 07/13] update golden for slight difference in alpha calculations --- .../verifyb143464703_soft_noxform.png | Bin 6985 -> 6981 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png b/shell/platform/embedder/fixtures/verifyb143464703_soft_noxform.png index b89b38290b87c9adfd1b1ae13adbed4b86a577a2..38097bf97b876feffbb3010d5a2a0351e8886a9f 100644 GIT binary patch literal 6981 zcmeHKYg7|w-XAUkRk>~T*eJIuZB<@N+bouQpt^z{i&d&Y1>y?3X*ZQy$~7cG*&-qe ziaiPiVpZf&E~&DCgv$gM6-7v-0+CC&Logu|kS0SiOlF_Z?m3>b`=0ZDf0+-@GhhDa znfcA{HW$7K3$b0hc`XD%w);c(9)TcBaBCi6V+FQlhiluwX36|)|Ccu4%Cw2ig`myL z`}YQanO3TnpGn#B>GkgpE2Bs^=>4P;`i${P?4us*;~UQYGq|jkTG?DAJau#XNx0z4 zt|1=Rs8?C>mKGKj%3TTX#q*X&rN#j=<*}9@Ma)UBB;~LOqQ_NH(t?*}BD6*+(ZpVm zhAd-8WpGnUJ4wt^(+HJ11=C`*M#6}y3u@>VBeL0YORZ5_q*NvJV})r{km#v!#@r$Z;=) z&)}-pNWGJ2JYV?KNW40nyJpv7!+?lG2pym-#)*$JqhA@`|ExW+R$=^ny3qFvC5d)| zHS6ZXR6idVjpx5@@gOdHR)c8Rqs$6T6a-1=!ftfdg=Nc=rOgcOdOdtEd+MPe9PXqr z)xGSuEVE??0w$lZZS_|gPY$~IBHM^%W?4P@mckG_X%0Q7Yv-FVZE2-eaAG-%v@%;M zVOUfk9`wS@f9O2+aW=Awg8lzWBX_y(m6fI`T1Uyqrg{j9{dgGhoc8(#kN7G8EjnWT zG^!t-z_&E>dxMi^>XqgX!tJ7eKu>1$r*8CDs9I^-!+Fv`jwO^eTI&*>D(0ltD&hlB zXP@k;at3X|_?D&T0Xh98T<4pM>{3OJVm3Is(+u*KD#v>Z7k&8;La6>P9H-;UZ;(SJ zC;gInRoH{pEz>mVqk)`df*Nxh>lPQE`<-5dN# zGL0o`6+SotzN7gm(?#OQ8s;5y-FDO}?XQNfTpr5RsZ zU>c-mA3ah+A7h3VsX;oxCg?kxso-6Of-N~q#f){9hyGx5HE=8^?v#RVNFBl0eBQHE z2wJSLZ>;CjrLs2aa6$9DD4dAo-RJj`C6U;qpej4|349tbN?l9{zT0s8{AQG6so*h2 zFvm>zJ_LO*TB(IO&fX^0zbPlIIzSnTZ2NVR6Pjse!UXcbs@mE*I%=-s~SzICm*$ zH0DqtLz~cS$Vfu|+!-i2_G_RG)#=E;iORm_dnX5}9?xGl?7Fu;r&kfHW~;%A;q}B~ zyd`w%QfnZ+?w)T@7e_}ZT`j1T(a=p|YSo^fg641ic(Ky1%2sNqL{!Oslo#T~5ow|# z(mt*ng66)~&Wj8gbyc$-h^L=^xXJ=4W>nfu_+TY^iDqT|+Rs5B<)GVMs*i4fvizIz zu%^#4vLfzLTtDp)j8t1IBqVn<63asmxh=)2EBpVJMs9P&nv`xC-F&z*;05tys9F3r zl%+Zz;KYZ^C;y%^c|wZ+P#XNYMG(?7g8P>X<#h znooDF+JGmb?>8a>){YaL8K}VjsKbNt$&56h!@TGd$mzG7o2u z^wSbIpEz~Z;$WK~9Q}TGD2ILK37o>FWLKb`34?gP{4J`pmZC6s&O&!6@Mat(_$Ij#~Lg52fx7{`=jY=-2qAS;*LTx%3#x zdl1C@uj(1A%6=_1<5RI}Ag8KKn#?fig=M-LF0`jBm zk9Ji%M%NT(>^nPFB;dU^4xS^I`!3Gd-=YnrYtXrk0l-&}9<$CJ-~}%R?x7>E*4HXr zZYG5~Z`bC>pkDKvB9w)L^EF&0Tz>zVW<76#V@Y>}vcPi<^~s8u_Tjq3hJGgHYu>wF zF|}71l?X%WCkCQ2e^4YLYE_VSyuNmYQYCudbyG)KCjW@_ zFxliYOr7AVTd}Wy$B4L>UGY+1Z2Y(Ok+Tp8ZvY}h7+EFsVmAVW?YJFp6rHzI4>~1my6F>_q<4>oV#raxn+;1 z!5)Dag;^%|M&AYI-U*xPg8DKY--8OCO|A{sjQ+@5a33i+^#STqAw*4zY#cI-Cu_Cp z6B?RIqn?|>Ve!l0%o+H!1dOLk4VpI0(H&b#+yw%q)+Nr?ryyNNq_5n;i2yBgt$0U}3dDHjKgBq8qPaA277V4ChSDYm4-KR` z1YiT`VhxLO&R&*zAGE$20kD9=R-{IDQ!Cg>B6pb-jNp92OfaD)EYa7SHyt0FJ+V{?nL@Fp6|W(E@x)23Nvg^4z-n2+exl<>;_i6 zTVp13G)3N8;CCH@x{3maC2)BF&4QLlLH?5FS&Kw!xj;h8ijW+>cAJgI3snG&M~ln8pSJW z3^l2-fVJdYi+=Nq$SCe0LsjG?Achj%`W6Hw{w_wgu*IN<@4rg@%R{@X zZ<`tw$bE|kA7-a@!O>vKWgNG0(JLe4JK{!ufS}Sd+Ibt~+@J0>Z=+3VSof;d05_;%(II4|S27>j^(`@UWGL%Cb z5y6JpjZhZzovkyAb3`OO!HFCbg`0A!JCZR4c#vo6Fs89a@WpC^dojo1xJnKF;PD#h zlzsJisr-#l{JpNVlE#WokMCv;S@*jf=0b$X^a~N_z;}B?JYTnVGh=fSwTaMUZLDGB-DE8yrmQ%`DRj ztug)FLEGI->*|OJg;m4=2&;qvAgt=eq=!|z03fWorHKlwVF5r`%}M~mYE>}lVYMm% p5LT-K0AaN%nDp@fuquoZX3@R30~3T_3QY*?|5MoBy3bCX`xlosdqV&K literal 6985 zcmeI0`BzhC9>!k*fvPOlT7|M%9GB9%rN}0UsS9XZsWL(ZCDw&>OjR(3C1jz}5yT~8 zW!xa?Se0RbM8yn*K!QcYg~?TmM3xXGNHie{TN23S=FS^COV62^Gk?I4AMVXB-}9dP z+~@N=-{jaAn_@hs`A!1>cx;GWy9EFj_|@r4cUO3u@ID(1Z!YvtHvHBdepv2%3IKQy zY*1Na1*gWHQ~qzN=pSlS@s&Mp~hW@EjEz`$%S z_O8WBLbYnHMZ?3SJQ_CkvgVvb0WAM7zpQ@0Ssm*dHuy z%~X0uk4{HzL-x+;{PaEGJ?k-r6-*|HSjel)OJTdWsWkFt6t1R_AsX!Ppq{xv|yO*X;0rz&*swfX= z&uljyF^vnadY+M1vYXMc3`%)kUn}+L)FWkn^(##^qm`x)vJ}c3x$ehz1ndcS0OU#0 z^+pb6OhVL5W7Hwn`_W2SFE-0PAoUbUQhx}+vUM~t@?uZ-oe_urHCe5#_h$ua`gX7-@b+qAYPOWocD;8ardgiyv$ zuYb4Y^ZLZ{ygfQusxb7Bp^d%Q^_IT|5J-N?#75W8wvffWguoTC(7oG zVzVnXbxWa(E00?A)i56=HK=(Y2K zSFRbHYg}!E+G>-q+`ux5EiDRVT38;eW~_F)Aj*uG7o!`aHLYS&%B9HIqXQw969grH zoRS<{#Q0OXDJIP>>2C0lFYRb%5xs0iZ?KZXHOxlB8HP6U3I^o{ zwM1emqKQ5(NLgq9@f|NEXBNnP`Xs-rLrK7qFB1E@*N8T}dDuf$I~3uE;S@`ko6}6- z*IG1!f#l(yxu!A6Fww&}E7}F*7ALqBbO^{J#5Gjo>lszrfFQ={?w7nv<WCJ4$+DrHRhntiWi zzKYsLjQlYzmc6OHM=1-utt6ood_4f|&-CR8or9HJoh+qhgn&hvFuzQN`Fa0appl>8C8!W-FBZyr?Ki(iN+X~1B^kOPezoX= zX@c(=rncj9P0 zekEI^KalrIR3CUd5;}WH(-4b~AwOsJcYQS6{cD37R>W@d=ZnG%kZ$F&ZF#R0$f%&& z`j*zVOXCC{?KKb9*_f8cwxp%ROkGfRzbvCeyG^-i$0|l6`j|wys!{(4ncIlYydtqK zYEBe=3&7yHr%igDzDwLAZ>Ot^9QI8PitS8%kt=?vSA+lfo4`dVf24$^l&&5i{DQwEfpDVl2d|D6YJOzAt(40mK%290-k@d8#_Sx35 z8Ie$%3w5Fkw3FMrYKBR_>yvFk!s&*B)z|;dtq*StOe0Z}f6x=WnpvahYcCEi#@1*A24D+S0y(41Xg0@As&|#i||Fw zO=302J{LaGLUwH_J9-VyN@$Ygr~Dj#tE3srIIqjEq+e!Fa2>!rEH|^gmg7k+u^HzutZK^-9=}Xtb=h^6>uPt z5~qm%tfQp^HqI6H(Y6!f^+H%)<^P}~5)XTJhkW=%CH*SIQG~t?az}hV3`mBDkx;Ig z_ics16mm(Hy=x#SB7Oh2&(|%71>o@e*#f*zoyoQQA?&WSP^2(tymIi!`8$uS?S7fV z4IBN_5NHfP+9b!@1 z(lsgm*5}<3d3}5uWyt=P%TgiMbZJTe{V93$B89C+Hx~*5evW;+=0dig%gR^71xGa88!-O?(oRzA!mt zupRPb7zVai+y^DR2TOH?7E23Cp8z4S5%Sr;Yk_ry^>GnO$L zOKTH4u&xa6=#$pq@QqOXxu>+!-fEnn>^MmeJQEpvZ6L%9!`+zGPC87Ht`aUY+x=}m zalM#(#v#+@!W;*YY0|2|lthYJXts;_=&3h$bs{UUxJTnKKXE{pv*C}P>7vnyFIQJut`WAjDWPiEmLud>~~< zdhF*uy^y9l5pPR=+aq>p$2oL=sQM22WML~OIXGb z&vK{RG?yd4hpX4}-iit8L*MHHsRkAT->R*X-vnUUL){ByrrRD`19wh@b#{ImjPn!z zj*}u+g_R5?qjfq0_PU>IX7`uL^J#Jn+wB5&o*aFVVKLX{s~WG^np@5?91qcq1^*yVfxuK%P1%TdDDy|!%bzVLiHH@W3>WSq{Y#Tddk zJ;R?L?>5xNj^2At?L*XH?uz*UWdFJztF)Xa#5McwCIvg%k(jsV)nEw|zs5OthgF{U zN`F%kYQ)^Hah9F|z?;g*J&z6GPxBwXfsvEA9>Q3(dhjwdZm9Rbih`B_Jp)e7go{p* z1I2;<2B=;~f)`IpK|;rC!;O78TO*!(PDmn+6kX6_?>pV{>#LXDh1J7579f=sI)TPb zPn_=*Vg{Sf*jD4ybT38eKkGmj6tUcUVlXwgWjn*Wp+8Y=<(%y`@!p1YdV^W3!kDM% zA|73I#PxnUZMw@Gb~}kdFP=6z{*p+~96JmgqaP1*ZuMOYmmWigttarC?EqM=Gsn8If0g1tg8$MK;$n+G7FdN9 zK0?D-#a$nLB2(SiR%^LNE z{B#B0Qo)DA8B!Goz1w+qOoJzw0C0Uaj-1)UM2AL}&fVFU+1nTl z!w`O^!OjEVi`9;6u9K7F_73b00Dj1IT)|YwPdl(E$Z=ggWs2iAIRZtPR1AtRX&46& zle|C?CgZ|kg~_ypB21PA6k)O}9Xw2~3Q&Z}RRM}Hxhgn#m|PX02>*|(0`9d9|B+12 TJP-d#0bs+rO>2dp>^$-}t Date: Wed, 8 Dec 2021 15:00:55 -0800 Subject: [PATCH 08/13] prototype new tests and switching test surfaces to GL --- flow/BUILD.gn | 1 + flow/display_list.cc | 134 ++++++++------------ flow/display_list.h | 56 ++++++++- flow/display_list_canvas.cc | 11 +- flow/display_list_canvas_unittests.cc | 118 +++++++++++++++++- flow/display_list_unittests.cc | 168 ++++++++++++++++++++++++++ 6 files changed, 388 insertions(+), 100 deletions(-) diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 642541ee8dfc0..45ead55f9a73c 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -174,6 +174,7 @@ if (enable_unittests) { ":flow_testing", "//flutter/common/graphics", "//flutter/fml", + "//flutter/testing:opengl", "//flutter/testing:skia", "//flutter/testing:testing_lib", "//third_party/dart/runtime:libdart_jit", # for tracing diff --git a/flow/display_list.cc b/flow/display_list.cc index ec5c446ac587f..4856b7d102398 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -1079,7 +1079,7 @@ sk_sp DisplayListBuilder::Build() { used_ = allocated_ = op_count_ = 0; nested_bytes_ = nested_op_count_ = 0; storage_.realloc(bytes); - bool compatible = layer_stack_.back().compatible_with_group_opacity(); + bool compatible = layer_stack_.back().is_group_opacity_compatible(); return sk_sp(new DisplayList(storage_.release(), bytes, count, nested_bytes, nested_count, cull_rect_, compatible)); @@ -1088,6 +1088,7 @@ sk_sp DisplayListBuilder::Build() { DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect) : cull_rect_(cull_rect) { layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); } DisplayListBuilder::~DisplayListBuilder() { @@ -1105,6 +1106,7 @@ void DisplayListBuilder::onSetDither(bool dither) { } void DisplayListBuilder::onSetInvertColors(bool invert) { Push(0, 0, current_invert_colors_ = invert); + UpdateCurrentOpacityCompatibility(); } void DisplayListBuilder::onSetStrokeCap(SkPaint::Cap cap) { Push(0, 0, current_stroke_cap_ = cap); @@ -1127,6 +1129,7 @@ void DisplayListBuilder::onSetColor(SkColor color) { void DisplayListBuilder::onSetBlendMode(SkBlendMode mode) { current_blender_ = nullptr; Push(0, 0, current_blend_mode_ = mode); + UpdateCurrentOpacityCompatibility(); } void DisplayListBuilder::onSetBlender(sk_sp blender) { // setBlender(nullptr) should be redirected to setBlendMode(SrcOver) @@ -1141,6 +1144,7 @@ void DisplayListBuilder::onSetBlender(sk_sp blender) { (current_blender_ = blender) // ? Push(0, 0, std::move(blender)) : Push(0, 0); + UpdateCurrentOpacityCompatibility(); } } void DisplayListBuilder::onSetShader(sk_sp shader) { @@ -1157,6 +1161,7 @@ void DisplayListBuilder::onSetColorFilter(sk_sp filter) { (current_color_filter_ = filter) // ? Push(0, 0, std::move(filter)) : Push(0, 0); + UpdateCurrentOpacityCompatibility(); } void DisplayListBuilder::onSetPathEffect(sk_sp effect) { (current_path_effect_ = effect) // @@ -1242,41 +1247,10 @@ void DisplayListBuilder::setAttributesFromPaint( } } -void DisplayListBuilder::CheckOptimizations( - const DisplayListAttributeFlags& flags) { - LayerInfo& info = layer_stack_.back(); - if (flags.ignores_paint()) { - if (flags.supports_optional_paint()) { - info.add_compatible_op(); - } else { - info.mark_incompatible(); - } - return; - } - if (flags.applies_image_filter() && current_image_filter_) { - info.mark_incompatible(); - return; - } - if (flags.applies_color_filter() && - (current_color_filter_ || current_invert_colors_)) { - info.mark_incompatible(); - return; - } - if (flags.applies_blend() && - (current_blender_ || current_blend_mode_ != SkBlendMode::kSrcOver)) { - info.mark_incompatible(); - return; - } - if (!flags.applies_alpha_or_color()) { - info.mark_incompatible(); - return; - } - info.add_compatible_op(); -} - void DisplayListBuilder::save() { Push(0, 1); layer_stack_.emplace_back(); + current_layer_ = &layer_stack_.back(); } void DisplayListBuilder::restore() { if (layer_stack_.size() > 1) { @@ -1284,15 +1258,15 @@ void DisplayListBuilder::restore() { // on the stack. LayerInfo layer_info = layer_stack_.back(); layer_stack_.pop_back(); + current_layer_ = &layer_stack_.back(); Push(0, 1); if (!layer_info.has_layer) { // For regular save() ops there was no protecting layer so we have to // accumulate the values into the enclosing layer. - LayerInfo& info = layer_stack_.back(); if (layer_info.cannot_inherit_opacity) { - info.mark_incompatible(); + current_layer_->mark_incompatible(); } else if (layer_info.has_compatible_op) { - info.add_compatible_op(); + current_layer_->add_compatible_op(); } } } @@ -1302,9 +1276,9 @@ void DisplayListBuilder::saveLayer(const SkRect* bounds, bounds // ? Push(0, 1, *bounds, restore_with_paint) : Push(0, 1, restore_with_paint); - CheckOptimizations(restore_with_paint ? kSaveLayerFlags - : kSaveLayerWithPaintFlags); + CheckLayerOpacityCompatibility(restore_with_paint); layer_stack_.emplace_back(true); + current_layer_ = &layer_stack_.back(); } void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) { @@ -1418,31 +1392,27 @@ void DisplayListBuilder::clipPath(const SkPath& path, void DisplayListBuilder::drawPaint() { Push(0, 1); - CheckOptimizations(kDrawPaintFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawColor(SkColor color, SkBlendMode mode) { Push(0, 1, color, mode); - if (mode != SkBlendMode::kSrcOver) { - layer_stack_.back().mark_incompatible(); - } else { - layer_stack_.back().add_compatible_op(); - } + CheckLayerOpacityCompatibility(mode); } void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) { Push(0, 1, p0, p1); - CheckOptimizations(kDrawLineFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawRect(const SkRect& rect) { Push(0, 1, rect); - CheckOptimizations(kDrawRectFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawOval(const SkRect& bounds) { Push(0, 1, bounds); - CheckOptimizations(kDrawOvalFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) { Push(0, 1, center, radius); - CheckOptimizations(kDrawCircleFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawRRect(const SkRRect& rrect) { if (rrect.isRect()) { @@ -1451,17 +1421,17 @@ void DisplayListBuilder::drawRRect(const SkRRect& rrect) { drawOval(rrect.rect()); } else { Push(0, 1, rrect); - CheckOptimizations(kDrawRRectFlags); + CheckLayerOpacityCompatibility(); } } void DisplayListBuilder::drawDRRect(const SkRRect& outer, const SkRRect& inner) { Push(0, 1, outer, inner); - CheckOptimizations(kDrawDRRectFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawPath(const SkPath& path) { Push(0, 1, path); - CheckOptimizations(kDrawPathFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawArc(const SkRect& bounds, @@ -1469,8 +1439,7 @@ void DisplayListBuilder::drawArc(const SkRect& bounds, SkScalar sweep, bool useCenter) { Push(0, 1, bounds, start, sweep, useCenter); - CheckOptimizations(useCenter ? kDrawArcWithCenterFlags - : kDrawArcNoCenterFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, uint32_t count, @@ -1481,26 +1450,30 @@ void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, switch (mode) { case SkCanvas::PointMode::kPoints_PointMode: data_ptr = Push(bytes, 1, count); - CheckOptimizations(kDrawPointsAsPointsFlags); break; case SkCanvas::PointMode::kLines_PointMode: data_ptr = Push(bytes, 1, count); - CheckOptimizations(kDrawPointsAsLinesFlags); break; case SkCanvas::PointMode::kPolygon_PointMode: data_ptr = Push(bytes, 1, count); - CheckOptimizations(kDrawPointsAsPolygonFlags); break; default: FML_DCHECK(false); return; } CopyV(data_ptr, pts, count); + // drawPoints treats every point or line (or segment of a polygon) + // as a completely separate operation meaning we cannot ensure + // distribution of group opacity without analyzing the mode and the + // bounds of every sub-primitive. + UpdateLayerOpacityCompatibility(false); } void DisplayListBuilder::drawVertices(const sk_sp vertices, SkBlendMode mode) { Push(0, 1, std::move(vertices), mode); - CheckOptimizations(kDrawVerticesFlags); + // DrawVertices applies its colors to the paint so we have no way + // of controlling opacity using the current paint attributes. + UpdateLayerOpacityCompatibility(false); } void DisplayListBuilder::drawImage(const sk_sp image, @@ -1510,8 +1483,7 @@ void DisplayListBuilder::drawImage(const sk_sp image, render_with_attributes ? Push(0, 1, std::move(image), point, sampling) : Push(0, 1, std::move(image), point, sampling); - CheckOptimizations(render_with_attributes ? kDrawImageWithPaintFlags - : kDrawImageFlags); + CheckLayerOpacityCompatibility(render_with_attributes); } void DisplayListBuilder::drawImageRect(const sk_sp image, const SkRect& src, @@ -1521,8 +1493,7 @@ void DisplayListBuilder::drawImageRect(const sk_sp image, SkCanvas::SrcRectConstraint constraint) { Push(0, 1, std::move(image), src, dst, sampling, render_with_attributes, constraint); - CheckOptimizations(render_with_attributes ? kDrawImageRectWithPaintFlags - : kDrawImageRectFlags); + CheckLayerOpacityCompatibility(render_with_attributes); } void DisplayListBuilder::drawImageNine(const sk_sp image, const SkIRect& center, @@ -1533,8 +1504,7 @@ void DisplayListBuilder::drawImageNine(const sk_sp image, ? Push(0, 1, std::move(image), center, dst, filter) : Push(0, 1, std::move(image), center, dst, filter); - CheckOptimizations(render_with_attributes ? kDrawImageNineWithPaintFlags - : kDrawImageNineFlags); + CheckLayerOpacityCompatibility(render_with_attributes); } void DisplayListBuilder::drawImageLattice(const sk_sp image, const SkCanvas::Lattice& lattice, @@ -1556,8 +1526,7 @@ void DisplayListBuilder::drawImageLattice(const sk_sp image, filter, render_with_attributes); CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount, lattice.fColors, cellCount, lattice.fRectTypes, cellCount); - CheckOptimizations(render_with_attributes ? kDrawImageLatticeWithPaintFlags - : kDrawImageLatticeFlags); + CheckLayerOpacityCompatibility(render_with_attributes); } void DisplayListBuilder::drawAtlas(const sk_sp atlas, const SkRSXform xform[], @@ -1592,8 +1561,10 @@ void DisplayListBuilder::drawAtlas(const sk_sp atlas, } CopyV(data_ptr, xform, count, tex, count); } - CheckOptimizations(render_with_attributes ? kDrawAtlasWithPaintFlags - : kDrawAtlasFlags); + // drawAtlas treats each image as a separate operation so we cannot rely + // on it to distribute the opacity without overlap without checking all + // of the transforms and texture rectangles. + UpdateLayerOpacityCompatibility(false); } void DisplayListBuilder::drawPicture(const sk_sp picture, @@ -1611,8 +1582,7 @@ void DisplayListBuilder::drawPicture(const sk_sp picture, // This behavior is identical to the way SkPicture computes nested op counts. nested_op_count_ += picture->approximateOpCount(true) - 1; nested_bytes_ += picture->approximateBytesUsed(); - CheckOptimizations(render_with_attributes ? kDrawPictureWithPaintFlags - : kDrawPictureFlags); + CheckLayerOpacityCompatibility(render_with_attributes); } void DisplayListBuilder::drawDisplayList( const sk_sp display_list) { @@ -1625,17 +1595,13 @@ void DisplayListBuilder::drawDisplayList( // This behavior is identical to the way SkPicture computes nested op counts. nested_op_count_ += display_list->op_count(true) - 1; nested_bytes_ += display_list->bytes(true); - if (display_list->can_apply_group_opacity()) { - layer_stack_.back().add_compatible_op(); - } else { - layer_stack_.back().mark_incompatible(); - } + UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity()); } void DisplayListBuilder::drawTextBlob(const sk_sp blob, SkScalar x, SkScalar y) { Push(0, 1, std::move(blob), x, y); - CheckOptimizations(kDrawTextBlobFlags); + CheckLayerOpacityCompatibility(); } void DisplayListBuilder::drawShadow(const SkPath& path, const SkColor color, @@ -1645,7 +1611,7 @@ void DisplayListBuilder::drawShadow(const SkPath& path, transparent_occluder // ? Push(0, 1, path, color, elevation, dpr) : Push(0, 1, path, color, elevation, dpr); - CheckOptimizations(kDrawShadowFlags); + UpdateLayerOpacityCompatibility(false); } // clang-format off @@ -1680,7 +1646,7 @@ void DisplayListBuilder::drawShadow(const SkPath& path, // clang-format on const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerFlags = - DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kSaveLayerWithPaintFlags = DisplayListAttributeFlags(kIsNonGeometric_ | // @@ -1753,14 +1719,14 @@ const DisplayListAttributeFlags DisplayListOpFlags::kDrawVerticesFlags = kUsesImageFilter_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageFlags = - DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE | // kUsesAntiAlias_ | kUsesMaskFilter_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageRectFlags = - DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageRectWithPaintFlags = @@ -1768,33 +1734,33 @@ const DisplayListAttributeFlags kUsesAntiAlias_ | kUsesMaskFilter_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageNineFlags = - DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageNineWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageLatticeFlags = - DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawImageLatticeWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE); const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasFlags = - DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawAtlasWithPaintFlags = DisplayListAttributeFlags(IMAGE_FLAGS_BASE); const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureFlags = - DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawPictureWithPaintFlags = kSaveLayerWithPaintFlags; const DisplayListAttributeFlags DisplayListOpFlags::kDrawDisplayListFlags = - DisplayListAttributeFlags(kHasOptionalPaint_ | kIgnoresPaint_); + DisplayListAttributeFlags(kIgnoresPaint_); const DisplayListAttributeFlags DisplayListOpFlags::kDrawTextBlobFlags = DisplayListAttributeFlags(PAINT_FLAGS | STROKE_OR_FILL_FLAGS | diff --git a/flow/display_list.h b/flow/display_list.h index 538dbdc1d5846..05c344624d75e 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -552,7 +552,6 @@ class DisplayListFlags { // a default paint object can be constructed when rendering // the op to carry information imposed from outside the // DisplayList (for example, the opacity override). - static constexpr int kHasOptionalPaint_ = 1 << 29; static constexpr int kIgnoresPaint_ = 1 << 30; // clang-format on @@ -636,7 +635,6 @@ class DisplayListAttributeFlags : DisplayListFlagsBase { } bool ignores_paint() const { return has_any(kIgnoresPaint_); } - bool supports_optional_paint() const { return has_any(kHasOptionalPaint_); } bool applies_anti_alias() const { return has_any(kUsesAntiAlias_); } bool applies_dither() const { return has_any(kUsesDither_); } @@ -1013,9 +1011,7 @@ class DisplayListBuilder final : public virtual Dispatcher, bool cannot_inherit_opacity; bool has_compatible_op; - bool compatible_with_group_opacity() const { - return !cannot_inherit_opacity; - } + bool is_group_opacity_compatible() const { return !cannot_inherit_opacity; } void mark_incompatible() { cannot_inherit_opacity = true; } @@ -1036,8 +1032,56 @@ class DisplayListBuilder final : public virtual Dispatcher, }; std::vector layer_stack_; + LayerInfo* current_layer_; + + // This flag indicates whether or not the current rendering attributes + // are compatible with rendering ops applying an inherited opacity. + bool current_opacity_compatibility_ = true; + + // Returns the compatibility of a given blend mode for applying an + // inherited opacity value to modulate the visibility of the op. + // For now we only accept SrcOver blend modes but this could be expanded + // in the future to include other (rarely used) modes that also modulate + // the opacity of a rendering operation at the cost of a switch statement + // or lookup table. + static bool IsOpacityCompatible(SkBlendMode mode) { + return (mode == SkBlendMode::kSrcOver); + } + + void UpdateCurrentOpacityCompatibility() { + current_opacity_compatibility_ = + current_color_filter_ == nullptr && + !current_invert_colors_ && + current_blender_ == nullptr && + IsOpacityCompatible(current_blend_mode_); + } + + // Update the opacity compatibility flags of the current layer for an op + // that has determined its compatibility as indicated by |compatible|. + void UpdateLayerOpacityCompatibility(bool compatible) { + if (compatible) { + current_layer_->add_compatible_op(); + } else { + current_layer_->mark_incompatible(); + } + } - void CheckOptimizations(const DisplayListAttributeFlags& flags); + // Check for opacity compatibility for an op that may or may not use the + // current rendering attributes as indicated by |uses_blend_attribute|. + // If the flag is false then the rendering op will be able to substitute + // a default Paint object with the opacity applied using the default SrcOver + // blend mode which is always compatible with applying an inherited opacity. + void CheckLayerOpacityCompatibility(bool uses_blend_attribute = true) { + UpdateLayerOpacityCompatibility(!uses_blend_attribute || + current_opacity_compatibility_); + } + + // Check for opacity compatibility for an op that ignores the current + // attributes and uses the indicated blend |mode| to render to the layer. + // This is only used by |drawColor| currently. + void CheckLayerOpacityCompatibility(SkBlendMode mode) { + UpdateLayerOpacityCompatibility(IsOpacityCompatible(mode)); + } void onSetAntiAlias(bool aa); void onSetDither(bool dither); diff --git a/flow/display_list_canvas.cc b/flow/display_list_canvas.cc index de71177199fa8..eeda70d57270f 100644 --- a/flow/display_list_canvas.cc +++ b/flow/display_list_canvas.cc @@ -103,7 +103,11 @@ void DisplayListCanvasDispatcher::drawPaint() { canvas_->drawPaint(sk_paint); } void DisplayListCanvasDispatcher::drawColor(SkColor color, SkBlendMode mode) { - canvas_->drawColor(color, mode); + // SkCanvas::drawColor(SkColor) does the following conversion anyway + // We do it here manually to increase precision on applying opacity + SkColor4f color4f = SkColor4f::FromColor(color); + color4f.fA *= opacity(); + canvas_->drawColor(color4f, mode); } void DisplayListCanvasDispatcher::drawLine(const SkPoint& p0, const SkPoint& p1) { @@ -205,10 +209,7 @@ void DisplayListCanvasDispatcher::drawPicture(const sk_sp picture, void DisplayListCanvasDispatcher::drawDisplayList( const sk_sp display_list) { int save_count = canvas_->save(); - { - DisplayListCanvasDispatcher dispatcher(canvas_, opacity()); - display_list->Dispatch(dispatcher); - } + display_list->RenderTo(canvas_, opacity()); canvas_->restoreToCount(save_count); } void DisplayListCanvasDispatcher::drawTextBlob(const sk_sp blob, diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 4352c01008ad1..abd7bb363ae40 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/flow/display_list_canvas.h" #include "flutter/flow/layers/physical_shape_layer.h" +#include "flutter/testing/test_gl_surface.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkImageInfo.h" @@ -247,9 +248,15 @@ class RenderSurface { explicit RenderSurface(sk_sp surface) : surface_(surface) {} ~RenderSurface() { sk_free(addr_); } - SkCanvas* canvas() { return surface_->getCanvas(); } + SkCanvas* canvas() { + MakeCurrent(); + return surface_->getCanvas(); + } + + virtual void MakeCurrent() {} const SkPixmap* pixmap() { + surface_->flushAndSubmit(); if (!pixmap_.addr()) { SkImageInfo info = surface_->imageInfo(); if (info.colorType() != kN32_SkColorType || @@ -257,18 +264,51 @@ class RenderSurface { info = SkImageInfo::MakeN32Premul(info.dimensions()); addr_ = malloc(info.computeMinByteSize() * info.height()); pixmap_.reset(info, addr_, info.minRowBytes()); - EXPECT_TRUE(surface_->readPixels(pixmap_, 0, 0)); + if (!surface_->readPixels(pixmap_, 0, 0)) { + MakeCurrent(); + auto snapshot = surface_->makeImageSnapshot(); + EXPECT_NE(snapshot, nullptr); + auto image = snapshot->makeRasterImage(); + EXPECT_NE(image, nullptr); + EXPECT_TRUE(image->readPixels(pixmap_, 0, 0)); + } } } return &pixmap_; } + protected: + void set_surface(sk_sp surface) { surface_ = surface; } + private: sk_sp surface_; SkPixmap pixmap_; void* addr_ = nullptr; }; +class RenderGLSurface : public RenderSurface { + public: + explicit RenderGLSurface(int width, int height) + : RenderSurface(nullptr) { + SkISize size = SkISize::Make(width, height); + gl_surface_ = std::make_unique(size); + gl_surface_->MakeCurrent(); + context_ = gl_surface_->GetGrContext(); + set_surface(SkSurface::MakeRenderTarget(context_.get(), SkBudgeted::kNo, + SkImageInfo::MakeN32Premul(size), 1, + kTopLeft_GrSurfaceOrigin, nullptr, + false)); + } + + virtual void MakeCurrent() override { + gl_surface_->MakeCurrent(); + } + + private: + std::unique_ptr gl_surface_; + sk_sp context_; +}; + class RenderEnvironment { public: static RenderEnvironment Make565() { @@ -289,6 +329,14 @@ class RenderEnvironment { return RenderSurface(surface); } + RenderSurface MakeGPUSurface(const SkColor bg = SK_ColorTRANSPARENT, + int width = TestWidth, + int height = TestHeight) const { + RenderGLSurface surface(width, height); + surface.canvas()->clear(bg); + return surface; + } + void init_ref(CvRenderer& cv_renderer, SkColor bg = SK_ColorTRANSPARENT) { init_ref([=](SkCanvas*, SkPaint&) {}, cv_renderer, bg); } @@ -304,6 +352,7 @@ class RenderEnvironment { ref_pixmap_ = ref_surface_.pixmap(); } + const SkImageInfo& info() const { return info_; } SkCanvas* ref_canvas() { return ref_surface_.canvas(); } const SkPaint& ref_paint() const { return ref_paint_; } const SkMatrix& ref_matrix() const { return ref_matrix_; } @@ -1733,6 +1782,10 @@ class CanvasCompareTester { compareToReference(dl_surface.pixmap(), sk_pixels, info + " (DisplayList built directly -> surface)", &dl_bounds, &tolerance, bg); + + if (display_list->can_apply_group_opacity()) { + checkGroupOpacity(env, display_list, info + " with Group Opacity", bg); + } } // This test cannot work if the rendering is using shadows until @@ -1790,6 +1843,39 @@ class CanvasCompareTester { } } + static void checkGroupOpacity(const RenderEnvironment& env, + sk_sp display_list, + const std::string info, + SkColor bg) { + DisplayListBuilder builder; + SkColor color = builder.getColor(); + builder.setColor(SkColorSetARGB(128, 0, 0, 0)); + builder.saveLayer(&TestBounds, true); + // Reset the state for the display list + builder.setColor(color); + builder.setAntiAlias(true); + display_list->Dispatch(builder); + builder.restore(); + sk_sp save_layer_display_list = builder.Build(); + + RenderSurface save_layer_surface = env.MakeGPUSurface(bg); + save_layer_display_list->RenderTo(save_layer_surface.canvas()); + const SkPixmap* save_layer_pixmap = save_layer_surface.pixmap(); + + RenderSurface group_opacity_surface = env.MakeGPUSurface(bg); + SkCanvas* group_opacity_canvas = group_opacity_surface.canvas(); + DisplayListCanvasDispatcher dispatcher(group_opacity_canvas, 128.0 / 255.0); + dispatcher.setAntiAlias(true); + display_list->Dispatch(dispatcher); + const SkPixmap* group_opacity_pixmap = group_opacity_surface.pixmap(); + + quickCompareToReference(save_layer_pixmap, + group_opacity_pixmap, + true, info, + // For 565 surfaces off-by-1 is a jump of 8 or 9 + env.info().bytesPerPixel() < 4 ? 9 : 1); + } + static void checkPixels(const SkPixmap* ref_pixels, SkRect ref_bounds, const std::string info, @@ -1813,10 +1899,29 @@ class CanvasCompareTester { ASSERT_GT(pixels_touched, 0) << info; } + static bool fudgedCompare(uint32_t ref_pixel, uint32_t test_pixel, + int fudge) { + if (ref_pixel == test_pixel) { + return true; + } + if (fudge == 0) { + return false; + } + for (int i = 0; i < 32; i += 8) { + int ref_comp = (ref_pixel >> i) & 0xff; + int test_comp = (test_pixel >> i) & 0xff; + if (std::abs(ref_comp - test_comp) > fudge) { + return false; + } + } + return true; + } + static void quickCompareToReference(const SkPixmap* ref_pixels, const SkPixmap* test_pixels, bool should_match, - const std::string info) { + const std::string info, + int fudge = 0) { ASSERT_EQ(test_pixels->width(), ref_pixels->width()) << info; ASSERT_EQ(test_pixels->height(), ref_pixels->height()) << info; ASSERT_EQ(test_pixels->info().bytesPerPixel(), 4) << info; @@ -1826,7 +1931,10 @@ class CanvasCompareTester { const uint32_t* ref_row = ref_pixels->addr32(0, y); const uint32_t* test_row = test_pixels->addr32(0, y); for (int x = 0; x < test_pixels->width(); x++) { - if (ref_row[x] != test_row[x]) { + if (!fudgedCompare(ref_row[x], test_row[x], fudge)) { + if (fudge != 0) { + FML_LOG(ERROR) << "Pixels differ at " << x << ", " << y << " => " << std::hex << ref_row[x] << " != " << test_row[x]; + } pixels_different++; } } @@ -2844,7 +2952,7 @@ TEST_F(DisplayListCanvas, DrawPicture) { TEST_F(DisplayListCanvas, DrawPictureWithMatrix) { sk_sp picture = makeTestPicture(); - SkMatrix matrix = SkMatrix::Scale(0.95, 0.95); + SkMatrix matrix = SkMatrix::Scale(0.9, 0.9); CanvasCompareTester::RenderAll( // TestParameters( [=](SkCanvas* canvas, const SkPaint& paint) { // diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 6228440a0f228..8807bb6a0ac6f 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -232,6 +232,7 @@ struct DisplayListInvocation { size_t sk_byte_count_; DlInvoker invoker; + bool supports_group_opacity_ = false; bool sk_version_matches() { return (op_count_ == sk_op_count_ && byte_count_ == sk_byte_count_); @@ -246,6 +247,8 @@ struct DisplayListInvocation { bool is_empty() { return byte_count_ == 0; } + bool supports_group_opacity() { return supports_group_opacity_; } + int op_count() { return op_count_; } // byte count for the individual ops, no DisplayList overhead size_t raw_byte_count() { return byte_count_; } @@ -1397,5 +1400,170 @@ TEST(DisplayList, SetMaskFilterNullResetsMaskFilter) { ASSERT_EQ(display_list->bytes(), sizeof(DisplayList) + 8u + 24u + 8u + 24u); } +TEST(DisplayList, SingleOpsMightSupportGroupOpacityWithOrWithoutBlendMode) { + auto run_tests = [](std::string name, + void build(DisplayListBuilder & builder), + bool expect_for_op, + bool expect_with_kSrc) { + { + // First test is the draw op, by itself + // (usually supports group opacity) + DisplayListBuilder builder; + build(builder); + auto display_list = builder.Build(); + EXPECT_EQ(display_list->can_apply_group_opacity(), expect_for_op) + << "{" << std::endl + << " " << name << std::endl + << "}"; + } + { + // Second test i the draw op with kSrc, + // (usually fails group opacity) + DisplayListBuilder builder; + builder.setBlendMode(SkBlendMode::kSrc); + build(builder); + auto display_list = builder.Build(); + EXPECT_EQ(display_list->can_apply_group_opacity(), expect_with_kSrc) + << "{" << std::endl + << " builder.setBlendMode(kSrc);" << std::endl + << " " << name << std::endl + << "}"; + } + }; + +#define RUN_TESTS(body) \ + run_tests(#body, [](DisplayListBuilder& builder) { body }, true, false) +#define RUN_TESTS2(body, expect) \ + run_tests(#body, [](DisplayListBuilder& builder) { body }, expect, expect) + + RUN_TESTS(builder.drawPaint();); + RUN_TESTS2(builder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);, true); + RUN_TESTS2(builder.drawColor(SK_ColorRED, SkBlendMode::kSrc);, false); + RUN_TESTS(builder.drawLine({0, 0}, {10, 10});); + RUN_TESTS(builder.drawRect({0, 0, 10, 10});); + RUN_TESTS(builder.drawOval({0, 0, 10, 10});); + RUN_TESTS(builder.drawCircle({10, 10}, 5);); + RUN_TESTS(builder.drawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2));); + RUN_TESTS(builder.drawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2), + SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2));); + RUN_TESTS(builder.drawPath(SkPath().addOval({0, 0, 10, 10}) + .addOval({5, 5, 15, 15}));); + RUN_TESTS(builder.drawArc({0, 0, 10, 10}, 0, M_PI, true);); + RUN_TESTS2(builder.drawPoints(SkCanvas::kPoints_PointMode, TestPointCount, + TestPoints);, false); + RUN_TESTS2(builder.drawVertices(TestVertices1, SkBlendMode::kSrc);, false); + RUN_TESTS(builder.drawImage(TestImage1, {0, 0}, DisplayList::LinearSampling, + true);); + RUN_TESTS2(builder.drawImage(TestImage1, {0, 0}, DisplayList::LinearSampling, + false);, true); + RUN_TESTS(builder.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10}, + DisplayList::NearestSampling, true);); + RUN_TESTS2(builder.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10}, + DisplayList::NearestSampling, false);, true); + RUN_TESTS(builder.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, + SkFilterMode::kLinear, true);); + RUN_TESTS2(builder.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, + SkFilterMode::kLinear, false);, true); + RUN_TESTS(builder.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, true);); + RUN_TESTS2(builder.drawImageLattice(TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false);, true); + static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; + static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; + RUN_TESTS2(builder.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr, true);, false); + RUN_TESTS2(builder.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr, false);, false); + RUN_TESTS(builder.drawPicture(TestPicture1, nullptr, true);); + RUN_TESTS2(builder.drawPicture(TestPicture1, nullptr, false);, true); + EXPECT_TRUE(TestDisplayList1->can_apply_group_opacity()); + RUN_TESTS2(builder.drawDisplayList(TestDisplayList1);, true); + { + static DisplayListBuilder builder; + builder.drawRect({0, 0, 10, 10}); + builder.drawRect({5, 5, 15, 15}); + static auto display_list = builder.Build(); + RUN_TESTS2(builder.drawDisplayList(display_list);, false); + } + RUN_TESTS(builder.drawTextBlob(TestBlob1, 0, 0);); + RUN_TESTS2(builder.drawShadow(TestPath1, SK_ColorBLACK, 1.0, false, 1.0);, false); + +#undef RUN_TESTS2 +#undef RUN_TESTS +} + +TEST(DisplayList, OverlappingOpsDoNotSupportGroupOpacity) { + DisplayListBuilder builder; + for (int i = 0; i < 10; i++) { + builder.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); + } + auto display_list = builder.Build(); + EXPECT_FALSE(display_list->can_apply_group_opacity()); +} + +TEST(DisplayList, SaveLayerFalseSupportsGroupOpacityWithOverlappingChidren) { + DisplayListBuilder builder; + builder.saveLayer(nullptr, false); + for (int i = 0; i < 10; i++) { + builder.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); + } + builder.restore(); + auto display_list = builder.Build(); + EXPECT_TRUE(display_list->can_apply_group_opacity()); +} + +TEST(DisplayList, SaveLayerTrueSupportsGroupOpacityWithOverlappingChidren) { + DisplayListBuilder builder; + builder.saveLayer(nullptr, true); + for (int i = 0; i < 10; i++) { + builder.drawRect(SkRect::MakeXYWH(i * 10, 0, 30, 30)); + } + builder.restore(); + auto display_list = builder.Build(); + EXPECT_TRUE(display_list->can_apply_group_opacity()); +} + +TEST(DisplayList, SaveLayerFalseWithSrcBlendSupportsGroupOpacity) { + DisplayListBuilder builder; + builder.setBlendMode(SkBlendMode::kSrc); + builder.saveLayer(nullptr, false); + builder.drawRect({0, 0, 10, 10}); + builder.restore(); + auto display_list = builder.Build(); + EXPECT_TRUE(display_list->can_apply_group_opacity()); +} + +TEST(DisplayList, SaveLayerTrueWithSrcBlendDoesNotSupportGroupOpacity) { + DisplayListBuilder builder; + builder.setBlendMode(SkBlendMode::kSrc); + builder.saveLayer(nullptr, true); + builder.drawRect({0, 0, 10, 10}); + builder.restore(); + auto display_list = builder.Build(); + EXPECT_FALSE(display_list->can_apply_group_opacity()); +} + +TEST(DisplayList, SaveLayerFalseSupportsGroupOpacityWithChildSrcBlend) { + DisplayListBuilder builder; + builder.saveLayer(nullptr, false); + builder.setBlendMode(SkBlendMode::kSrc); + builder.drawRect({0, 0, 10, 10}); + builder.restore(); + auto display_list = builder.Build(); + EXPECT_TRUE(display_list->can_apply_group_opacity()); +} + +TEST(DisplayList, SaveLayerTrueSupportsGroupOpacityWithChildSrcBlend) { + DisplayListBuilder builder; + builder.saveLayer(nullptr, true); + builder.setBlendMode(SkBlendMode::kSrc); + builder.drawRect({0, 0, 10, 10}); + builder.restore(); + auto display_list = builder.Build(); + EXPECT_TRUE(display_list->can_apply_group_opacity()); +} + } // namespace testing } // namespace flutter From b71d31574cf9f3b8a95b20eed5cd9903bf478e94 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Wed, 8 Dec 2021 15:16:09 -0800 Subject: [PATCH 09/13] formatting --- flow/display_list.h | 8 ++-- flow/display_list_canvas_unittests.cc | 24 +++++------ flow/display_list_unittests.cc | 61 ++++++++++++++++----------- 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/flow/display_list.h b/flow/display_list.h index 05c344624d75e..1ba3c12d85e47 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -1049,10 +1049,10 @@ class DisplayListBuilder final : public virtual Dispatcher, } void UpdateCurrentOpacityCompatibility() { - current_opacity_compatibility_ = - current_color_filter_ == nullptr && - !current_invert_colors_ && - current_blender_ == nullptr && + current_opacity_compatibility_ = // + current_color_filter_ == nullptr && // + !current_invert_colors_ && // + current_blender_ == nullptr && // IsOpacityCompatible(current_blend_mode_); } diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index abd7bb363ae40..e99a9d2934766 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -288,21 +288,17 @@ class RenderSurface { class RenderGLSurface : public RenderSurface { public: - explicit RenderGLSurface(int width, int height) - : RenderSurface(nullptr) { + explicit RenderGLSurface(int width, int height) : RenderSurface(nullptr) { SkISize size = SkISize::Make(width, height); gl_surface_ = std::make_unique(size); gl_surface_->MakeCurrent(); context_ = gl_surface_->GetGrContext(); - set_surface(SkSurface::MakeRenderTarget(context_.get(), SkBudgeted::kNo, - SkImageInfo::MakeN32Premul(size), 1, - kTopLeft_GrSurfaceOrigin, nullptr, - false)); + set_surface(SkSurface::MakeRenderTarget( + context_.get(), SkBudgeted::kNo, SkImageInfo::MakeN32Premul(size), 1, + kTopLeft_GrSurfaceOrigin, nullptr, false)); } - virtual void MakeCurrent() override { - gl_surface_->MakeCurrent(); - } + virtual void MakeCurrent() override { gl_surface_->MakeCurrent(); } private: std::unique_ptr gl_surface_; @@ -1869,9 +1865,7 @@ class CanvasCompareTester { display_list->Dispatch(dispatcher); const SkPixmap* group_opacity_pixmap = group_opacity_surface.pixmap(); - quickCompareToReference(save_layer_pixmap, - group_opacity_pixmap, - true, info, + quickCompareToReference(save_layer_pixmap, group_opacity_pixmap, true, info, // For 565 surfaces off-by-1 is a jump of 8 or 9 env.info().bytesPerPixel() < 4 ? 9 : 1); } @@ -1899,7 +1893,8 @@ class CanvasCompareTester { ASSERT_GT(pixels_touched, 0) << info; } - static bool fudgedCompare(uint32_t ref_pixel, uint32_t test_pixel, + static bool fudgedCompare(uint32_t ref_pixel, + uint32_t test_pixel, int fudge) { if (ref_pixel == test_pixel) { return true; @@ -1933,7 +1928,8 @@ class CanvasCompareTester { for (int x = 0; x < test_pixels->width(); x++) { if (!fudgedCompare(ref_row[x], test_row[x], fudge)) { if (fudge != 0) { - FML_LOG(ERROR) << "Pixels differ at " << x << ", " << y << " => " << std::hex << ref_row[x] << " != " << test_row[x]; + FML_LOG(ERROR) << "Pixels differ at " << x << ", " << y << " => " + << std::hex << ref_row[x] << " != " << test_row[x]; } pixels_different++; } diff --git a/flow/display_list_unittests.cc b/flow/display_list_unittests.cc index 8807bb6a0ac6f..cb8b7c21c41c6 100644 --- a/flow/display_list_unittests.cc +++ b/flow/display_list_unittests.cc @@ -1403,8 +1403,7 @@ TEST(DisplayList, SetMaskFilterNullResetsMaskFilter) { TEST(DisplayList, SingleOpsMightSupportGroupOpacityWithOrWithoutBlendMode) { auto run_tests = [](std::string name, void build(DisplayListBuilder & builder), - bool expect_for_op, - bool expect_with_kSrc) { + bool expect_for_op, bool expect_with_kSrc) { { // First test is the draw op, by itself // (usually supports group opacity) @@ -1432,9 +1431,11 @@ TEST(DisplayList, SingleOpsMightSupportGroupOpacityWithOrWithoutBlendMode) { }; #define RUN_TESTS(body) \ - run_tests(#body, [](DisplayListBuilder& builder) { body }, true, false) + run_tests( \ + #body, [](DisplayListBuilder& builder) { body }, true, false) #define RUN_TESTS2(body, expect) \ - run_tests(#body, [](DisplayListBuilder& builder) { body }, expect, expect) + run_tests( \ + #body, [](DisplayListBuilder& builder) { body }, expect, expect) RUN_TESTS(builder.drawPaint();); RUN_TESTS2(builder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);, true); @@ -1446,36 +1447,47 @@ TEST(DisplayList, SingleOpsMightSupportGroupOpacityWithOrWithoutBlendMode) { RUN_TESTS(builder.drawRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2));); RUN_TESTS(builder.drawDRRect(SkRRect::MakeRectXY({0, 0, 10, 10}, 2, 2), SkRRect::MakeRectXY({2, 2, 8, 8}, 2, 2));); - RUN_TESTS(builder.drawPath(SkPath().addOval({0, 0, 10, 10}) - .addOval({5, 5, 15, 15}));); + RUN_TESTS(builder.drawPath( + SkPath().addOval({0, 0, 10, 10}).addOval({5, 5, 15, 15}));); RUN_TESTS(builder.drawArc({0, 0, 10, 10}, 0, M_PI, true);); RUN_TESTS2(builder.drawPoints(SkCanvas::kPoints_PointMode, TestPointCount, - TestPoints);, false); + TestPoints); + , false); RUN_TESTS2(builder.drawVertices(TestVertices1, SkBlendMode::kSrc);, false); RUN_TESTS(builder.drawImage(TestImage1, {0, 0}, DisplayList::LinearSampling, true);); - RUN_TESTS2(builder.drawImage(TestImage1, {0, 0}, DisplayList::LinearSampling, - false);, true); + RUN_TESTS2( + builder.drawImage(TestImage1, {0, 0}, DisplayList::LinearSampling, false); + , true); RUN_TESTS(builder.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10}, DisplayList::NearestSampling, true);); RUN_TESTS2(builder.drawImageRect(TestImage1, {10, 10, 20, 20}, {0, 0, 10, 10}, - DisplayList::NearestSampling, false);, true); + DisplayList::NearestSampling, false); + , true); RUN_TESTS(builder.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, SkFilterMode::kLinear, true);); RUN_TESTS2(builder.drawImageNine(TestImage2, {20, 20, 30, 30}, {0, 0, 20, 20}, - SkFilterMode::kLinear, false);, true); - RUN_TESTS(builder.drawImageLattice(TestImage1, - {TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr}, - {10, 10, 40, 40}, SkFilterMode::kNearest, true);); - RUN_TESTS2(builder.drawImageLattice(TestImage1, - {TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr}, - {10, 10, 40, 40}, SkFilterMode::kNearest, false);, true); - static SkRSXform xforms[] = { {1, 0, 0, 0}, {0, 1, 0, 0} }; - static SkRect texs[] = { { 10, 10, 20, 20 }, {20, 20, 30, 30} }; - RUN_TESTS2(builder.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, - DisplayList::NearestSampling, nullptr, true);, false); - RUN_TESTS2(builder.drawAtlas(TestImage1, xforms, texs, nullptr, 2, SkBlendMode::kSrcIn, - DisplayList::NearestSampling, nullptr, false);, false); + SkFilterMode::kLinear, false); + , true); + RUN_TESTS(builder.drawImageLattice( + TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, true);); + RUN_TESTS2(builder.drawImageLattice( + TestImage1, + {TestDivs1, TestDivs1, nullptr, 3, 3, &TestLatticeSrcRect, nullptr}, + {10, 10, 40, 40}, SkFilterMode::kNearest, false); + , true); + static SkRSXform xforms[] = {{1, 0, 0, 0}, {0, 1, 0, 0}}; + static SkRect texs[] = {{10, 10, 20, 20}, {20, 20, 30, 30}}; + RUN_TESTS2(builder.drawAtlas(TestImage1, xforms, texs, nullptr, 2, + SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr, true); + , false); + RUN_TESTS2(builder.drawAtlas(TestImage1, xforms, texs, nullptr, 2, + SkBlendMode::kSrcIn, + DisplayList::NearestSampling, nullptr, false); + , false); RUN_TESTS(builder.drawPicture(TestPicture1, nullptr, true);); RUN_TESTS2(builder.drawPicture(TestPicture1, nullptr, false);, true); EXPECT_TRUE(TestDisplayList1->can_apply_group_opacity()); @@ -1488,7 +1500,8 @@ TEST(DisplayList, SingleOpsMightSupportGroupOpacityWithOrWithoutBlendMode) { RUN_TESTS2(builder.drawDisplayList(display_list);, false); } RUN_TESTS(builder.drawTextBlob(TestBlob1, 0, 0);); - RUN_TESTS2(builder.drawShadow(TestPath1, SK_ColorBLACK, 1.0, false, 1.0);, false); + RUN_TESTS2(builder.drawShadow(TestPath1, SK_ColorBLACK, 1.0, false, 1.0); + , false); #undef RUN_TESTS2 #undef RUN_TESTS From 23dc98094b5659acc9c5a063c52af1867bbf7cab Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Fri, 10 Dec 2021 14:15:49 -0800 Subject: [PATCH 10/13] change group opacity test methods and mark hairline ops incompatible --- flow/display_list.cc | 8 +- flow/display_list.h | 6 + flow/display_list_canvas_unittests.cc | 229 ++++++++++++++++++-------- flow/layers/layer.h | 2 +- 4 files changed, 171 insertions(+), 74 deletions(-) diff --git a/flow/display_list.cc b/flow/display_list.cc index 4856b7d102398..6c96bbf1da7b1 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -1431,7 +1431,7 @@ void DisplayListBuilder::drawDRRect(const SkRRect& outer, } void DisplayListBuilder::drawPath(const SkPath& path) { Push(0, 1, path); - CheckLayerOpacityCompatibility(); + CheckLayerOpacityHairlineCompatibility(); } void DisplayListBuilder::drawArc(const SkRect& bounds, @@ -1439,7 +1439,11 @@ void DisplayListBuilder::drawArc(const SkRect& bounds, SkScalar sweep, bool useCenter) { Push(0, 1, bounds, start, sweep, useCenter); - CheckLayerOpacityCompatibility(); + if (useCenter) { + CheckLayerOpacityHairlineCompatibility(); + } else { + CheckLayerOpacityCompatibility(); + } } void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, uint32_t count, diff --git a/flow/display_list.h b/flow/display_list.h index 1ba3c12d85e47..89c3478c232d2 100644 --- a/flow/display_list.h +++ b/flow/display_list.h @@ -1076,6 +1076,12 @@ class DisplayListBuilder final : public virtual Dispatcher, current_opacity_compatibility_); } + void CheckLayerOpacityHairlineCompatibility() { + UpdateLayerOpacityCompatibility( + current_opacity_compatibility_ && + (current_style_ == SkPaint::kFill_Style || current_stroke_width_ > 0)); + } + // Check for opacity compatibility for an op that ignores the current // attributes and uses the indicated blend |mode| to render to the layer. // This is only used by |drawColor| currently. diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index e99a9d2934766..541b5b9d624c7 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -246,7 +246,7 @@ static void EmptyDlRenderer(DisplayListBuilder&) {} class RenderSurface { public: explicit RenderSurface(sk_sp surface) : surface_(surface) {} - ~RenderSurface() { sk_free(addr_); } + virtual ~RenderSurface() { sk_free(addr_); } SkCanvas* canvas() { MakeCurrent(); @@ -255,23 +255,17 @@ class RenderSurface { virtual void MakeCurrent() {} - const SkPixmap* pixmap() { - surface_->flushAndSubmit(); + virtual const SkPixmap* pixmap() { if (!pixmap_.addr()) { + MakeCurrent(); + surface_->flushAndSubmit(); SkImageInfo info = surface_->imageInfo(); if (info.colorType() != kN32_SkColorType || !surface_->peekPixels(&pixmap_)) { info = SkImageInfo::MakeN32Premul(info.dimensions()); addr_ = malloc(info.computeMinByteSize() * info.height()); pixmap_.reset(info, addr_, info.minRowBytes()); - if (!surface_->readPixels(pixmap_, 0, 0)) { - MakeCurrent(); - auto snapshot = surface_->makeImageSnapshot(); - EXPECT_NE(snapshot, nullptr); - auto image = snapshot->makeRasterImage(); - EXPECT_NE(image, nullptr); - EXPECT_TRUE(image->readPixels(pixmap_, 0, 0)); - } + EXPECT_TRUE(surface_->readPixels(pixmap_, 0, 0)); } } return &pixmap_; @@ -279,11 +273,9 @@ class RenderSurface { protected: void set_surface(sk_sp surface) { surface_ = surface; } - - private: - sk_sp surface_; SkPixmap pixmap_; void* addr_ = nullptr; + sk_sp surface_; }; class RenderGLSurface : public RenderSurface { @@ -295,10 +287,36 @@ class RenderGLSurface : public RenderSurface { context_ = gl_surface_->GetGrContext(); set_surface(SkSurface::MakeRenderTarget( context_.get(), SkBudgeted::kNo, SkImageInfo::MakeN32Premul(size), 1, - kTopLeft_GrSurfaceOrigin, nullptr, false)); + kBottomLeft_GrSurfaceOrigin, nullptr, false)); + } + + ~RenderGLSurface() { + context_ = nullptr; + gl_surface_.reset(); } - virtual void MakeCurrent() override { gl_surface_->MakeCurrent(); } + virtual void MakeCurrent() override { + EXPECT_TRUE(gl_surface_->MakeCurrent()); + } + + virtual const SkPixmap* pixmap() override { + if (!pixmap_.addr()) { + SkImageInfo info = surface_->imageInfo(); + MakeCurrent(); + surface_->flushAndSubmit(); + auto snapshot = surface_->makeImageSnapshot(); + EXPECT_NE(snapshot, nullptr); + auto image = snapshot->makeRasterImage(); + EXPECT_NE(image, nullptr); + info = SkImageInfo::MakeN32Premul(info.dimensions()); + addr_ = malloc(info.computeMinByteSize() * info.height()); + pixmap_.reset(info, addr_, info.minRowBytes()); + EXPECT_TRUE(image->readPixels(pixmap_, 0, 0)); + context_ = nullptr; + gl_surface_.reset(); + } + return &pixmap_; + } private: std::unique_ptr gl_surface_; @@ -307,6 +325,7 @@ class RenderGLSurface : public RenderSurface { class RenderEnvironment { public: + static constexpr bool UseGPU = true; static RenderEnvironment Make565() { return RenderEnvironment(SkImageInfo::Make({1, 1}, kRGB_565_SkColorType, kOpaque_SkAlphaType, nullptr)); @@ -316,20 +335,26 @@ class RenderEnvironment { return RenderEnvironment(SkImageInfo::MakeN32Premul(1, 1)); } - RenderSurface MakeSurface(const SkColor bg = SK_ColorTRANSPARENT, - int width = TestWidth, - int height = TestHeight) const { + std::unique_ptr MakeSurface( + const SkColor bg = SK_ColorTRANSPARENT, + int width = TestWidth, + int height = TestHeight) const { sk_sp surface = SkSurface::MakeRaster(info_.makeWH(width, height)); surface->getCanvas()->clear(bg); - return RenderSurface(surface); + return std::make_unique(surface); } - RenderSurface MakeGPUSurface(const SkColor bg = SK_ColorTRANSPARENT, - int width = TestWidth, - int height = TestHeight) const { - RenderGLSurface surface(width, height); - surface.canvas()->clear(bg); + std::unique_ptr MakeGPUSurface( + const SkColor bg = SK_ColorTRANSPARENT, + int width = TestWidth, + int height = TestHeight) const { + if (!UseGPU) { + return MakeSurface(bg, width, height); + } + std::unique_ptr surface = + std::make_unique(width, height); + surface->canvas()->clear(bg); return surface; } @@ -345,11 +370,11 @@ class RenderEnvironment { ref_matrix_ = ref_canvas()->getTotalMatrix(); ref_clip_ = ref_canvas()->getDeviceClipBounds(); cv_renderer(ref_canvas(), ref_paint_); - ref_pixmap_ = ref_surface_.pixmap(); + ref_pixmap_ = ref_surface_->pixmap(); } const SkImageInfo& info() const { return info_; } - SkCanvas* ref_canvas() { return ref_surface_.canvas(); } + SkCanvas* ref_canvas() { return ref_surface_->canvas(); } const SkPaint& ref_paint() const { return ref_paint_; } const SkMatrix& ref_matrix() const { return ref_matrix_; } const SkIRect& ref_clip_bounds() const { return ref_clip_; } @@ -364,7 +389,7 @@ class RenderEnvironment { SkPaint ref_paint_; SkMatrix ref_matrix_; SkIRect ref_clip_; - RenderSurface ref_surface_; + std::unique_ptr ref_surface_; const SkPixmap* ref_pixmap_ = nullptr; }; @@ -1325,7 +1350,7 @@ class CanvasCompareTester { } static void RenderWithStrokes(const TestParameters& testP, - const RenderEnvironment env, + const RenderEnvironment& env, const BoundsTolerance& tolerance_in) { // The test cases were generated with geometry that will try to fill // out the various miter limits used for testing, but they can be off @@ -1557,9 +1582,14 @@ class CanvasCompareTester { [=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); })); { - SkMatrix tx = SkMatrix::MakeAll(1.10, 0.10, 5, // - 0.05, 1.05, 10, // + SkScalar tweak = 1.0 / 16.0; + SkMatrix tx = SkMatrix::MakeAll(1.0 + tweak, tweak, 5, // + tweak, 1.0 + tweak, 10, // 0, 0, 1); + // SkMatrix tx = SkMatrix::I(); + // tx.preTranslate(RenderCenterX, RenderCenterY); + // tx.preRotate(M_PI / 60); // 3 degrees around Z + // tx.preTranslate(-RenderCenterX, -RenderCenterY); RenderWith(testP, env, skewed_tolerance, CaseParameters( "Transform 2D Affine", @@ -1705,8 +1735,8 @@ class CanvasCompareTester { // DisplayList mechanisms are not involved in this operation const std::string info = caseP.info(); const SkColor bg = caseP.bg(); - RenderSurface sk_surface = env.MakeSurface(bg); - SkCanvas* sk_canvas = sk_surface.canvas(); + std::unique_ptr sk_surface = env.MakeSurface(bg); + SkCanvas* sk_canvas = sk_surface->canvas(); SkPaint sk_paint; caseP.cv_setup()(sk_canvas, sk_paint); SkMatrix sk_matrix = sk_canvas->getTotalMatrix(); @@ -1717,7 +1747,7 @@ class CanvasCompareTester { caseP.cv_restore()(sk_canvas, sk_paint); const sk_sp sk_picture = getSkPicture(testP, caseP); SkRect sk_bounds = sk_picture->cullRect(); - const SkPixmap* sk_pixels = sk_surface.pixmap(); + const SkPixmap* sk_pixels = sk_surface->pixmap(); ASSERT_EQ(sk_pixels->width(), TestWidth) << info; ASSERT_EQ(sk_pixels->height(), TestHeight) << info; ASSERT_EQ(sk_pixels->info().bytesPerPixel(), 4) << info; @@ -1737,7 +1767,6 @@ class CanvasCompareTester { // This sequence plays the provided equivalently constructed // DisplayList onto the SkCanvas of the surface // DisplayList => direct rendering - RenderSurface dl_surface = env.MakeSurface(bg); DisplayListBuilder builder(TestBounds); caseP.render_to(builder, testP); sk_sp display_list = builder.Build(); @@ -1774,13 +1803,15 @@ class CanvasCompareTester { << info; } - display_list->RenderTo(dl_surface.canvas()); - compareToReference(dl_surface.pixmap(), sk_pixels, + std::unique_ptr dl_surface = env.MakeSurface(bg); + display_list->RenderTo(dl_surface->canvas()); + compareToReference(dl_surface->pixmap(), sk_pixels, info + " (DisplayList built directly -> surface)", &dl_bounds, &tolerance, bg); if (display_list->can_apply_group_opacity()) { - checkGroupOpacity(env, display_list, info + " with Group Opacity", bg); + checkGroupOpacity(env, display_list, dl_surface->pixmap(), + info + " with Group Opacity", bg); } } @@ -1790,11 +1821,11 @@ class CanvasCompareTester { // This sequence renders SkCanvas calls to a DisplayList and then // plays them back on SkCanvas to SkSurface // SkCanvas calls => DisplayList => rendering - RenderSurface cv_dl_surface = env.MakeSurface(bg); + std::unique_ptr cv_dl_surface = env.MakeSurface(bg); DisplayListCanvasRecorder dl_recorder(TestBounds); caseP.render_to(&dl_recorder, testP); - dl_recorder.builder()->Build()->RenderTo(cv_dl_surface.canvas()); - compareToReference(cv_dl_surface.pixmap(), sk_pixels, + dl_recorder.builder()->Build()->RenderTo(cv_dl_surface->canvas()); + compareToReference(cv_dl_surface->pixmap(), sk_pixels, info + " (Skia calls -> DisplayList -> surface)", nullptr, nullptr, bg); } @@ -1815,12 +1846,12 @@ class CanvasCompareTester { caseP.render_to(ref_canvas, testP); sk_sp ref_x2_picture = sk_x2_recorder.finishRecordingAsPicture(); - RenderSurface ref_x2_surface = + std::unique_ptr ref_x2_surface = env.MakeSurface(bg, TestWidth2, TestHeight2); - SkCanvas* ref_x2_canvas = ref_x2_surface.canvas(); + SkCanvas* ref_x2_canvas = ref_x2_surface->canvas(); ref_x2_canvas->scale(TestScale, TestScale); ref_x2_picture->playback(ref_x2_canvas); - const SkPixmap* ref_x2_pixels = ref_x2_surface.pixmap(); + const SkPixmap* ref_x2_pixels = ref_x2_surface->pixmap(); ASSERT_EQ(ref_x2_pixels->width(), TestWidth2) << info; ASSERT_EQ(ref_x2_pixels->height(), TestHeight2) << info; ASSERT_EQ(ref_x2_pixels->info().bytesPerPixel(), 4) << info; @@ -1828,12 +1859,12 @@ class CanvasCompareTester { DisplayListBuilder builder_x2(TestBounds); caseP.render_to(builder_x2, testP); sk_sp display_list_x2 = builder_x2.Build(); - RenderSurface test_x2_surface = + std::unique_ptr test_x2_surface = env.MakeSurface(bg, TestWidth2, TestHeight2); - SkCanvas* test_x2_canvas = test_x2_surface.canvas(); + SkCanvas* test_x2_canvas = test_x2_surface->canvas(); test_x2_canvas->scale(TestScale, TestScale); display_list_x2->RenderTo(test_x2_canvas); - compareToReference(test_x2_surface.pixmap(), ref_x2_pixels, + compareToReference(test_x2_surface->pixmap(), ref_x2_pixels, info + " (Both rendered scaled 2x)", nullptr, nullptr, bg, TestWidth2, TestHeight2, false); } @@ -1841,37 +1872,92 @@ class CanvasCompareTester { static void checkGroupOpacity(const RenderEnvironment& env, sk_sp display_list, + const SkPixmap* ref_pixmap, const std::string info, SkColor bg) { - DisplayListBuilder builder; - SkColor color = builder.getColor(); - builder.setColor(SkColorSetARGB(128, 0, 0, 0)); - builder.saveLayer(&TestBounds, true); - // Reset the state for the display list - builder.setColor(color); - builder.setAntiAlias(true); - display_list->Dispatch(builder); - builder.restore(); - sk_sp save_layer_display_list = builder.Build(); - - RenderSurface save_layer_surface = env.MakeGPUSurface(bg); - save_layer_display_list->RenderTo(save_layer_surface.canvas()); - const SkPixmap* save_layer_pixmap = save_layer_surface.pixmap(); - - RenderSurface group_opacity_surface = env.MakeGPUSurface(bg); - SkCanvas* group_opacity_canvas = group_opacity_surface.canvas(); + // DisplayListBuilder builder; + // // SkColor color = builder.getColor(); + // // builder.setColor(SkColorSetARGB(128, 0, 0, 0)); + // // builder.saveLayer(&TestBounds, true); + // // Reset the state for the display list + // // builder.setColor(color); + // builder.setAntiAlias(true); + // display_list->Dispatch(builder); + // // builder.restore(); + // sk_sp save_layer_display_list = builder.Build(); + SkScalar opacity = 128.0 / 255.0; + + // std::unique_ptr save_layer_surface = + // env.MakeGPUSurface(bg); + // SkCanvas* save_layer_canvas = save_layer_surface->canvas(); + // SkPaint p; + // p.setAlphaf(opacity); + // save_layer_canvas->saveLayer(&TestBounds, &p); + // display_list->RenderTo(save_layer_canvas); + // save_layer_canvas->restore(); + // const SkPixmap* save_layer_pixmap = save_layer_surface->pixmap(); + std::unique_ptr ref_surface = env.MakeGPUSurface(bg); + SkCanvas* ref_canvas = ref_surface->canvas(); + display_list->RenderTo(ref_canvas); + ref_pixmap = ref_surface->pixmap(); + checkPixels(ref_pixmap, TestBounds, info, bg); + + std::unique_ptr group_opacity_surface = + env.MakeGPUSurface(bg); + SkCanvas* group_opacity_canvas = group_opacity_surface->canvas(); + // display_list->RenderTo(group_opacity_canvas, opacity); DisplayListCanvasDispatcher dispatcher(group_opacity_canvas, 128.0 / 255.0); - dispatcher.setAntiAlias(true); + // dispatcher.setStrokeWidth(0.5); display_list->Dispatch(dispatcher); - const SkPixmap* group_opacity_pixmap = group_opacity_surface.pixmap(); + const SkPixmap* group_opacity_pixmap = group_opacity_surface->pixmap(); + checkPixels(group_opacity_pixmap, TestBounds, info, bg); + + ASSERT_EQ(group_opacity_pixmap->width(), TestWidth) << info; + ASSERT_EQ(group_opacity_pixmap->height(), TestHeight) << info; + ASSERT_EQ(group_opacity_pixmap->info().bytesPerPixel(), 4) << info; - quickCompareToReference(save_layer_pixmap, group_opacity_pixmap, true, info, - // For 565 surfaces off-by-1 is a jump of 8 or 9 - env.info().bytesPerPixel() < 4 ? 9 : 1); + // const SkPixmap* ref_pixmap = env.ref_pixmap(); + ASSERT_EQ(ref_pixmap->width(), TestWidth) << info; + ASSERT_EQ(ref_pixmap->height(), TestHeight) << info; + ASSERT_EQ(ref_pixmap->info().bytesPerPixel(), 4) << info; + + int pixels_different = 0; + int pixels_overdrawn = 0; + int fudge = env.info().bytesPerPixel() < 4 ? 9 : 2; + for (int y = 0; y < TestHeight; y++) { + const uint32_t* ref_row = ref_pixmap->addr32(0, y); + const uint32_t* test_row = group_opacity_pixmap->addr32(0, y); + for (int x = 0; x < TestWidth; x++) { + uint32_t ref_pixel = ref_row[x]; + uint32_t test_pixel = test_row[x]; + if (ref_pixel != bg || test_pixel != bg) { + for (int i = 0; i < 32; i += 8) { + int ref_comp = (ref_pixel >> i) & 0xff; + int bg_comp = (bg >> i) & 0xff; + SkScalar faded_comp = bg_comp + (ref_comp - bg_comp) * opacity; + int test_comp = (test_pixel >> i) & 0xff; + if (std::abs(faded_comp - test_comp) > fudge) { + SkScalar overdrawn_comp = + faded_comp + (ref_comp - faded_comp) * opacity; + if (std::abs(overdrawn_comp - test_comp) <= fudge) { + pixels_overdrawn++; + continue; + } + pixels_different++; + break; + } + } + } + } + } + if (pixels_overdrawn) { + FML_LOG(ERROR) << pixels_overdrawn << " pixels overdrawn for " << info; + } + ASSERT_EQ(pixels_different, 0) << info; } static void checkPixels(const SkPixmap* ref_pixels, - SkRect ref_bounds, + const SkRect ref_bounds, const std::string info, const SkColor bg) { SkPMColor untouched = SkPreMultiplyColor(bg); @@ -1929,7 +2015,8 @@ class CanvasCompareTester { if (!fudgedCompare(ref_row[x], test_row[x], fudge)) { if (fudge != 0) { FML_LOG(ERROR) << "Pixels differ at " << x << ", " << y << " => " - << std::hex << ref_row[x] << " != " << test_row[x]; + << std::hex << std::setfill('0') << std::setw(8) + << ref_row[x] << " != " << test_row[x]; } pixels_different++; } diff --git a/flow/layers/layer.h b/flow/layers/layer.h index f2e9478f01836..e3a8f655fe0bf 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -64,7 +64,7 @@ struct PrerollContext { // This value indicates that the entire subtree below the layer can inherit // an opacity value and modulate its own visibility accordingly. - // For Layers which cannot either apply such an inherited opacity or pass + // For Layers which cannot either apply such an inherited opacity nor pass // it along to their children, they can ignore this value as its default // behavior is "opt-in". // For Layers that support this condition, it can be recorded in their From 999499b7933e2b2115215b5bef1a2301d2202b1b Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 13 Dec 2021 15:37:38 -0800 Subject: [PATCH 11/13] revert to SW shader for group opacity tests and allow 1 pixel difference --- flow/display_list_canvas_unittests.cc | 148 +++++++++----------------- 1 file changed, 53 insertions(+), 95 deletions(-) diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 541b5b9d624c7..76bd2d3301f42 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -245,7 +245,11 @@ static void EmptyDlRenderer(DisplayListBuilder&) {} class RenderSurface { public: - explicit RenderSurface(sk_sp surface) : surface_(surface) {} + explicit RenderSurface(sk_sp surface, + std::shared_ptr gl_surface = nullptr) + : surface_(surface), gl_surface_(gl_surface) { + EXPECT_EQ(canvas()->save(), 1); + } virtual ~RenderSurface() { sk_free(addr_); } SkCanvas* canvas() { @@ -253,12 +257,18 @@ class RenderSurface { return surface_->getCanvas(); } - virtual void MakeCurrent() {} + virtual void MakeCurrent() { + if (gl_surface_) { + EXPECT_TRUE(gl_surface_->MakeCurrent()); + } + } virtual const SkPixmap* pixmap() { if (!pixmap_.addr()) { - MakeCurrent(); - surface_->flushAndSubmit(); + canvas()->restoreToCount(1); + if (gl_surface_) { + surface_->flushAndSubmit(); + } SkImageInfo info = surface_->imageInfo(); if (info.colorType() != kN32_SkColorType || !surface_->peekPixels(&pixmap_)) { @@ -266,66 +276,26 @@ class RenderSurface { addr_ = malloc(info.computeMinByteSize() * info.height()); pixmap_.reset(info, addr_, info.minRowBytes()); EXPECT_TRUE(surface_->readPixels(pixmap_, 0, 0)); + // If we read the pixels then we can delete the + // surface. This helps reclaim GPU resources faster + // and avoid problems with BAD CONTEXT. + surface_ = nullptr; } + gl_surface_ = nullptr; } return &pixmap_; } protected: - void set_surface(sk_sp surface) { surface_ = surface; } SkPixmap pixmap_; void* addr_ = nullptr; sk_sp surface_; -}; - -class RenderGLSurface : public RenderSurface { - public: - explicit RenderGLSurface(int width, int height) : RenderSurface(nullptr) { - SkISize size = SkISize::Make(width, height); - gl_surface_ = std::make_unique(size); - gl_surface_->MakeCurrent(); - context_ = gl_surface_->GetGrContext(); - set_surface(SkSurface::MakeRenderTarget( - context_.get(), SkBudgeted::kNo, SkImageInfo::MakeN32Premul(size), 1, - kBottomLeft_GrSurfaceOrigin, nullptr, false)); - } - - ~RenderGLSurface() { - context_ = nullptr; - gl_surface_.reset(); - } - - virtual void MakeCurrent() override { - EXPECT_TRUE(gl_surface_->MakeCurrent()); - } - - virtual const SkPixmap* pixmap() override { - if (!pixmap_.addr()) { - SkImageInfo info = surface_->imageInfo(); - MakeCurrent(); - surface_->flushAndSubmit(); - auto snapshot = surface_->makeImageSnapshot(); - EXPECT_NE(snapshot, nullptr); - auto image = snapshot->makeRasterImage(); - EXPECT_NE(image, nullptr); - info = SkImageInfo::MakeN32Premul(info.dimensions()); - addr_ = malloc(info.computeMinByteSize() * info.height()); - pixmap_.reset(info, addr_, info.minRowBytes()); - EXPECT_TRUE(image->readPixels(pixmap_, 0, 0)); - context_ = nullptr; - gl_surface_.reset(); - } - return &pixmap_; - } - - private: - std::unique_ptr gl_surface_; - sk_sp context_; + std::shared_ptr gl_surface_; }; class RenderEnvironment { public: - static constexpr bool UseGPU = true; + static constexpr bool UseGPU = false; static RenderEnvironment Make565() { return RenderEnvironment(SkImageInfo::Make({1, 1}, kRGB_565_SkColorType, kOpaque_SkAlphaType, nullptr)); @@ -349,13 +319,17 @@ class RenderEnvironment { const SkColor bg = SK_ColorTRANSPARENT, int width = TestWidth, int height = TestHeight) const { - if (!UseGPU) { + if (!gl_surface_) { return MakeSurface(bg, width, height); } - std::unique_ptr surface = - std::make_unique(width, height); - surface->canvas()->clear(bg); - return surface; + SkISize size = SkISize::Make(width, height); + EXPECT_TRUE(gl_surface_->MakeCurrent()); + GrDirectContext* context = gl_surface_->GetGrContext().get(); + sk_sp surface = SkSurface::MakeRenderTarget( + context, SkBudgeted::kNo, SkImageInfo::MakeN32Premul(size), 1, + kBottomLeft_GrSurfaceOrigin, nullptr, false); + surface->getCanvas()->clear(bg); + return std::make_unique(surface, gl_surface_); } void init_ref(CvRenderer& cv_renderer, SkColor bg = SK_ColorTRANSPARENT) { @@ -381,8 +355,13 @@ class RenderEnvironment { const SkPixmap* ref_pixmap() const { return ref_pixmap_; } private: - explicit RenderEnvironment(const SkImageInfo& info) - : info_(info), ref_surface_(MakeSurface()) {} + explicit RenderEnvironment(const SkImageInfo& info) : info_(info) { + if (UseGPU) { + gl_surface_ = + std::make_shared(SkISize::Make(TestWidth, TestHeight)); + } + ref_surface_ = MakeSurface(); + } const SkImageInfo info_; @@ -391,6 +370,8 @@ class RenderEnvironment { SkIRect ref_clip_; std::unique_ptr ref_surface_; const SkPixmap* ref_pixmap_ = nullptr; + + std::shared_ptr gl_surface_; }; class TestParameters { @@ -1872,35 +1853,20 @@ class CanvasCompareTester { static void checkGroupOpacity(const RenderEnvironment& env, sk_sp display_list, - const SkPixmap* ref_pixmap, + const SkPixmap* dl_ref_pixmap, const std::string info, SkColor bg) { - // DisplayListBuilder builder; - // // SkColor color = builder.getColor(); - // // builder.setColor(SkColorSetARGB(128, 0, 0, 0)); - // // builder.saveLayer(&TestBounds, true); - // // Reset the state for the display list - // // builder.setColor(color); - // builder.setAntiAlias(true); - // display_list->Dispatch(builder); - // // builder.restore(); - // sk_sp save_layer_display_list = builder.Build(); SkScalar opacity = 128.0 / 255.0; - // std::unique_ptr save_layer_surface = - // env.MakeGPUSurface(bg); - // SkCanvas* save_layer_canvas = save_layer_surface->canvas(); - // SkPaint p; - // p.setAlphaf(opacity); - // save_layer_canvas->saveLayer(&TestBounds, &p); - // display_list->RenderTo(save_layer_canvas); - // save_layer_canvas->restore(); - // const SkPixmap* save_layer_pixmap = save_layer_surface->pixmap(); - std::unique_ptr ref_surface = env.MakeGPUSurface(bg); - SkCanvas* ref_canvas = ref_surface->canvas(); - display_list->RenderTo(ref_canvas); - ref_pixmap = ref_surface->pixmap(); - checkPixels(ref_pixmap, TestBounds, info, bg); + const SkPixmap* ref_pixmap; + if (RenderEnvironment::UseGPU) { + std::unique_ptr ref_surface = env.MakeGPUSurface(bg); + SkCanvas* ref_canvas = ref_surface->canvas(); + display_list->RenderTo(ref_canvas); + ref_pixmap = ref_surface->pixmap(); + } else { + ref_pixmap = dl_ref_pixmap; + } std::unique_ptr group_opacity_surface = env.MakeGPUSurface(bg); @@ -1910,7 +1876,6 @@ class CanvasCompareTester { // dispatcher.setStrokeWidth(0.5); display_list->Dispatch(dispatcher); const SkPixmap* group_opacity_pixmap = group_opacity_surface->pixmap(); - checkPixels(group_opacity_pixmap, TestBounds, info, bg); ASSERT_EQ(group_opacity_pixmap->width(), TestWidth) << info; ASSERT_EQ(group_opacity_pixmap->height(), TestHeight) << info; @@ -1921,8 +1886,8 @@ class CanvasCompareTester { ASSERT_EQ(ref_pixmap->height(), TestHeight) << info; ASSERT_EQ(ref_pixmap->info().bytesPerPixel(), 4) << info; + int pixels_touched = 0; int pixels_different = 0; - int pixels_overdrawn = 0; int fudge = env.info().bytesPerPixel() < 4 ? 9 : 2; for (int y = 0; y < TestHeight; y++) { const uint32_t* ref_row = ref_pixmap->addr32(0, y); @@ -1931,18 +1896,13 @@ class CanvasCompareTester { uint32_t ref_pixel = ref_row[x]; uint32_t test_pixel = test_row[x]; if (ref_pixel != bg || test_pixel != bg) { + pixels_touched++; for (int i = 0; i < 32; i += 8) { int ref_comp = (ref_pixel >> i) & 0xff; int bg_comp = (bg >> i) & 0xff; SkScalar faded_comp = bg_comp + (ref_comp - bg_comp) * opacity; int test_comp = (test_pixel >> i) & 0xff; if (std::abs(faded_comp - test_comp) > fudge) { - SkScalar overdrawn_comp = - faded_comp + (ref_comp - faded_comp) * opacity; - if (std::abs(overdrawn_comp - test_comp) <= fudge) { - pixels_overdrawn++; - continue; - } pixels_different++; break; } @@ -1950,10 +1910,8 @@ class CanvasCompareTester { } } } - if (pixels_overdrawn) { - FML_LOG(ERROR) << pixels_overdrawn << " pixels overdrawn for " << info; - } - ASSERT_EQ(pixels_different, 0) << info; + ASSERT_GT(pixels_touched, 20) << info; + ASSERT_LE(pixels_different, 1) << info; } static void checkPixels(const SkPixmap* ref_pixels, From 8118fa2a2881615d497963b4971e3af9cfc31ef0 Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Mon, 13 Dec 2021 16:17:46 -0800 Subject: [PATCH 12/13] remove code using TestGLSurface entirely to fix Fuchsia build problems --- flow/BUILD.gn | 1 - flow/display_list_canvas_unittests.cc | 111 +++----------------------- 2 files changed, 11 insertions(+), 101 deletions(-) diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 45ead55f9a73c..642541ee8dfc0 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -174,7 +174,6 @@ if (enable_unittests) { ":flow_testing", "//flutter/common/graphics", "//flutter/fml", - "//flutter/testing:opengl", "//flutter/testing:skia", "//flutter/testing:testing_lib", "//third_party/dart/runtime:libdart_jit", # for tracing diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index 76bd2d3301f42..fcd8028397e00 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -4,7 +4,6 @@ #include "flutter/flow/display_list_canvas.h" #include "flutter/flow/layers/physical_shape_layer.h" -#include "flutter/testing/test_gl_surface.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkImageInfo.h" @@ -245,30 +244,16 @@ static void EmptyDlRenderer(DisplayListBuilder&) {} class RenderSurface { public: - explicit RenderSurface(sk_sp surface, - std::shared_ptr gl_surface = nullptr) - : surface_(surface), gl_surface_(gl_surface) { + explicit RenderSurface(sk_sp surface) : surface_(surface) { EXPECT_EQ(canvas()->save(), 1); } - virtual ~RenderSurface() { sk_free(addr_); } + ~RenderSurface() { sk_free(addr_); } - SkCanvas* canvas() { - MakeCurrent(); - return surface_->getCanvas(); - } - - virtual void MakeCurrent() { - if (gl_surface_) { - EXPECT_TRUE(gl_surface_->MakeCurrent()); - } - } + SkCanvas* canvas() { return surface_->getCanvas(); } - virtual const SkPixmap* pixmap() { + const SkPixmap* pixmap() { if (!pixmap_.addr()) { canvas()->restoreToCount(1); - if (gl_surface_) { - surface_->flushAndSubmit(); - } SkImageInfo info = surface_->imageInfo(); if (info.colorType() != kN32_SkColorType || !surface_->peekPixels(&pixmap_)) { @@ -276,26 +261,19 @@ class RenderSurface { addr_ = malloc(info.computeMinByteSize() * info.height()); pixmap_.reset(info, addr_, info.minRowBytes()); EXPECT_TRUE(surface_->readPixels(pixmap_, 0, 0)); - // If we read the pixels then we can delete the - // surface. This helps reclaim GPU resources faster - // and avoid problems with BAD CONTEXT. - surface_ = nullptr; } - gl_surface_ = nullptr; } return &pixmap_; } - protected: + private: + sk_sp surface_; SkPixmap pixmap_; void* addr_ = nullptr; - sk_sp surface_; - std::shared_ptr gl_surface_; }; class RenderEnvironment { public: - static constexpr bool UseGPU = false; static RenderEnvironment Make565() { return RenderEnvironment(SkImageInfo::Make({1, 1}, kRGB_565_SkColorType, kOpaque_SkAlphaType, nullptr)); @@ -315,23 +293,6 @@ class RenderEnvironment { return std::make_unique(surface); } - std::unique_ptr MakeGPUSurface( - const SkColor bg = SK_ColorTRANSPARENT, - int width = TestWidth, - int height = TestHeight) const { - if (!gl_surface_) { - return MakeSurface(bg, width, height); - } - SkISize size = SkISize::Make(width, height); - EXPECT_TRUE(gl_surface_->MakeCurrent()); - GrDirectContext* context = gl_surface_->GetGrContext().get(); - sk_sp surface = SkSurface::MakeRenderTarget( - context, SkBudgeted::kNo, SkImageInfo::MakeN32Premul(size), 1, - kBottomLeft_GrSurfaceOrigin, nullptr, false); - surface->getCanvas()->clear(bg); - return std::make_unique(surface, gl_surface_); - } - void init_ref(CvRenderer& cv_renderer, SkColor bg = SK_ColorTRANSPARENT) { init_ref([=](SkCanvas*, SkPaint&) {}, cv_renderer, bg); } @@ -356,10 +317,6 @@ class RenderEnvironment { private: explicit RenderEnvironment(const SkImageInfo& info) : info_(info) { - if (UseGPU) { - gl_surface_ = - std::make_shared(SkISize::Make(TestWidth, TestHeight)); - } ref_surface_ = MakeSurface(); } @@ -370,8 +327,6 @@ class RenderEnvironment { SkIRect ref_clip_; std::unique_ptr ref_surface_; const SkPixmap* ref_pixmap_ = nullptr; - - std::shared_ptr gl_surface_; }; class TestParameters { @@ -1567,10 +1522,6 @@ class CanvasCompareTester { SkMatrix tx = SkMatrix::MakeAll(1.0 + tweak, tweak, 5, // tweak, 1.0 + tweak, 10, // 0, 0, 1); - // SkMatrix tx = SkMatrix::I(); - // tx.preTranslate(RenderCenterX, RenderCenterY); - // tx.preRotate(M_PI / 60); // 3 degrees around Z - // tx.preTranslate(-RenderCenterX, -RenderCenterY); RenderWith(testP, env, skewed_tolerance, CaseParameters( "Transform 2D Affine", @@ -1853,35 +1804,20 @@ class CanvasCompareTester { static void checkGroupOpacity(const RenderEnvironment& env, sk_sp display_list, - const SkPixmap* dl_ref_pixmap, + const SkPixmap* ref_pixmap, const std::string info, SkColor bg) { SkScalar opacity = 128.0 / 255.0; - const SkPixmap* ref_pixmap; - if (RenderEnvironment::UseGPU) { - std::unique_ptr ref_surface = env.MakeGPUSurface(bg); - SkCanvas* ref_canvas = ref_surface->canvas(); - display_list->RenderTo(ref_canvas); - ref_pixmap = ref_surface->pixmap(); - } else { - ref_pixmap = dl_ref_pixmap; - } - - std::unique_ptr group_opacity_surface = - env.MakeGPUSurface(bg); + std::unique_ptr group_opacity_surface = env.MakeSurface(bg); SkCanvas* group_opacity_canvas = group_opacity_surface->canvas(); - // display_list->RenderTo(group_opacity_canvas, opacity); - DisplayListCanvasDispatcher dispatcher(group_opacity_canvas, 128.0 / 255.0); - // dispatcher.setStrokeWidth(0.5); - display_list->Dispatch(dispatcher); + display_list->RenderTo(group_opacity_canvas, opacity); const SkPixmap* group_opacity_pixmap = group_opacity_surface->pixmap(); ASSERT_EQ(group_opacity_pixmap->width(), TestWidth) << info; ASSERT_EQ(group_opacity_pixmap->height(), TestHeight) << info; ASSERT_EQ(group_opacity_pixmap->info().bytesPerPixel(), 4) << info; - // const SkPixmap* ref_pixmap = env.ref_pixmap(); ASSERT_EQ(ref_pixmap->width(), TestWidth) << info; ASSERT_EQ(ref_pixmap->height(), TestHeight) << info; ASSERT_EQ(ref_pixmap->info().bytesPerPixel(), 4) << info; @@ -1937,30 +1873,10 @@ class CanvasCompareTester { ASSERT_GT(pixels_touched, 0) << info; } - static bool fudgedCompare(uint32_t ref_pixel, - uint32_t test_pixel, - int fudge) { - if (ref_pixel == test_pixel) { - return true; - } - if (fudge == 0) { - return false; - } - for (int i = 0; i < 32; i += 8) { - int ref_comp = (ref_pixel >> i) & 0xff; - int test_comp = (test_pixel >> i) & 0xff; - if (std::abs(ref_comp - test_comp) > fudge) { - return false; - } - } - return true; - } - static void quickCompareToReference(const SkPixmap* ref_pixels, const SkPixmap* test_pixels, bool should_match, - const std::string info, - int fudge = 0) { + const std::string info) { ASSERT_EQ(test_pixels->width(), ref_pixels->width()) << info; ASSERT_EQ(test_pixels->height(), ref_pixels->height()) << info; ASSERT_EQ(test_pixels->info().bytesPerPixel(), 4) << info; @@ -1970,12 +1886,7 @@ class CanvasCompareTester { const uint32_t* ref_row = ref_pixels->addr32(0, y); const uint32_t* test_row = test_pixels->addr32(0, y); for (int x = 0; x < test_pixels->width(); x++) { - if (!fudgedCompare(ref_row[x], test_row[x], fudge)) { - if (fudge != 0) { - FML_LOG(ERROR) << "Pixels differ at " << x << ", " << y << " => " - << std::hex << std::setfill('0') << std::setw(8) - << ref_row[x] << " != " << test_row[x]; - } + if (ref_row[x] != test_row[x]) { pixels_different++; } } From f11845dd7d47af7a6d3f6281c6fc614341af475f Mon Sep 17 00:00:00 2001 From: Jim Graham Date: Tue, 14 Dec 2021 15:09:43 -0800 Subject: [PATCH 13/13] review feedback - adding comments here and there --- flow/display_list.cc | 1 + flow/display_list_canvas_unittests.cc | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/flow/display_list.cc b/flow/display_list.cc index 6c96bbf1da7b1..d9e2248637370 100644 --- a/flow/display_list.cc +++ b/flow/display_list.cc @@ -1470,6 +1470,7 @@ void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode, // as a completely separate operation meaning we cannot ensure // distribution of group opacity without analyzing the mode and the // bounds of every sub-primitive. + // See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c UpdateLayerOpacityCompatibility(false); } void DisplayListBuilder::drawVertices(const sk_sp vertices, diff --git a/flow/display_list_canvas_unittests.cc b/flow/display_list_canvas_unittests.cc index fcd8028397e00..088bd34e98f85 100644 --- a/flow/display_list_canvas_unittests.cc +++ b/flow/display_list_canvas_unittests.cc @@ -1518,6 +1518,13 @@ class CanvasCompareTester { [=](SkCanvas* c, SkPaint&) { c->skew(0.05, 0.05); }, [=](DisplayListBuilder& b) { b.skew(0.05, 0.05); })); { + // This rather odd transform can cause slight differences in + // computing in-bounds samples depending on which base rendering + // routine Skia uses. Making sure our matrix values are powers + // of 2 reduces, but does not eliminate, these slight differences + // in calculation when we are comparing rendering with an alpha + // to rendering opaque colors in the group opacity tests, for + // example. SkScalar tweak = 1.0 / 16.0; SkMatrix tx = SkMatrix::MakeAll(1.0 + tweak, tweak, 5, // tweak, 1.0 + tweak, 10, // @@ -1824,6 +1831,14 @@ class CanvasCompareTester { int pixels_touched = 0; int pixels_different = 0; + // We need to allow some slight differences per component due to the + // fact that rearranging discrete calculations can compound round off + // errors. Off-by-2 is enough for 8 bit components, but for the 565 + // tests we allow at least 9 which is the maximum distance between + // samples when converted to 8 bits. (You might think it would be a + // max step of 8 converting 5 bits to 8 bits, but it is really + // converting 31 steps to 255 steps with an average step size of + // 8.23 - 24 of the steps are by 8, but 7 of them are by 9.) int fudge = env.info().bytesPerPixel() < 4 ? 9 : 2; for (int y = 0; y < TestHeight; y++) { const uint32_t* ref_row = ref_pixmap->addr32(0, y);