Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 319d563

Browse files
committed
Wire up OpacityLayer to Scenic
1 parent 9eded82 commit 319d563

11 files changed

+235
-121
lines changed

flow/layers/child_scene_layer.cc

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,25 @@ ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id,
2020
void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
2121
TRACE_EVENT0("flutter", "ChildSceneLayer::Preroll");
2222
set_needs_system_composite(true);
23+
24+
// An alpha "hole punch" is required if the frame behind us is not opaque.
25+
if (!context->is_opaque) {
26+
set_paint_bounds(
27+
SkRect::MakeXYWH(offset_.fX, offset_.fY, size_.fWidth, size_.fHeight));
28+
}
2329
}
2430

2531
void ChildSceneLayer::Paint(PaintContext& context) const {
26-
FML_NOTREACHED() << "This layer never needs painting.";
32+
TRACE_EVENT0("flutter", "ChildSceneLayer::Paint");
33+
FML_DCHECK(needs_painting());
34+
35+
// If we are being rendered into our own frame using the system compositor,
36+
// then it is neccesary to "punch a hole" in the canvas/frame behind us so
37+
// that group opacity looks correct.
38+
SkPaint paint;
39+
paint.setColor(SK_ColorTRANSPARENT);
40+
paint.setBlendMode(SkBlendMode::kSrc);
41+
context.leaf_nodes_canvas->drawRect(paint_bounds(), paint);
2742
}
2843

