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

Display list R-Tree culling #38429

Merged
merged 14 commits into from
Dec 22, 2022
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
1 change: 1 addition & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
../../../flutter/display_list/display_list_matrix_clip_tracker_unittests.cc
../../../flutter/display_list/display_list_paint_unittests.cc
../../../flutter/display_list/display_list_path_effect_unittests.cc
../../../flutter/display_list/display_list_rtree_unittests.cc
../../../flutter/display_list/display_list_unittests.cc
../../../flutter/display_list/display_list_utils_unittests.cc
../../../flutter/display_list/display_list_vertices_unittests.cc
Expand Down
1 change: 1 addition & 0 deletions display_list/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ if (enable_unittests) {
"display_list_matrix_clip_tracker_unittests.cc",
"display_list_paint_unittests.cc",
"display_list_path_effect_unittests.cc",
"display_list_rtree_unittests.cc",
"display_list_unittests.cc",
"display_list_utils_unittests.cc",
"display_list_vertices_unittests.cc",
Expand Down
144 changes: 133 additions & 11 deletions display_list/display_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,144 @@ DisplayList::DisplayList(DisplayListStorage&& storage,
op_count_(op_count),
nested_byte_count_(nested_byte_count),
nested_op_count_(nested_op_count),
unique_id_(next_unique_id()),
bounds_(bounds),
can_apply_group_opacity_(can_apply_group_opacity),
rtree_(std::move(rtree)) {
rtree_(std::move(rtree)) {}

DisplayList::~DisplayList() {
uint8_t* ptr = storage_.get();
DisposeOps(ptr, ptr + byte_count_);
}

uint32_t DisplayList::next_unique_id() {
static std::atomic<uint32_t> next_id{1};
uint32_t id;
do {
unique_id_ = next_id.fetch_add(+1, std::memory_order_relaxed);
} while (unique_id_ == 0);
id = next_id.fetch_add(+1, std::memory_order_relaxed);
} while (id == 0);
return id;
}

DisplayList::~DisplayList() {
class Culler {
public:
virtual bool init(DispatchContext& context) = 0;
virtual void update(DispatchContext& context) = 0;
};
class NopCuller : public Culler {
public:
static NopCuller instance;

bool init(DispatchContext& context) override {
// Setting next_render_index to 0 means that
// all rendering ops will be at or after that
// index so they will execute and all restore
// indices will be after it as well so all
// clip and transform operations will execute.
context.next_render_index = 0;
return true;
}
void update(DispatchContext& context) override {}
};
NopCuller NopCuller::instance = NopCuller();
class VectorCuller : public Culler {
public:
VectorCuller(const DlRTree* rtree, const std::vector<int>& rect_indices)
: rtree_(rtree), cur_(rect_indices.begin()), end_(rect_indices.end()) {}

bool init(DispatchContext& context) override {
if (cur_ < end_) {
context.next_render_index = rtree_->id(*cur_++);
return true;
} else {
// Setting next_render_index to MAX_INT means that
// all rendering ops will be "before" that index and
// they will skip themselves and all clip and transform
// ops will see that the next render index is not
// before the next restore index (even if both are MAX_INT)
// and so they will also not execute.
// None of this really matters because returning false
// here should cause the Dispatch operation to abort,
// but this value is conceptually correct if that short
// circuit optimization isn't used.
context.next_render_index = std::numeric_limits<int>::max();
return false;
}
}
void update(DispatchContext& context) override {
if (++context.cur_index > context.next_render_index) {
while (cur_ < end_) {
context.next_render_index = rtree_->id(*cur_++);
if (context.next_render_index >= context.cur_index) {
// It should be rare that we have duplicate indices
// but if we do, then having a while loop is a cheap
// insurance for those cases.
// The main cause of duplicate indices is when a
// DrawDisplayListOp was added to this DisplayList and
// both are computing an R-Tree, in which case the
// builder method will forward all of the child
// DisplayList's rects to this R-Tree with the same
// op_index.
return;
}
}
context.next_render_index = std::numeric_limits<int>::max();
}
}

private:
const DlRTree* rtree_;
std::vector<int>::const_iterator cur_;
std::vector<int>::const_iterator end_;
};

void DisplayList::Dispatch(Dispatcher& ctx) const {
uint8_t* ptr = storage_.get();
DisposeOps(ptr, ptr + byte_count_);
Dispatch(ctx, ptr, ptr + byte_count_, NopCuller::instance);
}
void DisplayList::Dispatch(Dispatcher& ctx, const SkRect& cull_rect) const {
if (cull_rect.isEmpty()) {
return;
}
if (cull_rect.contains(bounds())) {
Dispatch(ctx);
return;
}
const DlRTree* rtree = this->rtree().get();
FML_DCHECK(rtree != nullptr);
if (rtree == nullptr) {
FML_LOG(ERROR) << "dispatched with culling rect on DL with no rtree";
Dispatch(ctx);
return;
}
uint8_t* ptr = storage_.get();
std::vector<int> rect_indices;
rtree->search(cull_rect, &rect_indices);
VectorCuller culler(rtree, rect_indices);
Dispatch(ctx, ptr, ptr + byte_count_, culler);
}

void DisplayList::Dispatch(Dispatcher& dispatcher,
uint8_t* ptr,
uint8_t* end) const {
uint8_t* end,
Culler& culler) const {
DispatchContext context = {
.dispatcher = dispatcher,
.cur_index = 0,
// next_render_index will be initialized by culler.init()
.next_restore_index = std::numeric_limits<int>::max(),
};
if (!culler.init(context)) {
return;
}
while (ptr < end) {
auto op = reinterpret_cast<const DLOp*>(ptr);
ptr += op->size;
FML_DCHECK(ptr <= end);
switch (op->type) {
#define DL_OP_DISPATCH(name) \
case DisplayListOpType::k##name: \
static_cast<const name##Op*>(op)->dispatch(dispatcher); \
#define DL_OP_DISPATCH(name) \
case DisplayListOpType::k##name: \
static_cast<const name##Op*>(op)->dispatch(context); \
break;

FOR_EACH_DISPLAY_LIST_OP(DL_OP_DISPATCH)
Expand All @@ -73,6 +186,7 @@ void DisplayList::Dispatch(Dispatcher& dispatcher,
FML_DCHECK(false);
return;
}
culler.update(context);
}
}

Expand Down Expand Up @@ -172,12 +286,20 @@ void DisplayList::RenderTo(DisplayListBuilder* builder,
if (!builder) {
return;
}
Dispatch(*builder);
if (has_rtree()) {
Dispatch(*builder, builder->getLocalClipBounds());
} else {
Dispatch(*builder);
}
}

void DisplayList::RenderTo(SkCanvas* canvas, SkScalar opacity) const {
DisplayListCanvasDispatcher dispatcher(canvas, opacity);
Dispatch(dispatcher);
if (has_rtree()) {
Dispatch(dispatcher, canvas->getLocalClipBounds());
} else {
Dispatch(dispatcher);
}
}

bool DisplayList::Equals(const DisplayList* other) const {
Expand Down
40 changes: 23 additions & 17 deletions display_list/display_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ class DisplayListStorage {
std::unique_ptr<uint8_t, FreeDeleter> ptr_;
};

class Culler;

// 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 All @@ -244,10 +246,8 @@ class DisplayList : public SkRefCnt {

~DisplayList();

void Dispatch(Dispatcher& ctx) const {
uint8_t* ptr = storage_.get();
Dispatch(ctx, ptr, ptr + byte_count_);
}
void Dispatch(Dispatcher& ctx) const;
void Dispatch(Dispatcher& ctx, const SkRect& cull_rect) const;

void RenderTo(DisplayListBuilder* builder,
SkScalar opacity = SK_Scalar1) const;
Expand All @@ -268,17 +268,18 @@ class DisplayList : public SkRefCnt {

uint32_t unique_id() const { return unique_id_; }

const SkRect& bounds() { return bounds_; }
const SkRect& bounds() const { return bounds_; }

sk_sp<const DlRTree> rtree() { return rtree_; }
bool has_rtree() const { return rtree_ != nullptr; }
sk_sp<const DlRTree> rtree() const { return rtree_; }

bool Equals(const DisplayList* other) const;
bool Equals(const DisplayList& other) const { return Equals(&other); }
bool Equals(sk_sp<const DisplayList> other) const {
return Equals(other.get());
}

bool can_apply_group_opacity() { return can_apply_group_opacity_; }
bool can_apply_group_opacity() const { return can_apply_group_opacity_; }

static void DisposeOps(uint8_t* ptr, uint8_t* end);

Expand All @@ -292,20 +293,25 @@ class DisplayList : public SkRefCnt {
bool can_apply_group_opacity,
sk_sp<const DlRTree> rtree);

DisplayListStorage storage_;
size_t byte_count_;
unsigned int op_count_;
static uint32_t next_unique_id();

const DisplayListStorage storage_;
const size_t byte_count_;
const unsigned int op_count_;

size_t nested_byte_count_;
unsigned int nested_op_count_;
const size_t nested_byte_count_;
const unsigned int nested_op_count_;

uint32_t unique_id_;
SkRect bounds_;
const uint32_t unique_id_;
const SkRect bounds_;

bool can_apply_group_opacity_;
sk_sp<const DlRTree> rtree_;
const bool can_apply_group_opacity_;
const sk_sp<const DlRTree> rtree_;

void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const;
void Dispatch(Dispatcher& ctx,
uint8_t* ptr,
uint8_t* end,
Culler& culler) const;

friend class DisplayListBuilder;
};
Expand Down
Loading