Skip to content

Commit dbe4cc8

Browse files
authored
DisplayList savelayer opacity peephole optimization (flutter#30957)
1 parent 0d39b6d commit dbe4cc8

13 files changed

+571
-78
lines changed

display_list/display_list.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111

1212
namespace flutter {
1313

14+
const SaveLayerOptions SaveLayerOptions::kNoAttributes = SaveLayerOptions();
15+
const SaveLayerOptions SaveLayerOptions::kWithAttributes =
16+
kNoAttributes.with_renders_with_attributes();
17+
1418
const SkSamplingOptions DisplayList::NearestSampling =
1519
SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone);
1620
const SkSamplingOptions DisplayList::LinearSampling =

display_list/display_list.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,52 @@ enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) };
150150
class Dispatcher;
151151
class DisplayListBuilder;
152152

153+
class SaveLayerOptions {
154+
public:
155+
static const SaveLayerOptions kWithAttributes;
156+
static const SaveLayerOptions kNoAttributes;
157+
158+
SaveLayerOptions() : flags_(0) {}
159+
SaveLayerOptions(const SaveLayerOptions& options) : flags_(options.flags_) {}
160+
SaveLayerOptions(const SaveLayerOptions* options) : flags_(options->flags_) {}
161+
162+
SaveLayerOptions without_optimizations() const {
163+
SaveLayerOptions options;
164+
options.fRendersWithAttributes = fRendersWithAttributes;
165+
return options;
166+
}
167+
168+
bool renders_with_attributes() const { return fRendersWithAttributes; }
169+
SaveLayerOptions with_renders_with_attributes() const {
170+
SaveLayerOptions options(this);
171+
options.fRendersWithAttributes = true;
172+
return options;
173+
}
174+
175+
bool can_distribute_opacity() const { return fCanDistributeOpacity; }
176+
SaveLayerOptions with_can_distribute_opacity() const {
177+
SaveLayerOptions options(this);
178+
options.fCanDistributeOpacity = true;
179+
return options;
180+
}
181+
182+
bool operator==(const SaveLayerOptions& other) const {
183+
return flags_ == other.flags_;
184+
}
185+
bool operator!=(const SaveLayerOptions& other) const {
186+
return flags_ != other.flags_;
187+
}
188+
189+
private:
190+
union {
191+
struct {
192+
unsigned fRendersWithAttributes : 1;
193+
unsigned fCanDistributeOpacity : 1;
194+
};
195+
uint32_t flags_;
196+
};
197+
};
198+
153199
// The base class that contains a sequence of rendering operations
154200
// for dispatch to a Dispatcher. These objects must be instantiated
155201
// through an instance of DisplayListBuilder::build().

