diff --git a/flow/layers/child_scene_layer.cc b/flow/layers/child_scene_layer.cc index e5652a2c87889..10b5a277db575 100644 --- a/flow/layers/child_scene_layer.cc +++ b/flow/layers/child_scene_layer.cc @@ -20,10 +20,25 @@ ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id, void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ChildSceneLayer::Preroll"); set_needs_system_composite(true); + + // An alpha "hole punch" is required if the frame behind us is not opaque. + if (!context->is_opaque) { + set_paint_bounds( + SkRect::MakeXYWH(offset_.fX, offset_.fY, size_.fWidth, size_.fHeight)); + } } void ChildSceneLayer::Paint(PaintContext& context) const { - FML_NOTREACHED() << "This layer never needs painting."; + TRACE_EVENT0("flutter", "ChildSceneLayer::Paint"); + FML_DCHECK(needs_painting()); + + // If we are being rendered into our own frame using the system compositor, + // then it is neccesary to "punch a hole" in the canvas/frame behind us so + // that group opacity looks correct. + SkPaint paint; + paint.setColor(SK_ColorTRANSPARENT); + paint.setBlendMode(SkBlendMode::kSrc); + context.leaf_nodes_canvas->drawRect(paint_bounds(), paint); } void ChildSceneLayer::UpdateScene(SceneUpdateContext& context) { diff --git a/flow/layers/fuchsia_system_composited_layer.cc b/flow/layers/fuchsia_system_composited_layer.cc index 68209b0820b6d..8c4a1b2ae26e3 100644 --- a/flow/layers/fuchsia_system_composited_layer.cc +++ b/flow/layers/fuchsia_system_composited_layer.cc @@ -7,8 +7,21 @@ namespace flutter { FuchsiaSystemCompositedLayer::FuchsiaSystemCompositedLayer(SkColor color, + SkAlpha opacity, float elevation) - : ElevatedContainerLayer(elevation), color_(color) {} + : ElevatedContainerLayer(elevation), color_(color), opacity_(opacity) {} + +void FuchsiaSystemCompositedLayer::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + TRACE_EVENT0("flutter", "FuchsiaSystemCompositedLayer::Preroll"); + + const float parent_is_opaque = context->is_opaque; + context->mutators_stack.PushOpacity(opacity_); + context->is_opaque = parent_is_opaque && (opacity_ == SK_AlphaOPAQUE); + ElevatedContainerLayer::Preroll(context, matrix); + context->is_opaque = parent_is_opaque; + context->mutators_stack.Pop(); +} void FuchsiaSystemCompositedLayer::UpdateScene(SceneUpdateContext& context) { FML_DCHECK(needs_system_composite()); @@ -28,14 +41,15 @@ void FuchsiaSystemCompositedLayer::UpdateScene(SceneUpdateContext& context) { TRACE_EVENT_INSTANT0("flutter", "retained cache miss, creating"); // If we can't find an existing retained surface, create one. - SceneUpdateContext::Frame frame(context, rrect_, color_, elevation(), this); + SceneUpdateContext::Frame frame(context, rrect_, color_, opacity_ / 255.0f, + elevation(), this); for (auto& layer : layers()) { if (layer->needs_painting()) { frame.AddPaintLayer(layer.get()); } } - ContainerLayer::UpdateScene(context); + ElevatedContainerLayer::UpdateScene(context); } } // namespace flutter diff --git a/flow/layers/fuchsia_system_composited_layer.h b/flow/layers/fuchsia_system_composited_layer.h index f2ceeb2536b6e..2fe00ee6d550f 100644 --- a/flow/layers/fuchsia_system_composited_layer.h +++ b/flow/layers/fuchsia_system_composited_layer.h @@ -14,17 +14,20 @@ class FuchsiaSystemCompositedLayer : public ElevatedContainerLayer { public: static bool can_system_composite() { return true; } - FuchsiaSystemCompositedLayer(SkColor color, float elevation); + FuchsiaSystemCompositedLayer(SkColor color, SkAlpha opacity, float elevation); + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; void UpdateScene(SceneUpdateContext& context) override; void set_dimensions(SkRRect rrect) { rrect_ = rrect; } SkColor color() const { return color_; } + SkAlpha opacity() const { return opacity_; } private: SkRRect rrect_ = SkRRect::MakeEmpty(); SkColor color_ = SK_ColorTRANSPARENT; + SkAlpha opacity_ = SK_AlphaOPAQUE; FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaSystemCompositedLayer); }; diff --git a/flow/layers/layer.h b/flow/layers/layer.h index ca7eecd5507fc..63c7cdb316d7e 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -61,10 +61,11 @@ struct PrerollContext { float frame_physical_depth; float frame_device_pixel_ratio; - // These allow us to track properties like elevation and opacity which stack - // with each other during Preroll. + // These allow us to track properties like elevation, opacity, and the + // prescence of a platform view during Preroll. float total_elevation = 0.0f; bool has_platform_view = false; + bool is_opaque = true; }; // Represents a single composited layer. Created on the UI thread but then diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 32f57179a3756..5892fff7792a6 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -9,8 +9,29 @@ namespace flutter { -OpacityLayer::OpacityLayer(int alpha, const SkPoint& offset) - : alpha_(alpha), offset_(offset) { +// The OpacityLayer has no real "elevation", but we want to avoid Z-fighting +// when using the system compositor. Choose a small but non-zero value for +// this. +constexpr float kOpacityElevationWhenUsingSystemCompositor = 0.01f; + +#if !defined(OS_FUCHSIA) +void OpacityLayerBase::Preroll(PrerollContext* context, + const SkMatrix& matrix) { + const float parent_is_opaque = context->is_opaque; + + context->mutators_stack.PushOpacity(opacity_); + context->is_opaque = parent_is_opaque && (opacity_ == SK_AlphaOPAQUE); + ContainerLayer::Preroll(context, matrix); + context->is_opaque = parent_is_opaque; + context->mutators_stack.Pop(); +} +#endif + +OpacityLayer::OpacityLayer(SkAlpha opacity, const SkPoint& offset) + : OpacityLayerBase(SK_ColorTRANSPARENT, + opacity, + kOpacityElevationWhenUsingSystemCompositor), + offset_(offset) { // Ensure OpacityLayer has only one direct child. // // This is needed to ensure that retained rendering can always be applied to @@ -31,34 +52,54 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { ContainerLayer* container = GetChildContainer(); FML_DCHECK(!container->layers().empty()); // OpacityLayer can't be a leaf. + // Factor in the offset during Preroll. |OpacityLayerBase| will handle the + // opacity. SkMatrix child_matrix = matrix; child_matrix.postTranslate(offset_.fX, offset_.fY); context->mutators_stack.PushTransform( SkMatrix::MakeTrans(offset_.fX, offset_.fY)); - context->mutators_stack.PushOpacity(alpha_); Layer::AutoPrerollSaveLayerState save = Layer::AutoPrerollSaveLayerState::Create(context); - ContainerLayer::Preroll(context, child_matrix); - context->mutators_stack.Pop(); + OpacityLayerBase::Preroll(context, child_matrix); context->mutators_stack.Pop(); - set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); - if (!context->has_platform_view && context->raster_cache && - SkRect::Intersects(context->cull_rect, paint_bounds())) { - SkMatrix ctm = child_matrix; + // When using the system compositor, do not include the offset since we are + // rendering as a separate piece of geometry and the offset will be baked into + // that geometry's transform. + if (OpacityLayerBase::can_system_composite() && needs_system_composite()) { + set_dimensions(SkRRect::MakeRect(paint_bounds())); + } else { + set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); + + if (!context->has_platform_view && context->raster_cache && + SkRect::Intersects(context->cull_rect, paint_bounds())) { + SkMatrix ctm = child_matrix; #ifndef SUPPORT_FRACTIONAL_TRANSLATION - ctm = RasterCache::GetIntegralTransCTM(ctm); + ctm = RasterCache::GetIntegralTransCTM(ctm); #endif - context->raster_cache->Prepare(context, container, ctm); + context->raster_cache->Prepare(context, container, ctm); + } } } +#if defined(OS_FUCHSIA) + +void OpacityLayer::UpdateScene(SceneUpdateContext& context) { + SceneUpdateContext::Transform transform( + context, SkMatrix::MakeTrans(offset_.fX, offset_.fY)); + + // OpacityLayerBase will handle applying the opacity itself. + OpacityLayerBase::UpdateScene(context); +} + +#endif + void OpacityLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "OpacityLayer::Paint"); FML_DCHECK(needs_painting()); SkPaint paint; - paint.setAlpha(alpha_); + paint.setAlpha(opacity()); SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); @@ -85,8 +126,7 @@ void OpacityLayer::Paint(PaintContext& context) const { // RasterCache::GetIntegralTransCTM optimization. // // Note that the following lines are only accessible when the raster cache is - // not available (e.g., when we're using the software backend in golden - // tests). + // not available, or when a cache miss occurs. SkRect saveLayerBounds; paint_bounds() .makeOffset(-offset_.fX, -offset_.fY) @@ -94,7 +134,7 @@ void OpacityLayer::Paint(PaintContext& context) const { Layer::AutoSaveLayer save_layer = Layer::AutoSaveLayer::Create(context, saveLayerBounds, &paint); - PaintChildren(context); + OpacityLayerBase::Paint(context); } ContainerLayer* OpacityLayer::GetChildContainer() const { diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index 92ae4cd1eadc7..f4951e0557c70 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -5,15 +5,42 @@ #ifndef FLUTTER_FLOW_LAYERS_OPACITY_LAYER_H_ #define FLUTTER_FLOW_LAYERS_OPACITY_LAYER_H_ -#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/elevated_container_layer.h" +#if defined(OS_FUCHSIA) +#include "flutter/flow/layers/fuchsia_system_composited_layer.h" +#endif namespace flutter { +#if !defined(OS_FUCHSIA) +class OpacityLayerBase : public ContainerLayer { + public: + static bool can_system_composite() { return false; } + + OpacityLayerBase(SkColor color, SkAlpha opacity, float elevation) + : color_(color), opacity_(opacity) {} + + void Preroll(PrerollContext* context, const SkMatrix& matrix) override; + + void set_dimensions(SkRRect rrect) {} + + SkColor color() const { return color_; } + SkAlpha opacity() const { return opacity_; } + float elevation() const { return 0; } + + private: + SkColor color_; + SkAlpha opacity_; +}; +#else +using OpacityLayerBase = FuchsiaSystemCompositedLayer; +#endif + // Don't add an OpacityLayer with no children to the layer tree. Painting an // OpacityLayer is very costly due to the saveLayer call. If there's no child, // having the OpacityLayer or not has the same effect. In debug_unopt build, // |Preroll| will assert if there are no children. -class OpacityLayer : public ContainerLayer { +class OpacityLayer : public OpacityLayerBase { public: // An offset is provided here because OpacityLayer.addToScene method in the // Flutter framework can take an optional offset argument. @@ -25,21 +52,19 @@ class OpacityLayer : public ContainerLayer { // the retained rendering inefficient as a small offset change could propagate // to many leaf layers. Therefore we try to capture that offset here to stop // the propagation as repainting the OpacityLayer is expensive. - OpacityLayer(int alpha, const SkPoint& offset); + OpacityLayer(SkAlpha alpha, const SkPoint& offset); void Add(std::shared_ptr layer) override; void Preroll(PrerollContext* context, const SkMatrix& matrix) override; - +#if defined(OS_FUCHSIA) + void UpdateScene(SceneUpdateContext& context) override; +#endif void Paint(PaintContext& context) const override; - // TODO(chinmaygarde): Once SCN-139 is addressed, introduce a new node in the - // session scene hierarchy. - private: ContainerLayer* GetChildContainer() const; - int alpha_; SkPoint offset_; FML_DISALLOW_COPY_AND_ASSIGN(OpacityLayer); diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index a3d1f70f0c09d..524c5a2ac939a 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -17,7 +17,11 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color, float elevation, const SkPath& path, Clip clip_behavior) +#if !defined(OS_FUCHSIA) : PhysicalShapeLayerBase(color, elevation), +#else + : PhysicalShapeLayerBase(color, /*opacity=*/1.f, elevation), +#endif shadow_color_(shadow_color), path_(path), isRect_(false), @@ -94,8 +98,9 @@ void PhysicalShapeLayer::UpdateScene(SceneUpdateContext& context) { TRACE_EVENT_INSTANT0("flutter", "cache miss, creating"); // If we can't find an existing retained surface, create one. - SceneUpdateContext::Frame frame(context, frameRRect_, color(), elevation(), - this); + SceneUpdateContext::Frame frame(context, frameRRect_, color(), opacity(), + elevation(), this); + for (auto& layer : layers()) { if (layer->needs_painting()) { frame.AddPaintLayer(layer.get()); diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index c52f1ca9eb4ae..5ffcc4b272ce9 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -7,6 +7,7 @@ #include "flutter/flow/layers/layer.h" #include "flutter/flow/matrix_decomposition.h" #include "flutter/fml/trace_event.h" +#include "include/core/SkColor.h" namespace flutter { @@ -59,18 +60,16 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, scenic::ShapeNode shape_node, const SkRRect& rrect, SkColor color, + float opacity, const SkRect& paint_bounds, std::vector paint_layers, Layer* layer) { - // Frames always clip their children. - SetEntityNodeClipPlanes(entity_node, rrect.getBounds()); - // TODO(SCN-1274): SetClip() will be deleted. - entity_node.SetClip(0u, true /* clip to self */); - // We don't need a shape if the frame is zero size. if (rrect.isEmpty()) return; + SetEntityNodeClipPlanes(entity_node, rrect.getBounds()); + // isEmpty should account for this, but we are adding these experimental // checks to validate if this is the root cause for b/144933519. if (std::isnan(rrect.width()) || std::isnan(rrect.height())) { @@ -102,7 +101,9 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, // Check whether a solid color will suffice. if (paint_layers.empty()) { - SetShapeColor(shape_node, color); + scenic::Material material(session_); + SetMaterialColor(material, color, opacity); + shape_node.SetMaterial(material); return; } @@ -110,43 +111,38 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, const float scale_x = ScaleX(); const float scale_y = ScaleY(); - // Apply a texture to the whole shape. - SetShapeTextureAndColor(shape_node, color, scale_x, scale_y, shape_bounds, - std::move(paint_layers), layer, - std::move(entity_node)); -} - -void SceneUpdateContext::SetShapeTextureAndColor( - scenic::ShapeNode& shape_node, - SkColor color, - SkScalar scale_x, - SkScalar scale_y, - const SkRect& paint_bounds, - std::vector paint_layers, - Layer* layer, - scenic::EntityNode entity_node) { scenic::Image* image = GenerateImageIfNeeded( - color, scale_x, scale_y, paint_bounds, std::move(paint_layers), layer, + color, scale_x, scale_y, shape_bounds, std::move(paint_layers), layer, std::move(entity_node)); if (image != nullptr) { scenic::Material material(session_); + + // The final shape's color is material_color * texture_color. The passed in + // material color was already used as a background when generating the + // texture, so set the model color to |SK_ColorWHITE| in order to allow + // using the texture's color unmodified. + SetMaterialColor(material, SK_ColorWHITE, opacity); material.SetTexture(*image); shape_node.SetMaterial(material); return; } - SetShapeColor(shape_node, color); -} + // No texture was needed, so apply a solid color to the whole shape. + if (SkColorGetA(color) != 0 && opacity != 0.0f) { + scenic::Material material(session_); -void SceneUpdateContext::SetShapeColor(scenic::ShapeNode& shape_node, - SkColor color) { - if (SkColorGetA(color) == 0) + SetMaterialColor(material, color, opacity); + shape_node.SetMaterial(material); return; + } +} - scenic::Material material(session_); +void SceneUpdateContext::SetMaterialColor(scenic::Material& material, + SkColor color, + float opacity) { + const SkAlpha color_alpha = (SkAlpha)(SkColorGetA(color) * opacity); material.SetColor(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), - SkColorGetA(color)); - shape_node.SetMaterial(material); + color_alpha); } scenic::Image* SceneUpdateContext::GenerateImageIfNeeded( @@ -235,6 +231,7 @@ SceneUpdateContext::Entity::Entity(SceneUpdateContext& context) entity_node_(context.session()) { if (previous_entity_) previous_entity_->embedder_node().AddChild(entity_node_); + context.top_entity_ = this; } @@ -293,28 +290,37 @@ SceneUpdateContext::Transform::~Transform() { context().top_scale_y_ = previous_scale_y_; } -SceneUpdateContext::Shape::Shape(SceneUpdateContext& context) - : Entity(context), shape_node_(context.session()) { - entity_node().AddChild(shape_node_); +SceneUpdateContext::Clip::Clip(SceneUpdateContext& context, + const SkRect& shape_bounds) + : Entity(context) { + SetEntityNodeClipPlanes(entity_node(), shape_bounds); } SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, const SkRRect& rrect, SkColor color, + float opacity, float elevation, Layer* layer) - : Shape(context), + : Entity(context), + opacity_node_(context.session()), + shape_node_(context.session()), + layer_(layer), rrect_(rrect), - color_(color), paint_bounds_(SkRect::MakeEmpty()), - layer_(layer) { + color_(color), + opacity_(opacity) { entity_node().SetTranslation(0.f, 0.f, -elevation); + + entity_node().AddChild(shape_node_); + entity_node().AddChild(opacity_node_); + opacity_node_.SetOpacity(opacity_); } SceneUpdateContext::Frame::~Frame() { - context().CreateFrame(std::move(entity_node()), std::move(shape_node()), - rrect_, color_, paint_bounds_, std::move(paint_layers_), - layer_); + context().CreateFrame(std::move(entity_node()), std::move(shape_node_), + rrect_, color_, opacity_, paint_bounds_, + std::move(paint_layers_), layer_); } void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) { @@ -323,10 +329,4 @@ void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) { paint_bounds_.join(layer->paint_bounds()); } -SceneUpdateContext::Clip::Clip(SceneUpdateContext& context, - const SkRect& shape_bounds) - : Entity(context) { - SetEntityNodeClipPlanes(entity_node(), shape_bounds); -} - } // namespace flutter diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index 6961d6f72c554..c992fa20bba36 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -89,25 +89,20 @@ class SceneUpdateContext { float scale_x, float scale_y, float scale_z); - virtual ~Transform(); + ~Transform() override; private: float const previous_scale_x_; float const previous_scale_y_; }; - class Shape : public Entity { + class Clip : public Entity { public: - Shape(SceneUpdateContext& context); - virtual ~Shape() = default; - - scenic::ShapeNode& shape_node() { return shape_node_; } - - private: - scenic::ShapeNode shape_node_; + Clip(SceneUpdateContext& context, const SkRect& shape_bounds); + ~Clip() override = default; }; - class Frame : public Shape { + class Frame : public Entity { public: // When layer is not nullptr, the frame is associated with a layer subtree // rooted with that layer. The frame may then create a surface that will be @@ -115,25 +110,25 @@ class SceneUpdateContext { Frame(SceneUpdateContext& context, const SkRRect& rrect, SkColor color, + float opacity = 1.0f, float elevation = 0.0f, Layer* layer = nullptr); - virtual ~Frame(); + ~Frame() override; + scenic::ContainerNode& embedder_node() override { return opacity_node_; } void AddPaintLayer(Layer* layer); private: - const SkRRect rrect_; - SkColor const color_; + scenic::OpacityNodeHACK opacity_node_; + scenic::ShapeNode shape_node_; std::vector paint_layers_; - SkRect paint_bounds_; Layer* layer_; - }; - class Clip : public Entity { - public: - Clip(SceneUpdateContext& context, const SkRect& shape_bounds); - ~Clip() = default; + SkRRect rrect_; + SkRect paint_bounds_; + SkColor color_; + float opacity_; }; SceneUpdateContext(scenic::Session* session, @@ -206,6 +201,7 @@ class SceneUpdateContext { scenic::ShapeNode shape_node, const SkRRect& rrect, SkColor color, + float opacity, const SkRect& paint_bounds, std::vector paint_layers, Layer* layer); @@ -217,7 +213,9 @@ class SceneUpdateContext { std::vector paint_layers, Layer* layer, scenic::EntityNode entity_node); - void SetShapeColor(scenic::ShapeNode& shape_node, SkColor color); + void SetMaterialColor(scenic::Material& material, + SkColor color, + float opacity); scenic::Image* GenerateImageIfNeeded(SkColor color, SkScalar scale_x, SkScalar scale_y,