Wire up OpacityLayer to Scenic (flutter/engine#14577)

A previous version of this change also removed
system compositing of PhysicalShapeLayers on
Fuchsia. In this reland, keep using system
composting for PhysicalShapeLayers.

Co-authored-by: David Worsham <arbreng@gmail.com>
This commit is contained in:
mikejurka 2019-12-19 23:25:43 +01:00 committed by GitHub
parent b44f4ae767
commit 36839c42a7
9 changed files with 199 additions and 98 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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);
};

View File

@ -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

View File

@ -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);
OpacityLayerBase::Preroll(context, child_matrix);
context->mutators_stack.Pop();
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 {

View File

@ -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> 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);

View File

@ -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());

View File

@ -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<Layer*> 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<Layer*> 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_);
SetMaterialColor(material, color, opacity);
shape_node.SetMaterial(material);
return;
}
}
void SceneUpdateContext::SetShapeColor(scenic::ShapeNode& shape_node,
SkColor color) {
if (SkColorGetA(color) == 0)
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

View File

@ -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<Layer*> 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<Layer*> paint_layers,
Layer* layer);
@ -217,7 +213,9 @@ class SceneUpdateContext {
std::vector<Layer*> 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,