Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opacity peephole optimization #29775

Merged
merged 13 commits into from
Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion common/graphics/texture.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class Texture {
const SkRect& bounds,
bool freeze,
GrDirectContext* context,
const SkSamplingOptions& sampling) = 0;
const SkSamplingOptions& sampling,
const SkPaint* paint = nullptr) = 0;

// Called from raster thread.
virtual void OnGrContextCreated() = 0;
Expand Down
93 changes: 82 additions & 11 deletions flow/display_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -978,8 +978,8 @@ static bool CompareOps(uint8_t* ptrA,
return true;
}

void DisplayList::RenderTo(SkCanvas* canvas) const {
DisplayListCanvasDispatcher dispatcher(canvas);
void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity) const {
DisplayListCanvasDispatcher dispatcher(canvas, opacity);
Dispatch(dispatcher);
}

Expand All @@ -995,19 +995,31 @@ bool DisplayList::Equals(const DisplayList& other) const {
return CompareOps(ptr, ptr + byte_count_, o_ptr, o_ptr + other.byte_count_);
}

DisplayList::DisplayList()
: byte_count_(0),
op_count_(0),
nested_byte_count_(0),
nested_op_count_(0),
unique_id_(0),
bounds_({0, 0, 0, 0}),
bounds_cull_({0, 0, 0, 0}),
can_apply_group_opacity_(true) {}