2944
void ChildSceneLayer::UpdateScene(SceneUpdateContext& context) {

flow/layers/fuchsia_system_composited_layer.cc

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@
77
namespace flutter {
88

99
FuchsiaSystemCompositedLayer::FuchsiaSystemCompositedLayer(SkColor color,
10+
SkAlpha opacity,
1011
float elevation)
11-
: ElevatedContainerLayer(elevation), color_(color) {}
12+
: ElevatedContainerLayer(elevation), color_(color), opacity_(opacity) {}
1213

1314
void FuchsiaSystemCompositedLayer::Preroll(PrerollContext* context,
1415
const SkMatrix& matrix) {
1516
TRACE_EVENT0("flutter", "FuchsiaSystemCompositedLayer::Preroll");
1617

18+
const float parent_is_opaque = context->is_opaque;
19+
context->mutators_stack.PushOpacity(opacity_);
20+
context->is_opaque = parent_is_opaque && (opacity_ == 255);
1721
ElevatedContainerLayer::Preroll(context, matrix);
22+
context->is_opaque = parent_is_opaque;
23+
context->mutators_stack.Pop();
1824

1925
// System-composite this layer if its elevated.
2026
if (elevation() != 0.0f) {
@@ -41,14 +47,15 @@ void FuchsiaSystemCompositedLayer::UpdateScene(SceneUpdateContext& context) {
4147

4248
TRACE_EVENT_INSTANT0("flutter", "retained cache miss, creating");
4349
// If we can't find an existing retained surface, create one.
44-
SceneUpdateContext::Frame frame(context, rrect_, color_, elevation(), this);
50+
SceneUpdateContext::Frame frame(context, rrect_, color_, opacity_ / 255.0f,
51+
elevation(), this);
4552
for (auto& layer : layers()) {
4653
if (layer->needs_painting()) {
4754
frame.AddPaintLayer(layer.get());
4855
}
4956
}
5057

51-
ContainerLayer::UpdateScene(context);
58+
ElevatedContainerLayer::UpdateScene(context);
5259
}
5360

5461
} // namespace flutter

flow/layers/fuchsia_system_composited_layer.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@ class FuchsiaSystemCompositedLayer : public ElevatedContainerLayer {
1313
public:
1414
static bool can_system_composite() { return true; }
1515

16-
FuchsiaSystemCompositedLayer(SkColor color, float elevation);
16+
FuchsiaSystemCompositedLayer(SkColor color, SkAlpha opacity, float elevation);
1717

1818
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
1919
void UpdateScene(SceneUpdateContext& context) override;
2020

2121
void set_dimensions(SkRRect rrect) { rrect_ = rrect; }
2222

2323
SkColor color() const { return color_; }
24+
SkAlpha opacity() const { return opacity_; }
2425

2526
private:
2627
SkRRect rrect_ = SkRRect::MakeEmpty();
2728
SkColor color_ = SK_ColorTRANSPARENT;
29+
SkAlpha opacity_ = 255;
2830

2931
FML_DISALLOW_COPY_AND_ASSIGN(FuchsiaSystemCompositedLayer);
3032
};

flow/layers/layer.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ struct PrerollContext {
6060
float frame_physical_depth;
6161
float frame_device_pixel_ratio;
6262

63-
// These allow us to track properties like elevation and opacity which stack
64-
// with each other during Preroll.
63+
// These allow us to track properties like elevation, opacity, and the
64+
// prescence of a platform view during Preroll.
6565
float total_elevation = 0.0f;
6666
bool has_platform_view = false;
67+
bool is_opaque = true;
6768
};
6869

6970
// Represents a single composited layer. Created on the UI thread but then

flow/layers/layer_tree.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ void LayerTree::UpdateScene(SceneUpdateContext& context,
8989
context,
9090
SkRRect::MakeRect(
9191
SkRect::MakeWH(frame_size_.width(), frame_size_.height())),
92-
SK_ColorTRANSPARENT, /* elevation */ 0.0f);
92+
SK_ColorTRANSPARENT, /* opacity */ 1.0f, /* elevation */ 0.0f);
9393
if (root_layer_->needs_system_composite()) {
9494
root_layer_->UpdateScene(context);
9595
}

flow/layers/opacity_layer.cc

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,29 @@
99

1010
namespace flutter {
1111

12-
OpacityLayer::OpacityLayer(int alpha, const SkPoint& offset)
13-
: alpha_(alpha), offset_(offset) {
12+
// The OpacityLayer has no real "elevation", but we want to avoid Z-fighting
13+
// when using the system compositor. Choose a small but non-zero value for
14+
// this.
15+
constexpr float kOpacityElevationWhenUsingSystemCompositor = 0.0001f;
16+
17+
#if !defined(OS_FUCHSIA)
18+
void OpacityLayerBase::Preroll(PrerollContext* context,
19+
const SkMatrix& matrix) {
20+
const float parent_is_opaque = context->is_opaque;
21+
22+
context->mutators_stack.PushOpacity(opacity_);
23+
context->is_opaque = parent_is_opaque && (opacity_ == 255);
24+
ContainerLayer::Preroll(context, matrix);
25+
context->is_opaque = parent_is_opaque;
26+
context->mutators_stack.Pop();
27+
}
28+
#endif
29+
30+
OpacityLayer::OpacityLayer(SkAlpha opacity, const SkPoint& offset)
31+
: OpacityLayerBase(SK_ColorBLACK,
32+
opacity,
33+
kOpacityElevationWhenUsingSystemCompositor),
34+
offset_(offset) {
1435
// Ensure OpacityLayer has only one direct child.
1536
//
1637
// This is needed to ensure that retained rendering can always be applied to
@@ -43,15 +64,30 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
4364
ContainerLayer* container = GetChildContainer();
4465
FML_DCHECK(!container->layers().empty()); // OpacityLayer can't be a leaf.
4566

67+
// Factor in the offset during Preroll. |OpacityLayerBase| will handle the
68+
// opacity.
4669
SkMatrix child_matrix = matrix;
4770
child_matrix.postTranslate(offset_.fX, offset_.fY);
4871
context->mutators_stack.PushTransform(
4972
SkMatrix::MakeTrans(offset_.fX, offset_.fY));
50-
context->mutators_stack.PushOpacity(alpha_);
51-
ContainerLayer::Preroll(context, child_matrix);
52-
context->mutators_stack.Pop();
73+
OpacityLayerBase::Preroll(context, child_matrix);
5374
context->mutators_stack.Pop();
54-
set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY));
75+
76+
// When using the system compositor, do not include the offset since we are
77+
// rendering as a separate piece of geometry and the offset will be baked into
78+
// that geometry's transform.
79+
if (OpacityLayerBase::can_system_composite()) {
80+
set_dimensions(SkRRect::MakeRect(paint_bounds()));
81+
82+
// If the frame behind us is opaque, don't punch a hole in it for group
83+
// opacity.
84+
if (context->is_opaque) {
85+
set_paint_bounds(SkRect::MakeEmpty());
86+
}
87+
set_needs_system_composite(true);
88+
} else {
89+
set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY));
90+
}
5591

5692
if (!context->has_platform_view && context->raster_cache &&
5793
SkRect::Intersects(context->cull_rect, paint_bounds())) {
@@ -63,12 +99,38 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) {
6399
}
64100
}
65101

