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

Commit b70ab5a

Browse files
committed
fix issue with layer bounds rejection when rendering a tree to a DL
1 parent db6b02b commit b70ab5a

File tree

6 files changed

+61
-18
lines changed

6 files changed

+61
-18
lines changed

display_list/display_list_builder.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,21 @@ SkRect DisplayListBuilder::getLocalClipBounds() {
709709
return kMaxCullRect_;
710710
}
711711

712+
bool DisplayListBuilder::quickReject(const SkRect& bounds) const {
713+
if (bounds.isEmpty()) {
714+
return true;
715+
}
716+
SkMatrix matrix = getTransform();
717+
// We don't need the inverse, but this method tells us if the matrix
718+
// is singular in which case we can reject all rendering.
719+
if (!matrix.invert(nullptr)) {
720+
return true;
721+
}
722+
SkRect dev_bounds;
723+
matrix.mapRect(bounds).roundOut(&dev_bounds);
724+
return !current_layer_->clip_bounds.intersects(dev_bounds);
725+
}
726+
712727
void DisplayListBuilder::drawPaint() {
713728
Push<DrawPaintOp>(0, 1);
714729
CheckLayerOpacityCompatibility();

display_list/display_list_builder.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ class DisplayListBuilder final : public virtual Dispatcher,
206206
/// Returns the 3x3 partial perspective transform representing all transform
207207
/// operations executed so far in this DisplayList within the enclosing
208208
/// save stack.
209-
SkMatrix getTransform() { return current_layer_->matrix.asM33(); }
209+
SkMatrix getTransform() const { return current_layer_->matrix.asM33(); }
210210

211211
void clipRect(const SkRect& rect, SkClipOp clip_op, bool is_aa) override;
212212
void clipRRect(const SkRRect& rrect, SkClipOp clip_op, bool is_aa) override;
@@ -221,6 +221,11 @@ class DisplayListBuilder final : public virtual Dispatcher,
221221
/// recorded rendering operations are interpreted.
222222
SkRect getLocalClipBounds();
223223

224+
/// Return true iff the supplied bounds are easily shown to be outside
225+
/// of the current clip bounds. This method may conservatively return
226+
/// false if it cannot make the determination.
227+
bool quickReject(const SkRect& bounds) const;
228+
224229
void drawPaint() override;
225230
void drawPaint(const DlPaint& paint);
226231
void drawColor(DlColor color, DlBlendMode mode) override;

flow/layers/clip_path_layer_unittests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ TEST_F(ClipPathLayerTest, OpacityInheritancePainting) {
370370
auto mock2 = MockLayer::MakeOpacityCompatible(path2);
371371
auto layer_clip = SkPath()
372372
.addRect(SkRect::MakeLTRB(5, 5, 25, 25))
373-
.addOval(SkRect::MakeLTRB(20, 20, 40, 50));
373+
.addOval(SkRect::MakeLTRB(45, 45, 55, 55));
374374
auto clip_path_layer =
375375
std::make_shared<ClipPathLayer>(layer_clip, Clip::antiAlias);
376376
clip_path_layer->Add(mock1);

flow/layers/layer.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,15 +240,7 @@ class Layer {
240240
// See https://github.com/flutter/flutter/issues/81419
241241
return true;
242242
}
243-
if (!context.state_stack.needs_painting()) {
244-
return false;
245-
}
246-
// Workaround for Skia bug (quickReject does not reject empty bounds).
247-
// https://bugs.chromium.org/p/skia/issues/detail?id=10951
248-
if (paint_bounds_.isEmpty()) {
249-
return false;
250-
}
251-
return !context.canvas->quickReject(paint_bounds_);
243+
return context.state_stack.needs_painting(paint_bounds_);
252244
}
253245

254246
// Propagated unique_id of the first layer in "chain" of replacement layers

flow/layers/layer_state_stack.cc

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ AutoRestore LayerStateStack::applyState(const SkRect& bounds,
6666
}
6767

6868
SkPaint* LayerStateStack::RenderingAttributes::fill(SkPaint& paint,
69-
DlBlendMode mode) {
69+
DlBlendMode mode) const {
7070
SkPaint* ret = nullptr;
7171
if (opacity < SK_Scalar1) {
7272
paint.setAlphaf(std::max(opacity, 0.0f));
@@ -94,7 +94,7 @@ SkPaint* LayerStateStack::RenderingAttributes::fill(SkPaint& paint,
9494
}
9595

9696
DlPaint* LayerStateStack::RenderingAttributes::fill(DlPaint& paint,
97-
DlBlendMode mode) {
97+
DlBlendMode mode) const {
9898
DlPaint* ret = nullptr;
9999
if (opacity < SK_Scalar1) {
100100
paint.setOpacity(std::max(opacity, 0.0f));
@@ -117,6 +117,27 @@ DlPaint* LayerStateStack::RenderingAttributes::fill(DlPaint& paint,
117117
return ret;
118118
}
119119

120+
bool LayerStateStack::needs_painting(const SkRect& bounds) const {
121+
if (!needs_painting()) {
122+
// Answer based on outstanding attributes...
123+
return false;
124+
}
125+
if (canvas_) {
126+
// Workaround for Skia bug (quickReject does not reject empty bounds).
127+
// https://bugs.chromium.org/p/skia/issues/detail?id=10951
128+
if (bounds.isEmpty()) {
129+
return false;
130+
}
131+
return !canvas_->quickReject(bounds);
132+
}
133+
if (builder_) {
134+
return !builder_->quickReject(bounds);
135+
}
136+
// We could track the attributes ourselves, but this method is
137+
// unlikely to be called without a canvas or builder to back it up.
138+
return true;
139+
}
140+
120141
MutatorContext LayerStateStack::save() {
121142
auto ret = MutatorContext(this);
122143
state_stack_.emplace_back(std::make_unique<SaveEntry>());

flow/layers/layer_state_stack.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,17 @@ class LayerStateStack {
140140
// Fill the provided paint object with any oustanding attributes and
141141
// return a pointer to it, or return a nullptr if there were no
142142
// outstanding attributes to paint with.
143-
DlPaint* fill(DlPaint& paint) { return outstanding_.fill(paint); }
143+
DlPaint* fill(DlPaint& paint) const { return outstanding_.fill(paint); }
144144

145-
bool needs_painting() { return outstanding_.opacity > 0; }
145+
// Tests if painting content with the current outstanding attributes
146+
// will produce any content.
147+
bool needs_painting() const { return outstanding_.opacity > 0; }
148+
149+
// Tests if painting content with the given bounds will produce any output.
150+
// This method also tests whether the outstanding attributes will allow
151+
// output to be produced, but then goes on to test if the supplied bounds
152+
// will fall within the current clip bounds based on the transform.
153+
bool needs_painting(const SkRect& bounds) const;
146154

147155
// Saves the current state of the state stack and returns a
148156
// MutatorContext which can be used to manipulate the state.
@@ -214,10 +222,12 @@ class LayerStateStack {
214222
std::shared_ptr<const DlColorFilter> color_filter;
215223
std::shared_ptr<const DlImageFilter> image_filter;
216224

217-
SkPaint* fill(SkPaint& paint, DlBlendMode mode = DlBlendMode::kSrcOver);
218-
DlPaint* fill(DlPaint& paint, DlBlendMode mode = DlBlendMode::kSrcOver);
225+
SkPaint* fill(SkPaint& paint,
226+
DlBlendMode mode = DlBlendMode::kSrcOver) const;
227+
DlPaint* fill(DlPaint& paint,
228+
DlBlendMode mode = DlBlendMode::kSrcOver) const;
219229

220-
bool operator==(const RenderingAttributes& other) {
230+
bool operator==(const RenderingAttributes& other) const {
221231
return save_layer_bounds == other.save_layer_bounds &&
222232
opacity == other.opacity &&
223233
Equals(color_filter, other.color_filter) &&

0 commit comments

Comments
 (0)