Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ FILE: ../../../flutter/display_list/display_list_paint_unittests.cc
FILE: ../../../flutter/display_list/display_list_path_effect.cc
FILE: ../../../flutter/display_list/display_list_path_effect.h
FILE: ../../../flutter/display_list/display_list_path_effect_unittests.cc
FILE: ../../../flutter/display_list/display_list_rtree.cc
FILE: ../../../flutter/display_list/display_list_rtree.h
FILE: ../../../flutter/display_list/display_list_sampling_options.h
FILE: ../../../flutter/display_list/display_list_test_utils.cc
FILE: ../../../flutter/display_list/display_list_test_utils.h
Expand Down
2 changes: 2 additions & 0 deletions display_list/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ source_set("display_list") {
"display_list_paint.h",
"display_list_path_effect.cc",
"display_list_path_effect.h",
"display_list_rtree.cc",
"display_list_rtree.h",
"display_list_sampling_options.h",
"display_list_tile_mode.h",
"display_list_utils.cc",
Expand Down
18 changes: 16 additions & 2 deletions display_list/display_list.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,23 @@ DisplayList::~DisplayList() {
}

void DisplayList::ComputeBounds() {
DisplayListBoundsCalculator calculator(&bounds_cull_);
RectBoundsAccumulator accumulator;
DisplayListBoundsCalculator calculator(accumulator, &bounds_cull_);
Dispatch(calculator);
bounds_ = calculator.bounds();
if (calculator.is_unbounded()) {
FML_LOG(INFO) << "returning partial bounds for unbounded DisplayList";
}
bounds_ = accumulator.bounds();
}

void DisplayList::ComputeRTree() {
RTreeBoundsAccumulator accumulator;
DisplayListBoundsCalculator calculator(accumulator, &bounds_cull_);
Dispatch(calculator);
if (calculator.is_unbounded()) {
FML_LOG(INFO) << "returning partial rtree for unbounded DisplayList";
}
rtree_ = accumulator.rtree();
}

void DisplayList::Dispatch(Dispatcher& dispatcher,
Expand Down
11 changes: 11 additions & 0 deletions display_list/display_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
#ifndef FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_
#define FLUTTER_DISPLAY_LIST_DISPLAY_LIST_H_

#include <memory>
#include <optional>

#include "flutter/display_list/display_list_rtree.h"
#include "flutter/display_list/display_list_sampling_options.h"
#include "flutter/display_list/types.h"
#include "flutter/fml/logging.h"
Expand Down Expand Up @@ -254,6 +256,13 @@ class DisplayList : public SkRefCnt {
return bounds_;
}

sk_sp<const DlRTree> rtree() {
if (!rtree_) {
ComputeRTree();
}
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 {
Expand Down Expand Up @@ -282,13 +291,15 @@ class DisplayList : public SkRefCnt {

uint32_t unique_id_;
SkRect bounds_;
sk_sp<const DlRTree> rtree_;

// Only used for drawPaint() and drawColor()
SkRect bounds_cull_;

bool can_apply_group_opacity_;

void ComputeBounds();
void ComputeRTree();
void Dispatch(Dispatcher& ctx, uint8_t* ptr, uint8_t* end) const;

friend class DisplayListBuilder;
Expand Down
97 changes: 97 additions & 0 deletions display_list/display_list_rtree.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/display_list/display_list_rtree.h"

#include "flutter/fml/logging.h"

namespace flutter {

DlRTree::DlRTree() : bbh_{SkRTreeFactory{}()}, all_ops_count_(0) {}

void DlRTree::insert(const SkRect boundsArray[],
const SkBBoxHierarchy::Metadata metadata[],
int N) {
FML_DCHECK(0 == all_ops_count_);
bbh_->insert(boundsArray, metadata, N);
for (int i = 0; i < N; i++) {
if (metadata == nullptr || metadata[i].isDraw) {
draw_op_[i] = boundsArray[i];
}
}
all_ops_count_ = N;
}

void DlRTree::insert(const SkRect boundsArray[], int N) {
insert(boundsArray, nullptr, N);
}

void DlRTree::search(const SkRect& query, std::vector<int>* results) const {
bbh_->search(query, results);
}

std::list<SkRect> DlRTree::searchNonOverlappingDrawnRects(
const SkRect& query) const {
// Get the indexes for the operations that intersect with the query rect.
std::vector<int> intermediary_results;
search(query, &intermediary_results);

std::list<SkRect> final_results;
for (int index : intermediary_results) {
auto draw_op = draw_op_.find(index);
// Ignore records that don't draw anything.
if (draw_op == draw_op_.end()) {
continue;
}
auto current_record_rect = draw_op->second;
auto replaced_existing_rect = false;
// // If the current record rect intersects with any of the rects in the
// // result list, then join them, and update the rect in final_results.
std::list<SkRect>::iterator curr_rect_itr = final_results.begin();
std::list<SkRect>::iterator first_intersecting_rect_itr;
while (!replaced_existing_rect && curr_rect_itr != final_results.end()) {
if (SkRect::Intersects(*curr_rect_itr, current_record_rect)) {
replaced_existing_rect = true;
first_intersecting_rect_itr = curr_rect_itr;
curr_rect_itr->join(current_record_rect);
}
curr_rect_itr++;
}
// It's possible that the result contains duplicated rects at this point.
// For example, consider a result list that contains rects A, B. If a
// new rect C is a superset of A and B, then A and B are the same set after
// the merge. As a result, find such cases and remove them from the result
// list.
while (replaced_existing_rect && curr_rect_itr != final_results.end()) {
if (SkRect::Intersects(*curr_rect_itr, *first_intersecting_rect_itr)) {
first_intersecting_rect_itr->join(*curr_rect_itr);
curr_rect_itr = final_results.erase(curr_rect_itr);
} else {
curr_rect_itr++;
}
}
if (!replaced_existing_rect) {
final_results.push_back(current_record_rect);
}
}
return final_results;
}

size_t DlRTree::bytesUsed() const {
return bbh_->bytesUsed();
}

DlRTreeFactory::DlRTreeFactory() {
r_tree_ = sk_make_sp<DlRTree>();
}

sk_sp<DlRTree> DlRTreeFactory::getInstance() {
return r_tree_;
}

sk_sp<SkBBoxHierarchy> DlRTreeFactory::operator()() const {
return r_tree_;
}

} // namespace flutter
68 changes: 68 additions & 0 deletions display_list/display_list_rtree.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_DISPLAY_LIST_RTREE_H_
#define FLUTTER_DISPLAY_LIST_RTREE_H_

#include <list>
#include <map>

#include "third_party/skia/include/core/SkBBHFactory.h"
#include "third_party/skia/include/core/SkRect.h"

namespace flutter {

/**
* An R-Tree implementation that forwards calls to an SkRTree. This is just
* a copy of flow/rtree.h/cc until we can figure out where these utilities
* can live with appropriate linking visibility.
*
* This implementation provides a searchNonOverlappingDrawnRects method,
* which can be used to query the rects for the operations recorded in the tree.
*/
class DlRTree : public SkBBoxHierarchy {
public:
DlRTree();

void insert(const SkRect[],
const SkBBoxHierarchy::Metadata[],
int N) override;
void insert(const SkRect[], int N) override;
void search(const SkRect& query, std::vector<int>* results) const override;
size_t bytesUsed() const override;

// Finds the rects in the tree that represent drawing operations and intersect
// with the query rect.
//
// When two rects intersect with each other, they are joined into a single
// rect which also intersects with the query rect. In other words, the bounds
// of each rect in the result list are mutually exclusive.
std::list<SkRect> searchNonOverlappingDrawnRects(const SkRect& query) const;

// Insertion count (not overall node count, which may be greater).
int getCount() const { return all_ops_count_; }

private:
// A map containing the draw operation rects keyed off the operation index
// in the insert call.
std::map<int, SkRect> draw_op_;
sk_sp<SkBBoxHierarchy> bbh_;
int all_ops_count_;
};

class DlRTreeFactory : public SkBBHFactory {
public:
DlRTreeFactory();

// Gets the instance to the R-tree.
sk_sp<DlRTree> getInstance();
sk_sp<SkBBoxHierarchy> operator()() const override;

private:
sk_sp<DlRTree> r_tree_;
};

} // namespace flutter

#endif // FLUTTER_DISPLAY_LIST_RTREE_H_
46 changes: 46 additions & 0 deletions display_list/display_list_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
// found in the LICENSE file.

#include <memory>

#include "flutter/display_list/display_list.h"
#include "flutter/display_list/display_list_blend_mode.h"
#include "flutter/display_list/display_list_builder.h"
#include "flutter/display_list/display_list_canvas_recorder.h"
#include "flutter/display_list/display_list_rtree.h"
#include "flutter/display_list/display_list_utils.h"
#include "flutter/fml/math.h"
#include "flutter/testing/display_list_testing.h"
Expand Down Expand Up @@ -2303,5 +2305,49 @@ TEST(DisplayList, FlatDrawPointsProducesBounds) {
}
}

static void test_rtree(sk_sp<const DlRTree> rtree,
const SkRect& query,
std::vector<SkRect> expected_rects,
std::vector<int> expected_indices) {
std::vector<int> indices;
rtree->search(query, &indices);
EXPECT_EQ(indices, expected_indices);
EXPECT_EQ(indices.size(), expected_indices.size());
std::list<SkRect> rects = rtree->searchNonOverlappingDrawnRects(query);
// ASSERT_EQ(rects.size(), expected_indices.size());
auto iterator = rects.cbegin();
for (int i : expected_indices) {
EXPECT_TRUE(iterator != rects.cend());
EXPECT_EQ(*iterator++, expected_rects[i]);
}
}

TEST(DisplayList, RTreeOfSimpleScene) {
DisplayListBuilder builder;
builder.drawRect({10, 10, 20, 20});
builder.drawRect({50, 50, 60, 60});
auto display_list = builder.Build();
auto rtree = display_list->rtree();
std::vector<SkRect> rects = {
{10, 10, 20, 20},
{50, 50, 60, 60},
};

// Missing all drawRect calls
test_rtree(rtree, {5, 5, 10, 10}, rects, {});
test_rtree(rtree, {20, 20, 25, 25}, rects, {});
test_rtree(rtree, {45, 45, 50, 50}, rects, {});
test_rtree(rtree, {60, 60, 65, 65}, rects, {});

// Hitting just 1 of the drawRects
test_rtree(rtree, {5, 5, 11, 11}, rects, {0});
test_rtree(rtree, {19, 19, 25, 25}, rects, {0});
test_rtree(rtree, {45, 45, 51, 51}, rects, {1});
test_rtree(rtree, {59, 59, 65, 65}, rects, {1});

// Hitting both drawRect calls
test_rtree(rtree, {19, 19, 51, 51}, rects, {0, 1});
}

} // namespace testing
} // namespace flutter
Loading