Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
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
83 changes: 83 additions & 0 deletions display_list/dl_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,9 @@ bool DisplayListBuilder::QuickReject(const SkRect& bounds) const {
void DisplayListBuilder::drawPaint() {
OpResult result = PaintResult(current_, kDrawPaintFlags);
if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
if (current_layer().update_clear_color(current_, false)) {
return;
}
Push<DrawPaintOp>(0);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
Expand All @@ -1054,6 +1057,9 @@ void DisplayListBuilder::DrawPaint(const DlPaint& paint) {
void DisplayListBuilder::DrawColor(DlColor color, DlBlendMode mode) {
OpResult result = PaintResult(DlPaint(color).setBlendMode(mode));
if (result != OpResult::kNoEffect && AccumulateUnbounded()) {
if (current_layer().update_clear_color(color, mode)) {
return;
}
Push<DrawColorOp>(0, color, mode);
CheckLayerOpacityCompatibility(mode);
UpdateLayerResult(result, mode);
Expand Down Expand Up @@ -1082,6 +1088,11 @@ void DisplayListBuilder::drawRect(const SkRect& rect) {
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect &&
AccumulateOpBounds(rect.makeSorted(), flags)) {
if (current_layer().still_clearing &&
current_info().global_state.rect_covers_cull(rect) &&
current_layer().update_clear_color(current_, true)) {
return;
}
Push<DrawRectOp>(0, rect);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
Expand All @@ -1096,6 +1107,11 @@ void DisplayListBuilder::drawOval(const SkRect& bounds) {
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect &&
AccumulateOpBounds(bounds.makeSorted(), flags)) {
if (current_layer().still_clearing &&
current_info().global_state.oval_covers_cull(bounds) &&
current_layer().update_clear_color(current_, true)) {
return;
}
Push<DrawOvalOp>(0, bounds);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
Expand All @@ -1112,6 +1128,11 @@ void DisplayListBuilder::drawCircle(const SkPoint& center, SkScalar radius) {
SkRect bounds = SkRect::MakeLTRB(center.fX - radius, center.fY - radius,
center.fX + radius, center.fY + radius);
if (AccumulateOpBounds(bounds, flags)) {
if (current_layer().still_clearing &&
current_info().global_state.oval_covers_cull(bounds) &&
current_layer().update_clear_color(current_, true)) {
return;
}
Push<DrawCircleOp>(0, center, radius);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
Expand All @@ -1134,6 +1155,11 @@ void DisplayListBuilder::drawRRect(const SkRRect& rrect) {
OpResult result = PaintResult(current_, flags);
if (result != OpResult::kNoEffect &&
AccumulateOpBounds(rrect.getBounds(), flags)) {
if (current_layer().still_clearing &&
current_info().global_state.rrect_covers_cull(rrect) &&
current_layer().update_clear_color(current_, true)) {
return;
}
Push<DrawRRectOp>(0, rrect);
CheckLayerOpacityCompatibility();
UpdateLayerResult(result);
Expand Down Expand Up @@ -1786,6 +1812,63 @@ bool DisplayListBuilder::AccumulateBounds(const SkRect& bounds,
return true;
}

bool DisplayListBuilder::LayerInfo::update_clear_color(DlColor color,
Copy link
Contributor

Choose a reason for hiding this comment

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

The render target's clear color will ignore all clips, so any time we add a non-culled clip to the builder we have to turn off clear color optimization.

This is makes clip culling is super important

Copy link
Contributor

Choose a reason for hiding this comment

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

Because of this actually, we may need to do clear color optimization while dispatching instead of recording, but lets discuss today and see if folks have ideas.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought of that last night. I was thinking that turning off the optimization after receiving a clip would do the trick there, but there is also the fact that the DL Builder is still figuring out the right bounds for the save layer while it is recording and won't know the true bounds until Restore is called. So, perhaps this entire optimization is not possible. Or perhaps it only works for DrawColor and DrawPaint that happen right after a SaveLayer but before any clips.

We'll discuss later...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I should probably transfer all of the new code to the clip functions to cull unnecessary clips even if it doesn't get used for clear color overlap detection...

DlBlendMode mode) {
if (still_clearing) {
switch (mode) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should be able to support all blend modes, if we clear more than 1 color we perform the blending on the CPU. clear color should not impact max_blend_mode I think

case DlBlendMode::kSrc:
if (color.isTransparent()) {
clear_color = DlColor::kTransparent();
max_blend_mode = DlBlendMode::kClear;
} else {
clear_color = color;
max_blend_mode = DlBlendMode::kSrc;
}
return true;
case DlBlendMode::kSrcOver:
if (color.isTransparent()) {
// No changes to clear_color or max_blend_mode...
} else {
if (color.isOpaque() || clear_color.isTransparent()) {
clear_color = color;
max_blend_mode = DlBlendMode::kSrc;
} else {
DlScalar fSrc = color.getAlphaF();
DlScalar fDst = clear_color.getAlphaF() * (1.0f - fSrc);
DlScalar fRed =
color.getRedF() * fSrc + clear_color.getRedF() * fDst;
DlScalar fGreen =
color.getGreenF() * fSrc + clear_color.getGreenF() * fDst;
DlScalar fBlue =
color.getBlueF() * fSrc + clear_color.getBlueF() * fDst;
clear_color = DlColor::MakeARGB(fSrc + fDst, fRed, fGreen, fBlue);
max_blend_mode = DlBlendMode::kSrc;
}
}
return true;
default:
break;
}
}
return false;
}

bool DisplayListBuilder::LayerInfo::update_clear_color(const DlPaint& paint,
bool has_geometry) {
if (paint.getColorSourcePtr() != nullptr ||
paint.getColorFilterPtr() != nullptr ||
paint.getImageFilterPtr() != nullptr) {
return false;
}
if (has_geometry) {
if (paint.getDrawStyle() != DlDrawStyle::kFill ||
paint.getMaskFilterPtr() != nullptr) {
return false;
}
}
return update_clear_color(paint.getColor(), paint.getBlendMode());
}

bool DisplayListBuilder::SaveInfo::AccumulateBoundsLocal(const SkRect& bounds) {
if (bounds.isEmpty()) {
return false;
Expand Down
13 changes: 13 additions & 0 deletions display_list/dl_builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@ class DisplayListBuilder final : public virtual DlCanvas,

DlBlendMode max_blend_mode = DlBlendMode::kClear;

bool still_clearing;
DlColor clear_color = DlColor::kTransparent();

bool opacity_incompatible_op_detected = false;
bool affects_transparent_layer = false;
bool contains_backdrop_filter = false;
Expand All @@ -553,6 +556,16 @@ class DisplayListBuilder final : public virtual DlCanvas,
max_blend_mode = mode;
}
}

// Update the layer's clear color according to the indicated fill
// color and blend mode and return true if the operation was
// successfully merged into the layer clear information.
bool update_clear_color(DlColor color, DlBlendMode mode);

// Update the layer's clear color according to the indicated fill
// color and blend mode and return true if the operation was
// successfully merged into the layer clear information.
bool update_clear_color(const DlPaint& paint, bool has_geometry);
};

// The SaveInfo class stores internal data common to both Save and
Expand Down
24 changes: 23 additions & 1 deletion display_list/dl_color.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#ifndef FLUTTER_DISPLAY_LIST_DL_COLOR_H_
#define FLUTTER_DISPLAY_LIST_DL_COLOR_H_

#include <algorithm>

#include "third_party/skia/include/core/SkScalar.h"

namespace flutter {
Expand All @@ -13,6 +15,15 @@ struct DlColor {
public:
constexpr DlColor() : argb_(0xFF000000) {}
constexpr explicit DlColor(uint32_t argb) : argb_(argb) {}
constexpr static DlColor MakeARGB(SkScalar fAlpha,
SkScalar fRed,
SkScalar fGreen,
SkScalar fBlue) {
return DlColor((toC(fAlpha) << 24) + //
(toC(fRed) << 16) + //
(toC(fGreen) << 8) + //
(toC(fBlue)));
}

static constexpr uint8_t toAlpha(SkScalar opacity) { return toC(opacity); }
static constexpr SkScalar toOpacity(uint8_t alpha) { return toF(alpha); }
Expand Down Expand Up @@ -87,7 +98,18 @@ struct DlColor {
uint32_t argb_;

static float toF(uint8_t comp) { return comp * (1.0f / 255); }
static uint8_t toC(float fComp) { return round(fComp * 255); }
static uint8_t toC(float fComp) {
return round(std::clamp(fComp, 0.0f, 1.0f) * 255);
}
static uint32_t fromFloats(SkScalar fAlpha,
SkScalar fRed,
SkScalar fGreen,
SkScalar fBlue) {
return ((toC(std::clamp(fAlpha, 0.0f, 1.0f)) << 24) +
(toC(std::clamp(fRed, 0.0f, 1.0f)) << 16) +
(toC(std::clamp(fGreen, 0.0f, 1.0f)) << 8) +
(toC(std::clamp(fBlue, 0.0f, 1.0f))));
}
};

} // namespace flutter
Expand Down
30 changes: 30 additions & 0 deletions display_list/dl_color_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,35 @@ TEST(DisplayListColor, DlColorDirectlyComparesToSkColor) {
EXPECT_EQ(DlColor::kBlue(), SK_ColorBLUE);
}

TEST(DisplayListColor, ScalarFactory) {
// Test 9 standard colors with their Scalar equivalents
EXPECT_EQ(DlColor::MakeARGB(0.0f, 0.0f, 0.0f, 0.0f), DlColor::kTransparent());
EXPECT_EQ(DlColor::MakeARGB(1.0f, 0.0f, 0.0f, 0.0f), DlColor::kBlack());
EXPECT_EQ(DlColor::MakeARGB(1.0f, 1.0f, 1.0f, 1.0f), DlColor::kWhite());

EXPECT_EQ(DlColor::MakeARGB(1.0f, 1.0f, 0.0f, 0.0f), DlColor::kRed());
EXPECT_EQ(DlColor::MakeARGB(1.0f, 0.0f, 1.0f, 0.0f), DlColor::kGreen());
EXPECT_EQ(DlColor::MakeARGB(1.0f, 0.0f, 0.0f, 1.0f), DlColor::kBlue());

EXPECT_EQ(DlColor::MakeARGB(1.0f, 0.0f, 1.0f, 1.0f), DlColor::kCyan());
EXPECT_EQ(DlColor::MakeARGB(1.0f, 1.0f, 0.0f, 1.0f), DlColor::kMagenta());
EXPECT_EQ(DlColor::MakeARGB(1.0f, 1.0f, 1.0f, 0.0f), DlColor::kYellow());

// Test each component reduced to half intensity
EXPECT_EQ(DlColor::MakeARGB(0.5f, 1.0f, 1.0f, 1.0f),
DlColor::kWhite().withAlpha(0x80));
EXPECT_EQ(DlColor::MakeARGB(1.0f, 0.5f, 1.0f, 1.0f),
DlColor::kWhite().withRed(0x80));
EXPECT_EQ(DlColor::MakeARGB(1.0f, 1.0f, 0.5f, 1.0f),
DlColor::kWhite().withGreen(0x80));
EXPECT_EQ(DlColor::MakeARGB(1.0f, 1.0f, 1.0f, 0.5f),
DlColor::kWhite().withBlue(0x80));

// Test clamping to [0.0, 1.0]
EXPECT_EQ(DlColor::MakeARGB(-1.0f, -1.0f, -1.0f, -1.0f),
DlColor::kTransparent());
EXPECT_EQ(DlColor::MakeARGB(2.0f, 2.0f, 2.0f, 2.0f), DlColor::kWhite());
}

} // namespace testing
} // namespace flutter
101 changes: 101 additions & 0 deletions display_list/utils/dl_matrix_clip_tracker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,105 @@ SkRect DisplayListMatrixClipState::local_cull_rect() const {
return ToSkRect(cull_rect_.TransformBounds(inverse));
}

bool DisplayListMatrixClipState::rect_covers_cull(const DlRect& content) const {
if (content.IsEmpty()) {
return false;
}
if (cull_rect_.IsEmpty()) {
return true;
}
DlPoint corners[4];
if (!getLocalCullCorners(corners)) {
return false;
}
for (auto corner : corners) {
if (!content.ContainsInclusive(corner)) {
return false;
}
}
return true;
}

bool DisplayListMatrixClipState::oval_covers_cull(const DlRect& bounds) const {
if (bounds.IsEmpty()) {
return false;
}
if (cull_rect_.IsEmpty()) {
return true;
}
DlPoint corners[4];
if (!getLocalCullCorners(corners)) {
return false;
}
DlPoint center = bounds.GetCenter();
DlSize scale = 2.0 / bounds.GetSize();
for (auto corner : corners) {
if (!bounds.Contains(corner)) {
return false;
}
if (((corner - center) * scale).GetLengthSquared() >= 1.0) {
return false;
}
}
return true;
}

bool DisplayListMatrixClipState::rrect_covers_cull(
const SkRRect& content) const {
if (content.isEmpty()) {
return false;
}
if (cull_rect_.IsEmpty()) {
return true;
}
if (content.isRect()) {
return rect_covers_cull(content.getBounds());
}
if (content.isOval()) {
return oval_covers_cull(content.getBounds());
}
if (!content.isSimple()) {
return false;
}
DlPoint corners[4];
if (!getLocalCullCorners(corners)) {
return false;
}
auto outer = content.getBounds();
DlScalar x_center = outer.centerX();
DlScalar y_center = outer.centerY();
auto radii = content.getSimpleRadii();
DlScalar inner_x = outer.width() * 0.5f - radii.fX;
DlScalar inner_y = outer.height() * 0.5f - radii.fY;
DlScalar scale_x = 1.0 / radii.fX;
DlScalar scale_y = 1.0 / radii.fY;
for (auto corner : corners) {
if (!outer.contains(corner.x, corner.y)) {
return false;
}
DlScalar x_rel = std::abs(corner.x - x_center) - inner_x;
DlScalar y_rel = std::abs(corner.y - y_center) - inner_y;
if (x_rel > 0.0f && y_rel > 0.0f) {
x_rel *= scale_x;
y_rel *= scale_y;
if (x_rel * x_rel + y_rel * y_rel >= 1.0f) {
return false;
}
}
}
return true;
}

bool DisplayListMatrixClipState::getLocalCullCorners(DlPoint corners[4]) const {
if (!is_matrix_invertable()) {
return false;
}
DlMatrix inverse = matrix_.Invert();
corners[0] = inverse * cull_rect_.GetLeftTop();
corners[1] = inverse * cull_rect_.GetRightTop();
corners[2] = inverse * cull_rect_.GetRightBottom();
corners[3] = inverse * cull_rect_.GetLeftBottom();
return true;
}

} // namespace flutter
11 changes: 11 additions & 0 deletions display_list/utils/dl_matrix_clip_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ class DisplayListMatrixClipState {
SkRect local_cull_rect() const;
SkRect device_cull_rect() const { return ToSkRect(cull_rect_); }

bool rect_covers_cull(const DlRect& content) const;
bool rect_covers_cull(const SkRect& content) const {
return rect_covers_cull(ToDlRect(content));
}
bool oval_covers_cull(const DlRect& content_bounds) const;
bool oval_covers_cull(const SkRect& content_bounds) const {
return oval_covers_cull(ToDlRect(content_bounds));
}
bool rrect_covers_cull(const SkRRect& content) const;

bool content_culled(const DlRect& content_bounds) const;
bool content_culled(const SkRect& content_bounds) const {
return content_culled(ToDlRect(content_bounds));
Expand Down Expand Up @@ -144,6 +154,7 @@ class DisplayListMatrixClipState {
DlRect cull_rect_;
DlMatrix matrix_;

bool getLocalCullCorners(DlPoint corners[4]) const;
void adjustCullRect(const DlRect& clip, ClipOp op, bool is_aa);

friend class DisplayListMatrixClipTracker;
Expand Down
Loading