display_list/display_list_builder.cc

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,25 @@ void DisplayListBuilder::restore() {
237237
layer_stack_.pop_back();
238238
current_layer_ = &layer_stack_.back();
239239
Push<RestoreOp>(0, 1);
240-
if (!layer_info.has_layer) {
240+
if (layer_info.has_layer) {
241+
if (layer_info.is_group_opacity_compatible()) {
242+
// We are now going to go back and modify the matching saveLayer
243+
// call to add the option indicating it can distribute an opacity
244+
// value to its children.
245+
//
246+
// Note that this operation cannot and does not change the size
247+
// or structure of the SaveLayerOp record. It only sets an option
248+
// flag on an existing field.
249+
//
250+
// Note that these kinds of modification operations on data already
251+
// in the DisplayList are only allowed *during* the build phase.
252+
// Once built, the DisplayList records must remain read only to
253+
// ensure consistency of rendering and |Equals()| behavior.
254+
SaveLayerOp* op = reinterpret_cast<SaveLayerOp*>(
255+
storage_.get() + layer_info.save_layer_offset);
256+
op->options = op->options.with_can_distribute_opacity();
257+
}
258+
} else {
241259
// For regular save() ops there was no protecting layer so we have to
242260
// accumulate the values into the enclosing layer.
243261
if (layer_info.cannot_inherit_opacity) {
@@ -249,13 +267,24 @@ void DisplayListBuilder::restore() {
249267
}
250268
}
251269
void DisplayListBuilder::saveLayer(const SkRect* bounds,
252-
bool restore_with_paint) {
270+
const SaveLayerOptions in_options) {
271+
SaveLayerOptions options = in_options.without_optimizations();
272+
size_t save_layer_offset = used_;
253273
bounds //
254-
? Push<SaveLayerBoundsOp>(0, 1, *bounds, restore_with_paint)
255-
: Push<SaveLayerOp>(0, 1, restore_with_paint);
256-
CheckLayerOpacityCompatibility(restore_with_paint);
257-
layer_stack_.emplace_back(true);
274+
? Push<SaveLayerBoundsOp>(0, 1, *bounds, options)
275+
: Push<SaveLayerOp>(0, 1, options);
276+
CheckLayerOpacityCompatibility(options.renders_with_attributes());
277+
layer_stack_.emplace_back(save_layer_offset, true);
258278
current_layer_ = &layer_stack_.back();
279+
if (options.renders_with_attributes()) {
280+
// |current_opacity_compatibility_| does not take an ImageFilter into
281+
// account because an individual primitive with an ImageFilter can apply
282+
// opacity on top of it. But, if the layer is applying the ImageFilter
283+
// then it cannot pass the opacity on.
284+
if (!current_opacity_compatibility_ || current_image_filter_ != nullptr) {
285+
UpdateLayerOpacityCompatibility(false);
286+
}
287+
}
259288
}
260289

261290
void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) {
@@ -455,6 +484,8 @@ void DisplayListBuilder::drawVertices(const sk_sp<SkVertices> vertices,
455484
Push<DrawVerticesOp>(0, 1, std::move(vertices), mode);
456485
// DrawVertices applies its colors to the paint so we have no way
457486
// of controlling opacity using the current paint attributes.
487+
// Although, examination of the |mode| might find some predictable
488+
// cases.
458489
UpdateLayerOpacityCompatibility(false);
459490
}
460491

display_list/display_list_builder.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,17 @@ class DisplayListBuilder final : public virtual Dispatcher,
153153
sk_sp<SkImageFilter> getImageFilter() const { return current_image_filter_; }
154154

155155
void save() override;
156-
void saveLayer(const SkRect* bounds, bool restore_with_paint) override;
156+
// Only the |renders_with_attributes()| option will be accepted here. Any
157+
// other flags will be ignored and calculated anew as the DisplayList is
158+
// built. Alternatively, use the |saveLayer(SkRect, bool)| method.
159+
void saveLayer(const SkRect* bounds, const SaveLayerOptions options) override;
160+
// Convenience method with just a boolean to indicate whether the saveLayer
161+
// should apply the rendering attributes.
162+
void saveLayer(const SkRect* bounds, bool renders_with_attributes) {
163+
saveLayer(bounds, renders_with_attributes
164+
? SaveLayerOptions::kWithAttributes
165+
: SaveLayerOptions::kNoAttributes);
166+
}
157167
void restore() override;
158168
int getSaveCount() { return layer_stack_.size(); }
159169

@@ -271,11 +281,20 @@ class DisplayListBuilder final : public virtual Dispatcher,
271281
}
272282

273283
struct LayerInfo {
274-
LayerInfo(bool has_layer = false)
275-
: has_layer(has_layer),
284+
LayerInfo(size_t save_layer_offset = 0, bool has_layer = false)
285+
: save_layer_offset(save_layer_offset),
286+
has_layer(has_layer),
276287
cannot_inherit_opacity(false),
277288
has_compatible_op(false) {}
278289

290+
// The offset into the memory buffer where the saveLayer DLOp record
291+
// for this saveLayer() call is placed. This may be needed if the
292+
// eventual restore() call has discovered important information about
293+
// the records inside the saveLayer that may impact how the saveLayer
294+
// is handled (e.g., |cannot_inherit_opacity| == false).
295+
// This offset is only valid if |has_layer| is true.
296+
size_t save_layer_offset;
297+
279298
bool has_layer;
280299
bool cannot_inherit_opacity;
281300
bool has_compatible_op;

display_list/display_list_canvas_dispatcher.cc

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,42 @@ const SkPaint* DisplayListCanvasDispatcher::safe_paint(bool use_attributes) {
2626

2727
void DisplayListCanvasDispatcher::save() {
2828
canvas_->save();
29-
save_opacity(false);
29+
// save has no impact on attributes, but it needs to register a record
30+
// on the restore stack so that the eventual call to restore() will
31+
// know what to do at that time. We could annotate the restore record
32+
// with a flag that the record came from a save call, but it is simpler
33+
// to just pass in the current opacity value as the value to be used by
34+
// the children and let the utility calls notice that it didn't change.
35+
save_opacity(opacity());
3036
}
3137
void DisplayListCanvasDispatcher::restore() {
3238
canvas_->restore();
3339
restore_opacity();
3440
}
3541
void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds,
36-
bool restore_with_paint) {
37-
TRACE_EVENT0("flutter", "Canvas::saveLayer");
38-
canvas_->saveLayer(bounds, safe_paint(restore_with_paint));
39-
save_opacity(true);
42+
const SaveLayerOptions options) {
43+
if (bounds == nullptr && options.can_distribute_opacity()) {
44+
// We know that:
45+
// - no bounds is needed for clipping here
46+
// - the current attributes only have an alpha
47+
// - the children are compatible with individually rendering with
48+
// an inherited opacity
49+
// Therefore we can just use a save instead of a saveLayer and pass the
50+
// intended opacity to the children.
51+
canvas_->save();
52+
// If the saveLayer does not use attributes, the children should continue
53+
// to render with the inherited opacity unmodified. If attributes are to
54+
// be applied, the children should render with the combination of the
55+
// inherited opacity combined with the alpha from the current color.
56+
save_opacity(options.renders_with_attributes() ? combined_opacity()
57+
: opacity());
58+
} else {
59+
TRACE_EVENT0("flutter", "Canvas::saveLayer");
60+
canvas_->saveLayer(bounds, safe_paint(options.renders_with_attributes()));
61+
// saveLayer will apply the current opacity on behalf of the children
62+
// so they will inherit an opaque opacity.
63+
save_opacity(SK_Scalar1);
64+
}
4065
}
4166

4267
void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) {

display_list/display_list_canvas_dispatcher.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher,
2929

3030
void save() override;
3131
void restore() override;
32-
void saveLayer(const SkRect* bounds, bool restore_with_paint) override;
32+
void saveLayer(const SkRect* bounds, const SaveLayerOptions options) override;
3333

3434
void translate(SkScalar tx, SkScalar ty) override;
3535
void scale(SkScalar sx, SkScalar sy) override;

0 commit comments

Comments
 (0)