Skip to content

Commit

Permalink
DisplayList savelayer opacity peephole optimization (#30957)
Browse files Browse the repository at this point in the history
  • Loading branch information
flar authored Feb 4, 2022
1 parent 0d39b6d commit dbe4cc8
Show file tree
Hide file tree
Showing 13 changed files with 571 additions and 78 deletions.
4 changes: 4 additions & 0 deletions display_list/display_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

namespace flutter {

const SaveLayerOptions SaveLayerOptions::kNoAttributes = SaveLayerOptions();
const SaveLayerOptions SaveLayerOptions::kWithAttributes =
kNoAttributes.with_renders_with_attributes();

const SkSamplingOptions DisplayList::NearestSampling =
SkSamplingOptions(SkFilterMode::kNearest, SkMipmapMode::kNone);
const SkSamplingOptions DisplayList::LinearSampling =
Expand Down
46 changes: 46 additions & 0 deletions display_list/display_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,52 @@ enum class DisplayListOpType { FOR_EACH_DISPLAY_LIST_OP(DL_OP_TO_ENUM_VALUE) };
class Dispatcher;
class DisplayListBuilder;

class SaveLayerOptions {
public:
static const SaveLayerOptions kWithAttributes;
static const SaveLayerOptions kNoAttributes;

SaveLayerOptions() : flags_(0) {}
SaveLayerOptions(const SaveLayerOptions& options) : flags_(options.flags_) {}
SaveLayerOptions(const SaveLayerOptions* options) : flags_(options->flags_) {}

SaveLayerOptions without_optimizations() const {
SaveLayerOptions options;
options.fRendersWithAttributes = fRendersWithAttributes;
return options;
}

bool renders_with_attributes() const { return fRendersWithAttributes; }
SaveLayerOptions with_renders_with_attributes() const {
SaveLayerOptions options(this);
options.fRendersWithAttributes = true;
return options;
}

bool can_distribute_opacity() const { return fCanDistributeOpacity; }
SaveLayerOptions with_can_distribute_opacity() const {
SaveLayerOptions options(this);
options.fCanDistributeOpacity = true;
return options;
}

bool operator==(const SaveLayerOptions& other) const {
return flags_ == other.flags_;
}
bool operator!=(const SaveLayerOptions& other) const {
return flags_ != other.flags_;
}

private:
union {
struct {
unsigned fRendersWithAttributes : 1;
unsigned fCanDistributeOpacity : 1;
};
uint32_t flags_;
};
};

// The base class that contains a sequence of rendering operations
// for dispatch to a Dispatcher. These objects must be instantiated
// through an instance of DisplayListBuilder::build().
Expand Down
43 changes: 37 additions & 6 deletions display_list/display_list_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,25 @@ void DisplayListBuilder::restore() {
layer_stack_.pop_back();
current_layer_ = &layer_stack_.back();
Push<RestoreOp>(0, 1);
if (!layer_info.has_layer) {
if (layer_info.has_layer) {
if (layer_info.is_group_opacity_compatible()) {
// We are now going to go back and modify the matching saveLayer
// call to add the option indicating it can distribute an opacity
// value to its children.
//
// Note that this operation cannot and does not change the size
// or structure of the SaveLayerOp record. It only sets an option
// flag on an existing field.
//
// Note that these kinds of modification operations on data already
// in the DisplayList are only allowed *during* the build phase.
// Once built, the DisplayList records must remain read only to
// ensure consistency of rendering and |Equals()| behavior.
SaveLayerOp* op = reinterpret_cast<SaveLayerOp*>(
storage_.get() + layer_info.save_layer_offset);
op->options = op->options.with_can_distribute_opacity();
}
} else {
// For regular save() ops there was no protecting layer so we have to
// accumulate the values into the enclosing layer.
if (layer_info.cannot_inherit_opacity) {
Expand All @@ -249,13 +267,24 @@ void DisplayListBuilder::restore() {
}
}
void DisplayListBuilder::saveLayer(const SkRect* bounds,
bool restore_with_paint) {
const SaveLayerOptions in_options) {
SaveLayerOptions options = in_options.without_optimizations();
size_t save_layer_offset = used_;
bounds //
? Push<SaveLayerBoundsOp>(0, 1, *bounds, restore_with_paint)
: Push<SaveLayerOp>(0, 1, restore_with_paint);
CheckLayerOpacityCompatibility(restore_with_paint);
layer_stack_.emplace_back(true);
? Push<SaveLayerBoundsOp>(0, 1, *bounds, options)
: Push<SaveLayerOp>(0, 1, options);
CheckLayerOpacityCompatibility(options.renders_with_attributes());
layer_stack_.emplace_back(save_layer_offset, true);
current_layer_ = &layer_stack_.back();
if (options.renders_with_attributes()) {
// |current_opacity_compatibility_| does not take an ImageFilter into
// account because an individual primitive with an ImageFilter can apply
// opacity on top of it. But, if the layer is applying the ImageFilter
// then it cannot pass the opacity on.
if (!current_opacity_compatibility_ || current_image_filter_ != nullptr) {
UpdateLayerOpacityCompatibility(false);
}
}
}

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

Expand Down
25 changes: 22 additions & 3 deletions display_list/display_list_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,17 @@ class DisplayListBuilder final : public virtual Dispatcher,
sk_sp<SkImageFilter> getImageFilter() const { return current_image_filter_; }

void save() override;
void saveLayer(const SkRect* bounds, bool restore_with_paint) override;
// Only the |renders_with_attributes()| option will be accepted here. Any
// other flags will be ignored and calculated anew as the DisplayList is
// built. Alternatively, use the |saveLayer(SkRect, bool)| method.
void saveLayer(const SkRect* bounds, const SaveLayerOptions options) override;
// Convenience method with just a boolean to indicate whether the saveLayer
// should apply the rendering attributes.
void saveLayer(const SkRect* bounds, bool renders_with_attributes) {
saveLayer(bounds, renders_with_attributes
? SaveLayerOptions::kWithAttributes
: SaveLayerOptions::kNoAttributes);
}
void restore() override;
int getSaveCount() { return layer_stack_.size(); }

Expand Down Expand Up @@ -271,11 +281,20 @@ class DisplayListBuilder final : public virtual Dispatcher,
}

struct LayerInfo {
LayerInfo(bool has_layer = false)
: has_layer(has_layer),
LayerInfo(size_t save_layer_offset = 0, bool has_layer = false)
: save_layer_offset(save_layer_offset),
has_layer(has_layer),
cannot_inherit_opacity(false),
has_compatible_op(false) {}

// The offset into the memory buffer where the saveLayer DLOp record
// for this saveLayer() call is placed. This may be needed if the
// eventual restore() call has discovered important information about
// the records inside the saveLayer that may impact how the saveLayer
// is handled (e.g., |cannot_inherit_opacity| == false).
// This offset is only valid if |has_layer| is true.
size_t save_layer_offset;

bool has_layer;
bool cannot_inherit_opacity;
bool has_compatible_op;
Expand Down
35 changes: 30 additions & 5 deletions display_list/display_list_canvas_dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,42 @@ const SkPaint* DisplayListCanvasDispatcher::safe_paint(bool use_attributes) {

void DisplayListCanvasDispatcher::save() {
canvas_->save();
save_opacity(false);
// save has no impact on attributes, but it needs to register a record
// on the restore stack so that the eventual call to restore() will
// know what to do at that time. We could annotate the restore record
// with a flag that the record came from a save call, but it is simpler
// to just pass in the current opacity value as the value to be used by
// the children and let the utility calls notice that it didn't change.
save_opacity(opacity());
}
void DisplayListCanvasDispatcher::restore() {
canvas_->restore();
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_opacity(true);
const SaveLayerOptions options) {
if (bounds == nullptr && options.can_distribute_opacity()) {
// We know that:
// - no bounds is needed for clipping here
// - the current attributes only have an alpha
// - the children are compatible with individually rendering with
// an inherited opacity
// Therefore we can just use a save instead of a saveLayer and pass the
// intended opacity to the children.
canvas_->save();
// If the saveLayer does not use attributes, the children should continue
// to render with the inherited opacity unmodified. If attributes are to
// be applied, the children should render with the combination of the
// inherited opacity combined with the alpha from the current color.
save_opacity(options.renders_with_attributes() ? combined_opacity()
: opacity());
} else {
TRACE_EVENT0("flutter", "Canvas::saveLayer");
canvas_->saveLayer(bounds, safe_paint(options.renders_with_attributes()));
// saveLayer will apply the current opacity on behalf of the children
// so they will inherit an opaque opacity.
save_opacity(SK_Scalar1);
}
}

void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) {
Expand Down
2 changes: 1 addition & 1 deletion display_list/display_list_canvas_dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class DisplayListCanvasDispatcher : public virtual Dispatcher,

void save() override;
void restore() override;
void saveLayer(const SkRect* bounds, bool restore_with_paint) override;
void saveLayer(const SkRect* bounds, const SaveLayerOptions options) override;

void translate(SkScalar tx, SkScalar ty) override;
void scale(SkScalar sx, SkScalar sy) override;
Expand Down
Loading

0 comments on commit dbe4cc8

Please sign in to comment.