102+
#if defined(OS_FUCHSIA)
103+
104+
void OpacityLayer::UpdateScene(SceneUpdateContext& context) {
105+
SceneUpdateContext::Transform transform(
106+
context, SkMatrix::MakeTrans(offset_.fX, offset_.fY));
107+
108+
// OpacityLayerBase will handle applying the opacity itself.
109+
OpacityLayerBase::UpdateScene(context);
110+
}
111+
112+
#endif
113+
66114
void OpacityLayer::Paint(PaintContext& context) const {
67115
TRACE_EVENT0("flutter", "OpacityLayer::Paint");
68116
FML_DCHECK(needs_painting());
69117

118+
// The compositor will paint this layer (which is |Sk_ColorBLACK| with alpha
119+
// scaled by opacity) via the model color on |SceneUpdateContext::Frame|.
120+
//
121+
// The child layers will be painted into the texture used by the Frame, so
122+
// painting them here would actually cause them to be painted on the display
123+
// twice -- once into the current canvas (which may be inside of another
124+
// Frame) and once into the Frame's texture (which is then drawn on top of the
125+
// current canvas).
126+
if (OpacityLayerBase::can_system_composite()) {
127+
FML_DCHECK(needs_system_composite());
128+
OpacityLayerBase::Paint(context);
129+
return;
130+
}
131+
70132
SkPaint paint;
71-
paint.setAlpha(alpha_);
133+
paint.setAlpha(opacity());
72134

73135
SkAutoCanvasRestore save(context.internal_nodes_canvas, true);
74136
context.internal_nodes_canvas->translate(offset_.fX, offset_.fY);
@@ -95,16 +157,15 @@ void OpacityLayer::Paint(PaintContext& context) const {
95157
// RasterCache::GetIntegralTransCTM optimization.
96158
//
97159
// Note that the following lines are only accessible when the raster cache is
98-
// not available (e.g., when we're using the software backend in golden
99-
// tests).
160+
// not available, or when a cache miss occurs.
100161
SkRect saveLayerBounds;
101162
paint_bounds()
102163
.makeOffset(-offset_.fX, -offset_.fY)
103164
.roundOut(&saveLayerBounds);
104165

105166
Layer::AutoSaveLayer save_layer =
106167
Layer::AutoSaveLayer::Create(context, saveLayerBounds, &paint);
107-
PaintChildren(context);
168+
OpacityLayerBase::Paint(context);
108169
}
109170

110171
ContainerLayer* OpacityLayer::GetChildContainer() const {

flow/layers/opacity_layer.h

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,42 @@
55
#ifndef FLUTTER_FLOW_LAYERS_OPACITY_LAYER_H_
66
#define FLUTTER_FLOW_LAYERS_OPACITY_LAYER_H_
77

8-
#include "flutter/flow/layers/container_layer.h"
8+
#include "flutter/flow/layers/elevated_container_layer.h"
9+
#if defined(OS_FUCHSIA)
10+
#include "flutter/flow/layers/fuchsia_system_composited_layer.h"
11+
#endif
912

1013
namespace flutter {
1114

15+
#if !defined(OS_FUCHSIA)
16+
class OpacityLayerBase : public ContainerLayer {
17+
public:
18+
static bool can_system_composite() { return false; }
19+
20+
OpacityLayerBase(SkColor color, SkAlpha opacity, float elevation)
21+
: color_(color), opacity_(opacity) {}
22+
23+
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
24+
25+
void set_dimensions(SkRRect rrect) {}
26+
27+
SkColor color() const { return color_; }
28+
SkAlpha opacity() const { return opacity_; }
29+
float elevation() const { return 0; }
30+
31+
private:
32+
SkColor color_;
33+
SkAlpha opacity_;
34+
};
35+
#else
36+
using OpacityLayerBase = FuchsiaSystemCompositedLayer;
37+
#endif
38+
1239
// Don't add an OpacityLayer with no children to the layer tree. Painting an
1340
// OpacityLayer is very costly due to the saveLayer call. If there's no child,
1441
// having the OpacityLayer or not has the same effect. In debug_unopt build,
1542
// |Preroll| will assert if there are no children.
16-
class OpacityLayer : public ContainerLayer {
43+
class OpacityLayer : public OpacityLayerBase {
1744
public:
1845
// An offset is provided here because OpacityLayer.addToScene method in the
1946
// Flutter framework can take an optional offset argument.
@@ -25,22 +52,20 @@ class OpacityLayer : public ContainerLayer {
2552
// the retained rendering inefficient as a small offset change could propagate
2653
// to many leaf layers. Therefore we try to capture that offset here to stop
2754
// the propagation as repainting the OpacityLayer is expensive.
28-
OpacityLayer(int alpha, const SkPoint& offset);
55+
OpacityLayer(SkAlpha alpha, const SkPoint& offset);
2956

3057
void Add(std::shared_ptr<Layer> layer) override;
3158

3259
AnalysisResult Analyze() const override;
3360
void Preroll(PrerollContext* context, const SkMatrix& matrix) override;
34-
61+
#if defined(OS_FUCHSIA)
62+
void UpdateScene(SceneUpdateContext& context) override;
63+
#endif
3564
void Paint(PaintContext& context) const override;
3665

37-
// TODO(chinmaygarde): Once SCN-139 is addressed, introduce a new node in the
38-
// session scene hierarchy.
39-
4066
private:
4167
ContainerLayer* GetChildContainer() const;
4268

43-
int alpha_;
4469
SkPoint offset_;
4570

4671
FML_DISALLOW_COPY_AND_ASSIGN(OpacityLayer);

flow/layers/physical_shape_layer.cc

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,35 @@ PhysicalShapeLayer::PhysicalShapeLayer(SkColor color,
1717
float elevation,
1818
const SkPath& path,
1919
Clip clip_behavior)
20-
: PhysicalShapeLayerBase(color, elevation),
20+
: PhysicalShapeLayerBase(color, SK_AlphaOPAQUE, elevation),
2121
shadow_color_(shadow_color),
2222
path_(path),
2323
clip_behavior_(clip_behavior) {
2424
// If rendering as a separate frame using the system compositor, then make
2525
// sure to set up the properties needed to do so.
26-
#if defined(OS_FUCHSIA)
27-
SkRect rect;
28-
SkRRect rrect;
29-
if (path.isRect(&rect)) {
30-
rrect = SkRRect::MakeRect(rect);
31-
} else if (path.isRRect(&rrect)) {
32-
// Nothing needed here, as isRRect will fill in frameRRect_ already.
33-
} else if (path.isOval(&rect)) {
34-
// isRRect returns false for ovals, so we need to explicitly check isOval
35-
// as well.
36-
rrect = SkRRect::MakeOval(rect);
37-
} else {
38-
// Scenic currently doesn't provide an easy way to create shapes from
39-
// arbitrary paths.
40-
// For shapes that cannot be represented as a rounded rectangle we
41-
// default to use the bounding rectangle.
42-
// TODO(amirh): fix this once we have a way to create a Scenic shape from
43-
// an SkPath.
44-
rrect = SkRRect::MakeRect(path.getBounds());
26+
if (PhysicalShapeLayerBase::can_system_composite()) {
27+
SkRect rect;
28+
SkRRect rrect;
29+
if (path.isRect(&rect)) {
30+
rrect = SkRRect::MakeRect(rect);
31+
} else if (path.isRRect(&rrect)) {
32+
// Nothing needed here, as isRRect will fill in frameRRect_ already.
33+
} else if (path.isOval(&rect)) {
34+
// isRRect returns false for ovals, so we need to explicitly check isOval
35+
// as well.
36+
rrect = SkRRect::MakeOval(rect);
37+
} else {
38+
// Scenic currently doesn't provide an easy way to create shapes from
39+
// arbitrary paths.
40+
// For shapes that cannot be represented as a rounded rectangle we
41+
// default to use the bounding rectangle.
42+
// TODO(amirh): fix this once we have a way to create a Scenic shape from
43+
// an SkPath.
44+
rrect = SkRRect::MakeRect(path.getBounds());
45+
}
46+
47+
set_dimensions(rrect);
4548
}
46-
47-
set_dimensions(rrect);
48-
#endif
4949
}
5050

5151
Layer::AnalysisResult PhysicalShapeLayer::Analyze() const {
@@ -61,9 +61,15 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context,
6161
TRACE_EVENT0("flutter", "PhysicalShapeLayer::Preroll");
6262
PhysicalShapeLayerBase::Preroll(context, matrix);
6363

64-
// Use the system compositor for shadows if it's available. 0-elevation
65-
// doesn't cast shadows, so it never uses the system compositor.
66-
if (PhysicalShapeLayerBase::can_system_composite() && elevation() != 0) {
64+
// Use the system compositor for shadows if it's available and we are at a
65+
// non-zero elevation. |PhysicalShapeLayerBase| has already checked the
66+
// elevation condition and set |needs_system_composite|.
67+
if (PhysicalShapeLayerBase::can_system_composite()) {
68+
// If the frame behind us is opaque, don't punch a hole in it for group
69+
// opacity.
70+
if (context->is_opaque) {
71+
set_paint_bounds(SkRect::MakeEmpty());
72+
}
6773
return;
6874
}
6975

0 commit comments

Comments
 (0)