DisplayList::DisplayList(uint8_t* ptr,
size_t byte_count,
int op_count,
size_t nested_byte_count,
int nested_op_count,
const SkRect& cull_rect)
const SkRect& cull_rect,
bool can_apply_group_opacity)
: storage_(ptr),
byte_count_(byte_count),
op_count_(op_count),
nested_byte_count_(nested_byte_count),
nested_op_count_(nested_op_count),
bounds_({0, 0, -1, -1}),
bounds_cull_(cull_rect) {
bounds_cull_(cull_rect),
can_apply_group_opacity_(can_apply_group_opacity) {
static std::atomic<uint32_t> nextID{1};
do {
unique_id_ = nextID.fetch_add(+1, std::memory_order_relaxed);
Expand Down Expand Up @@ -1057,7 +1069,7 @@ void* DisplayListBuilder::Push(size_t pod, int op_inc, Args&&... args) {
}

sk_sp<DisplayList> DisplayListBuilder::Build() {
while (save_level_ > 0) {
while (layer_stack_.size() > 1) {
restore();
}
size_t bytes = used_;
Expand All @@ -1067,13 +1079,17 @@ sk_sp<DisplayList> DisplayListBuilder::Build() {
used_ = allocated_ = op_count_ = 0;
nested_bytes_ = nested_op_count_ = 0;
storage_.realloc(bytes);
bool compatible = layer_stack_.back().is_group_opacity_compatible();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flar Hey, Could I ask you a question? How do i understand the compatible? and another method add_compatible_op(). Thank you

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These methods (and variable names) are specific to group opacity compatibility. It's the only peephole optimization we make currently, but more should come.

These optimizations are cases where we can elide a saveLayer because the modification that would have been applied to the rendering inside the layer can instead be applied to each operation individually without any changes to the result. It is common to want to fade an entire UI component by surrounding it with a "saveLayer with alpha". But if the component is a single rendering operation or a few non-overlapping operations, then we can instead apply the alpha to each rendering operation. The code that deals with the group opacity compatibility is looking for such "single or non-overlapping rendering operations that can take an alpha to reduce their opacity" cases.

return sk_sp<DisplayList>(new DisplayList(storage_.release(), bytes, count,
nested_bytes, nested_count,
cull_rect_));
cull_rect_, compatible));
}

DisplayListBuilder::DisplayListBuilder(const SkRect& cull_rect)
: cull_rect_(cull_rect) {}
: cull_rect_(cull_rect) {
layer_stack_.emplace_back();
current_layer_ = &layer_stack_.back();
}

DisplayListBuilder::~DisplayListBuilder() {
uint8_t* ptr = storage_.get();
Expand All @@ -1090,6 +1106,7 @@ void DisplayListBuilder::onSetDither(bool dither) {
}
void DisplayListBuilder::onSetInvertColors(bool invert) {
Push<SetInvertColorsOp>(0, 0, current_invert_colors_ = invert);
UpdateCurrentOpacityCompatibility();
}
void DisplayListBuilder::onSetStrokeCap(SkPaint::Cap cap) {
Push<SetStrokeCapOp>(0, 0, current_stroke_cap_ = cap);
Expand All @@ -1112,6 +1129,7 @@ void DisplayListBuilder::onSetColor(SkColor color) {
void DisplayListBuilder::onSetBlendMode(SkBlendMode mode) {
current_blender_ = nullptr;
Push<SetBlendModeOp>(0, 0, current_blend_mode_ = mode);
UpdateCurrentOpacityCompatibility();
}
void DisplayListBuilder::onSetBlender(sk_sp<SkBlender> blender) {
// setBlender(nullptr) should be redirected to setBlendMode(SrcOver)
Expand All @@ -1126,6 +1144,7 @@ void DisplayListBuilder::onSetBlender(sk_sp<SkBlender> blender) {
(current_blender_ = blender) //
? Push<SetBlenderOp>(0, 0, std::move(blender))
: Push<ClearBlenderOp>(0, 0);
UpdateCurrentOpacityCompatibility();
}
}
void DisplayListBuilder::onSetShader(sk_sp<SkShader> shader) {
Expand All @@ -1142,6 +1161,7 @@ void DisplayListBuilder::onSetColorFilter(sk_sp<SkColorFilter> filter) {
(current_color_filter_ = filter) //
? Push<SetColorFilterOp>(0, 0, std::move(filter))
: Push<ClearColorFilterOp>(0, 0);
UpdateCurrentOpacityCompatibility();
}
void DisplayListBuilder::onSetPathEffect(sk_sp<SkPathEffect> effect) {
(current_path_effect_ = effect) //
Expand Down Expand Up @@ -1228,21 +1248,37 @@ void DisplayListBuilder::setAttributesFromPaint(
}

void DisplayListBuilder::save() {
save_level_++;
Push<SaveOp>(0, 1);
layer_stack_.emplace_back();
current_layer_ = &layer_stack_.back();
}
void DisplayListBuilder::restore() {
if (save_level_ > 0) {
if (layer_stack_.size() > 1) {
// Grab the current layer info before we push the restore
// on the stack.
LayerInfo layer_info = layer_stack_.back();
layer_stack_.pop_back();
current_layer_ = &layer_stack_.back();
Push<RestoreOp>(0, 1);
save_level_--;
if (!layer_info.has_layer) {
// 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) {
current_layer_->mark_incompatible();
} else if (layer_info.has_compatible_op) {
current_layer_->add_compatible_op();
}
}
}
}
void DisplayListBuilder::saveLayer(const SkRect* bounds,
bool restore_with_paint) {
save_level_++;
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);
current_layer_ = &layer_stack_.back();
}

void DisplayListBuilder::translate(SkScalar tx, SkScalar ty) {
Expand Down Expand Up @@ -1356,21 +1392,27 @@ void DisplayListBuilder::clipPath(const SkPath& path,

void DisplayListBuilder::drawPaint() {
Push<DrawPaintOp>(0, 1);
CheckLayerOpacityCompatibility();
}
void DisplayListBuilder::drawColor(SkColor color, SkBlendMode mode) {
Push<DrawColorOp>(0, 1, color, mode);
CheckLayerOpacityCompatibility(mode);
}
void DisplayListBuilder::drawLine(const SkPoint& p0, const SkPoint& p1) {
Push<DrawLineOp>(0, 1, p0, p1);
CheckLayerOpacityCompatibility();
}
void DisplayListBuilder::drawRect(const SkRect& rect) {
Push<DrawRectOp>(0, 1, rect);
CheckLayerOpacityCompatibility();
}
void DisplayListBuilder::drawOval(const SkRect& bounds) {
Push<DrawOvalOp>(0, 1, bounds);
CheckLayerOpacityCompatibility();
}
void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) {
Push<DrawCircleOp>(0, 1, center, radius);
CheckLayerOpacityCompatibility();
}
void DisplayListBuilder::drawRRect(const SkRRect& rrect) {
if (rrect.isRect()) {
Expand All @@ -1379,21 +1421,29 @@ void DisplayListBuilder::drawRRect(const SkRRect& rrect) {
drawOval(rrect.rect());
} else {
Push<DrawRRectOp>(0, 1, rrect);
CheckLayerOpacityCompatibility();
}
}
void DisplayListBuilder::drawDRRect(const SkRRect& outer,
const SkRRect& inner) {
Push<DrawDRRectOp>(0, 1, outer, inner);
CheckLayerOpacityCompatibility();
}
void DisplayListBuilder::drawPath(const SkPath& path) {
Push<DrawPathOp>(0, 1, path);
CheckLayerOpacityHairlineCompatibility();
}

void DisplayListBuilder::drawArc(const SkRect& bounds,
SkScalar start,
SkScalar sweep,
bool useCenter) {
Push<DrawArcOp>(0, 1, bounds, start, sweep, useCenter);
if (useCenter) {
CheckLayerOpacityHairlineCompatibility();
} else {
CheckLayerOpacityCompatibility();
}
}
void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode,
uint32_t count,
Expand All @@ -1416,10 +1466,19 @@ void DisplayListBuilder::drawPoints(SkCanvas::PointMode mode,
return;
}
CopyV(data_ptr, pts, count);
// drawPoints treats every point or line (or segment of a polygon)
// as a completely separate operation meaning we cannot ensure
// distribution of group opacity without analyzing the mode and the
// bounds of every sub-primitive.
// See: https://fiddle.skia.org/c/228459001d2de8db117ce25ef5cedb0c
UpdateLayerOpacityCompatibility(false);
flar marked this conversation as resolved.
Show resolved Hide resolved
}
void DisplayListBuilder::drawVertices(const sk_sp<SkVertices> vertices,
SkBlendMode mode) {
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.
UpdateLayerOpacityCompatibility(false);
}

void DisplayListBuilder::drawImage(const sk_sp<SkImage> image,
Expand All @@ -1429,6 +1488,7 @@ void DisplayListBuilder::drawImage(const sk_sp<SkImage> image,
render_with_attributes
? Push<DrawImageWithAttrOp>(0, 1, std::move(image), point, sampling)
: Push<DrawImageOp>(0, 1, std::move(image), point, sampling);
CheckLayerOpacityCompatibility(render_with_attributes);
}
void DisplayListBuilder::drawImageRect(const sk_sp<SkImage> image,
const SkRect& src,
Expand All @@ -1438,6 +1498,7 @@ void DisplayListBuilder::drawImageRect(const sk_sp<SkImage> image,
SkCanvas::SrcRectConstraint constraint) {
Push<DrawImageRectOp>(0, 1, std::move(image), src, dst, sampling,
render_with_attributes, constraint);
CheckLayerOpacityCompatibility(render_with_attributes);
}
void DisplayListBuilder::drawImageNine(const sk_sp<SkImage> image,
const SkIRect& center,
Expand All @@ -1448,6 +1509,7 @@ void DisplayListBuilder::drawImageNine(const sk_sp<SkImage> image,
? Push<DrawImageNineWithAttrOp>(0, 1, std::move(image), center, dst,
filter)
: Push<DrawImageNineOp>(0, 1, std::move(image), center, dst, filter);
CheckLayerOpacityCompatibility(render_with_attributes);
}
void DisplayListBuilder::drawImageLattice(const sk_sp<SkImage> image,
const SkCanvas::Lattice& lattice,
Expand All @@ -1469,6 +1531,7 @@ void DisplayListBuilder::drawImageLattice(const sk_sp<SkImage> image,
filter, render_with_attributes);
CopyV(pod, lattice.fXDivs, xDivCount, lattice.fYDivs, yDivCount,
lattice.fColors, cellCount, lattice.fRectTypes, cellCount);
CheckLayerOpacityCompatibility(render_with_attributes);
}
void DisplayListBuilder::drawAtlas(const sk_sp<SkImage> atlas,
const SkRSXform xform[],
Expand Down Expand Up @@ -1503,6 +1566,10 @@ void DisplayListBuilder::drawAtlas(const sk_sp<SkImage> atlas,
}
CopyV(data_ptr, xform, count, tex, count);
}
// drawAtlas treats each image as a separate operation so we cannot rely
// on it to distribute the opacity without overlap without checking all
// of the transforms and texture rectangles.
UpdateLayerOpacityCompatibility(false);
}

void DisplayListBuilder::drawPicture(const sk_sp<SkPicture> picture,
Expand All @@ -1520,6 +1587,7 @@ void DisplayListBuilder::drawPicture(const sk_sp<SkPicture> picture,
// This behavior is identical to the way SkPicture computes nested op counts.
nested_op_count_ += picture->approximateOpCount(true) - 1;
nested_bytes_ += picture->approximateBytesUsed();
CheckLayerOpacityCompatibility(render_with_attributes);
}
void DisplayListBuilder::drawDisplayList(
const sk_sp<DisplayList> display_list) {
Expand All @@ -1532,11 +1600,13 @@ void DisplayListBuilder::drawDisplayList(
// This behavior is identical to the way SkPicture computes nested op counts.
nested_op_count_ += display_list->op_count(true) - 1;
nested_bytes_ += display_list->bytes(true);
UpdateLayerOpacityCompatibility(display_list->can_apply_group_opacity());
}
void DisplayListBuilder::drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) {
Push<DrawTextBlobOp>(0, 1, std::move(blob), x, y);
CheckLayerOpacityCompatibility();
}
void DisplayListBuilder::drawShadow(const SkPath& path,
const SkColor color,
Expand All @@ -1546,6 +1616,7 @@ void DisplayListBuilder::drawShadow(const SkPath& path,
transparent_occluder //
? Push<DrawShadowTransparentOccluderOp>(0, 1, path, color, elevation, dpr)
: Push<DrawShadowOp>(0, 1, path, color, elevation, dpr);
UpdateLayerOpacityCompatibility(false);
}

// clang-format off
Expand Down
Loading