From c06ba33e51c1f3a6836f97c03f4179ecd429e931 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Wed, 7 Jun 2023 00:25:18 +0200 Subject: [PATCH 01/39] Implement DlRegion::addRegion and DlRegion::intersects --- .../benchmarking/dl_region_benchmarks.cc | 276 +++++++- display_list/geometry/dl_region.cc | 620 ++++++++++++++++-- display_list/geometry/dl_region.h | 75 ++- display_list/geometry/dl_region_unittests.cc | 75 ++- 4 files changed, 942 insertions(+), 104 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index a07faeba88e8e..21b2c73ddfff7 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -9,9 +9,27 @@ #include +namespace { + class SkRegionAdapter { public: - void addRect(const SkIRect& rect) { region_.op(rect, SkRegion::kUnion_Op); } + void addRects(std::vector&& rects) { + for (const auto& rect : rects) { + region_.op(rect, SkRegion::kUnion_Op); + } + } + + SkIRect getBounds() { return region_.getBounds(); } + + void addRegion(const SkRegionAdapter& region) { + region_.op(region.region_, SkRegion::kUnion_Op); + } + + bool intersects(const SkRegionAdapter& region) { + return region_.intersects(region.region_); + } + + bool intersects(const SkIRect& rect) { return region_.intersects(rect); } std::vector getRects() { std::vector rects; @@ -29,64 +47,260 @@ class SkRegionAdapter { class DlRegionAdapter { public: - void addRect(const SkIRect& rect) { rects_.push_back(rect); } + void addRects(std::vector&& rects) { + region_.addRects(std::move(rects)); + } - std::vector getRects() { - flutter::DlRegion region(std::move(rects_)); - return region.getRects(false); + void addRegion(const DlRegionAdapter& region) { + region_.addRegion(region.region_); } + SkIRect getBounds() { return region_.bounds(); } + + bool intersects(const DlRegionAdapter& region) { + return region_.intersects(region.region_); + } + + bool intersects(const SkIRect& rect) { return region_.intersects(rect); } + + std::vector getRects() { return region_.getRects(false); } + + DlRegionAdapter() {} + + DlRegionAdapter(const DlRegionAdapter& copy) : region_(copy.region_, true) {} + private: - std::vector rects_; + flutter::DlRegion region_; }; template -void RunRegionBenchmark(benchmark::State& state, int maxSize) { - while (state.KeepRunning()) { - std::random_device d; - std::seed_seq seed{2, 1, 3}; - std::mt19937 rng(seed); +void RunAddRectsBenchmark(benchmark::State& state, int maxSize) { + std::random_device d; + std::seed_seq seed{2, 1, 3}; + std::mt19937 rng(seed); - std::uniform_int_distribution pos(0, 4000); - std::uniform_int_distribution size(1, maxSize); + std::uniform_int_distribution pos(0, 4000); + std::uniform_int_distribution size(1, maxSize); + std::vector rects; + for (int i = 0; i < 2000; ++i) { + SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects.push_back(rect); + } + + while (state.KeepRunning()) { Region region; + region.addRects(std::move(rects)); + auto vec2 = region.getRects(); + } +} - for (int i = 0; i < 2000; ++i) { - SkIRect rect = - SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); - region.addRect(rect); - } +template +void RunAddRegionBenchmark(benchmark::State& state, int maxSize) { + std::random_device d; + std::seed_seq seed{2, 1, 3}; + std::mt19937 rng(seed); - auto vec2 = region.getRects(); + std::uniform_int_distribution pos(0, 4000); + std::uniform_int_distribution size(1, maxSize); + + std::vector rects; + for (int i = 0; i < 500; ++i) { + SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects.push_back(rect); + } + + Region base; + + Region region1(base); + region1.addRects(std::move(rects)); + + rects.clear(); + for (int i = 0; i < 500; ++i) { + SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects.push_back(rect); + } + Region region2(base); + region2.addRects(std::move(rects)); + + while (state.KeepRunning()) { + Region copy_of_region1(region1); + copy_of_region1.addRegion(region2); + // Region copy_of_region2(region2); + // copy_of_region2.addRegion(region1); } } +template +void RunIntersectsRegionBenchmark(benchmark::State& state, int maxSize) { + std::random_device d; + std::seed_seq seed{2, 1, 3}; + std::mt19937 rng(seed); + + std::uniform_int_distribution pos(0, 4000); + std::uniform_int_distribution size(1, maxSize); + + std::vector rects; + for (int i = 0; i < 500; ++i) { + SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects.push_back(rect); + } + + Region base; + + Region region1(base); + region1.addRects(std::move(rects)); + + rects.clear(); + for (int i = 0; i < 500; ++i) { + SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects.push_back(rect); + } + Region region2(base); + region2.addRects(std::move(rects)); + + while (state.KeepRunning()) { + region1.intersects(region2); + } +} + +template +void RunIntersectsSingleRectBenchmark(benchmark::State& state, int maxSize) { + std::random_device d; + std::seed_seq seed{2, 1, 3}; + std::mt19937 rng(seed); + + std::uniform_int_distribution pos(0, 4000); + std::uniform_int_distribution size(1, maxSize); + + std::vector rects; + for (int i = 0; i < 500; ++i) { + SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects.push_back(rect); + } + + Region base; + + Region region1(base); + region1.addRects(std::move(rects)); + + rects.clear(); + for (int i = 0; i < 100; ++i) { + SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects.push_back(rect); + } + + while (state.KeepRunning()) { + for (auto& rect : rects) { + region1.intersects(rect); + } + } +} + +} // namespace + namespace flutter { -static void BM_RegionBenchmarkSkRegion(benchmark::State& state, int maxSize) { - RunRegionBenchmark(state, maxSize); +static void BM_DlRegion_AddRects(benchmark::State& state, int maxSize) { + RunAddRectsBenchmark(state, maxSize); +} + +static void BM_SkRegion_AddRects(benchmark::State& state, int maxSize) { + RunAddRectsBenchmark(state, maxSize); } -static void BM_RegionBenchmarkDlRegion(benchmark::State& state, int maxSize) { - RunRegionBenchmark(state, maxSize); +static void BM_DlRegion_AddRegion(benchmark::State& state, int maxSize) { + RunAddRegionBenchmark(state, maxSize); } -BENCHMARK_CAPTURE(BM_RegionBenchmarkDlRegion, Tiny, 30) +static void BM_SkRegion_AddRegion(benchmark::State& state, int maxSize) { + RunAddRegionBenchmark(state, maxSize); +} + +static void BM_DlRegion_IntersectsRegion(benchmark::State& state, int maxSize) { + RunIntersectsRegionBenchmark(state, maxSize); +} + +static void BM_SkRegion_IntersectsRegion(benchmark::State& state, int maxSize) { + RunIntersectsRegionBenchmark(state, maxSize); +} + +static void BM_DlRegion_IntersectsSingleRect(benchmark::State& state, + int maxSize) { + RunIntersectsSingleRectBenchmark(state, maxSize); +} + +static void BM_SkRegion_IntersectsSingleRect(benchmark::State& state, + int maxSize) { + RunIntersectsSingleRectBenchmark(state, maxSize); +} + +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsSingleRect, Tiny, 30) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsSingleRect, Tiny, 30) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsSingleRect, Small, 100) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsSingleRect, Small, 100) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsSingleRect, Medium, 400) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsSingleRect, Medium, 400) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsSingleRect, Large, 1500) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsSingleRect, Large, 1500) + ->Unit(benchmark::kNanosecond); + +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Tiny, 30) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Tiny, 30) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Small, 100) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Small, 100) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Medium, 400) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Medium, 400) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Large, 1500) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Large, 1500) + ->Unit(benchmark::kNanosecond); + +BENCHMARK_CAPTURE(BM_DlRegion_AddRegion, Tiny, 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_AddRegion, Tiny, 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_AddRegion, Small, 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_AddRegion, Small, 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_AddRegion, Medium, 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_AddRegion, Medium, 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_AddRegion, Large, 1500) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_AddRegion, Large, 1500) + ->Unit(benchmark::kMicrosecond); + +BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Tiny, 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_RegionBenchmarkSkRegion, Tiny, 30) +BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Tiny, 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_RegionBenchmarkDlRegion, Small, 100) +BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Small, 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_RegionBenchmarkSkRegion, Small, 100) +BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Small, 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_RegionBenchmarkDlRegion, Medium, 400) +BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Medium, 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_RegionBenchmarkSkRegion, Medium, 400) +BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Medium, 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_RegionBenchmarkDlRegion, Large, 1500) +BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Large, 1500) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_RegionBenchmarkSkRegion, Large, 1500) +BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Large, 1500) ->Unit(benchmark::kMicrosecond); } // namespace flutter \ No newline at end of file diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 281fd142b393f..90e7e2586862c 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -8,31 +8,330 @@ namespace flutter { -DlRegion::DlRegion(std::vector&& rects) { - // If SpanLines can not be memmoved `addRect` would be signifantly slower +class SpanBuffer { + public: + static const uint32_t kDefaultCapacity; + + SpanBuffer() { + free_handles_.reserve(256); + chunks_.reserve(256); + data_.reserve(2048); + } + + ~SpanBuffer() { + // fprintf(stderr, "Allocated size %zu, Handles: %zu, Free handles: %zu\n", + // data_.size(), chunks_.size(), free_handles_.size()); + } + + SpanChunkHandle allocateChunk(uint32_t capacity = kDefaultCapacity) { + for (auto it = free_handles_.rbegin(); it != free_handles_.rend(); ++it) { + SpanChunkInfo& info = chunks_[*it]; + if (info.capacity >= capacity) { + SpanChunkHandle handle = *it; + if (free_handles_.size() > 1) { + *it = free_handles_.back(); + free_handles_.pop_back(); + } else { + free_handles_.clear(); + } + ++info.ref_count; + info.size = 0; + return handle; + } + } + return allocateWithCapacity(capacity); + } + + SpanChunkHandle duplicateChunk(SpanChunkHandle handle) { + SpanChunkInfo& info = chunks_[handle]; + ++info.ref_count; + return handle; + } + + SpanChunkHandle copyChunk(DlRegion::Span* begin, DlRegion::Span* end) { + size_t size = end - begin; + SpanChunkHandle new_handle = allocateChunk(size * 2); + SpanChunkInfo& info = chunks_[new_handle]; + std::memcpy(&data_[info.offset], begin, size * sizeof(DlRegion::Span)); + info.size = size; + return new_handle; + } + + void freeChunk(SpanChunkHandle handle) { + SpanChunkInfo& info = chunks_[handle]; + FML_DCHECK(info.ref_count > 0); + --info.ref_count; + if (info.ref_count == 0) { + free_handles_.push_back(handle); + } + } + + size_t getChunkSize(SpanChunkHandle handle) { return chunks_[handle].size; } + + void updateChunkSize(SpanChunkHandle handle, size_t size) { + FML_DCHECK(chunks_[handle].ref_count == 1); + SpanChunkInfo& info = chunks_[handle]; + FML_DCHECK(size <= info.capacity); + info.size = size; + } + + void getSpans(SpanChunkHandle handle, + DlRegion::Span*& begin, + DlRegion::Span*& end) { + const SpanChunkInfo& info = chunks_[handle]; + begin = &data_[info.offset]; + end = begin + info.size; + } + + void eraseSpans(SpanChunkHandle handle, size_t begin, size_t end) { + FML_DCHECK(chunks_[handle].ref_count == 1); + SpanChunkInfo& info = chunks_[handle]; + + if (end == info.size) { + info.size = begin; + return; + } + + DlRegion::Span* span_begin = &data_[info.offset]; + DlRegion::Span* span_end = span_begin + info.size; + DlRegion::Span* erase_begin = span_begin + begin; + DlRegion::Span* erase_end = span_begin + end; + std::memmove(erase_begin, erase_end, + (span_end - erase_end) * sizeof(DlRegion::Span)); + info.size -= end - begin; + } + + void appendSpan(SpanChunkHandle handle, const DlRegion::Span& span) { + FML_DCHECK(chunks_[handle].ref_count == 1); + ensureSpace(handle); + SpanChunkInfo& info = chunks_[handle]; + DlRegion::Span* begin = &data_[info.offset]; + DlRegion::Span* end = begin + info.size; + *end = span; + ++info.size; + } + + void insertSpan(SpanChunkHandle handle, + size_t index, + const DlRegion::Span& span) { + FML_DCHECK(chunks_[handle].ref_count == 1); + ensureSpace(handle); + SpanChunkInfo& info = chunks_[handle]; + DlRegion::Span* begin = &data_[info.offset]; + DlRegion::Span* end = begin + info.size; + DlRegion::Span* insert = begin + index; + std::memmove(insert + 1, insert, (end - insert) * sizeof(DlRegion::Span)); + *insert = span; + ++info.size; + } + + SpanChunkHandle ensureChunkWritable(SpanChunkHandle handle) { + SpanChunkInfo info = chunks_[handle]; + FML_DCHECK(info.ref_count > 0); + if (info.ref_count == 1) { + return handle; + } else { + SpanChunkHandle new_handle = allocateChunk(info.capacity); + SpanChunkInfo& new_info = chunks_[new_handle]; + new_info.size = info.size; + std::memmove(&data_[new_info.offset], &data_[info.offset], + info.size * sizeof(DlRegion::Span)); + --chunks_[handle].ref_count; + return new_handle; + } + } + + private: + static size_t round_to_8(size_t x) { return ((x + 7) & (-8)); } + + SpanChunkHandle allocateWithCapacity(uint32_t capacity) { + capacity = round_to_8(std::max(capacity, kDefaultCapacity)); + chunks_.push_back({static_cast(data_.size()), 0, capacity, 1}); + data_.resize(data_.size() + capacity); + return chunks_.size() - 1; + } + + void ensureSpace(SpanChunkHandle handle) { + SpanChunkInfo& info = chunks_[handle]; + if (info.size == info.capacity) { + auto previous_info = info; + info.capacity *= 2; + auto prev_offset = info.offset; + info.offset = data_.size(); + data_.resize(data_.size() + info.capacity); + std::memmove(&data_[info.offset], &data_[prev_offset], + info.size * sizeof(DlRegion::Span)); + chunks_.push_back({ + previous_info.offset, + 0, + previous_info.capacity, + 0, + }); + free_handles_.push_back(chunks_.size() - 1); + } + } + + struct SpanChunkInfo { + /// Offset from the beginning of the buffer. + uint32_t offset; + uint32_t size; + uint32_t capacity; + int32_t ref_count; + }; + + std::vector data_; + std::vector chunks_; + std::vector free_handles_; +}; + +const uint32_t SpanBuffer::kDefaultCapacity = 16; + +DlRegion::DlRegion() : span_buffer_(std::make_shared()) {} + +DlRegion::DlRegion(std::vector&& rects) + : span_buffer_(std::make_shared()) { + // If SpanLines can not be memmoved `addRect` would be significantly slower // due to cost of inserting and removing elements from the `lines_` vector. static_assert(std::is_trivially_constructible::value, "SpanLine must be trivially constructible."); addRects(std::move(rects)); +} - for (auto& spanvec : spanvec_pool_) { - delete spanvec; +DlRegion::~DlRegion() { + for (auto& line : lines_) { + span_buffer_->freeChunk(line.chunk_handle); } - spanvec_pool_.clear(); } -DlRegion::~DlRegion() { +DlRegion::DlRegion(const DlRegion& region, bool share_buffer) + : span_buffer_(share_buffer + ? region.span_buffer_ + : std::make_shared(*region.span_buffer_)) { + lines_ = region.lines_; for (auto& line : lines_) { - delete line.spans; + line.chunk_handle = span_buffer_->duplicateChunk(line.chunk_handle); + } + bounds_ = region.bounds_; +} + +bool DlRegion::spansIntersect(const Span* begin1, + const Span* end1, + const Span* begin2, + const Span* end2) const { + while (begin1 != end1 && begin2 != end2) { + if (begin1->right <= begin2->left) { + ++begin1; + } else if (begin2->right <= begin1->left) { + ++begin2; + } else { + return true; + } + } + return false; +} + +bool DlRegion::intersects(const SkIRect& rect) const { + if (isEmpty()) { + return false; + } + + auto bounds_intersect = SkIRect::Intersects(bounds_, rect); + + if (!isComplex()) { + return bounds_intersect; + } + + if (!bounds_intersect) { + return false; + } + + auto it = std::upper_bound( + lines_.begin(), lines_.end(), rect.fTop, + [](int32_t i, const SpanLine& line) { return i < line.bottom; }); + + while (it != lines_.end() && it->top < rect.fBottom) { + FML_DCHECK(rect.fTop < it->bottom || it->top < rect.fBottom); + Span *begin, *end; + span_buffer_->getSpans(it->chunk_handle, begin, end); + while (begin != end && begin->left < rect.fRight) { + if (begin->right > rect.fLeft && begin->left < rect.fRight) { + return true; + } + ++begin; + } + ++it; + } + + return false; +} + +bool DlRegion::intersects(const DlRegion& region) const { + if (isEmpty() || region.isEmpty()) { + return false; + } + + auto our_complex = isComplex(); + auto their_complex = region.isComplex(); + auto bounds_intersect = SkIRect::Intersects(bounds_, region.bounds_); + + if (!our_complex && !their_complex) { + return bounds_intersect; + } + + if (!bounds_intersect) { + return false; + } + + if (!our_complex) { + return region.intersects(bounds_); + } + + if (!their_complex) { + return intersects(region.bounds_); + } + + auto ours = lines_.begin(); + auto theirs = region.lines_.begin(); + + while (ours != lines_.end() && theirs != region.lines_.end()) { + if (ours->bottom <= theirs->top) { + ++ours; + } else if (theirs->bottom <= ours->top) { + ++theirs; + } else { + FML_DCHECK(ours->top < theirs->bottom || theirs->top < ours->bottom); + Span *ours_begin, *ours_end; + span_buffer_->getSpans(ours->chunk_handle, ours_begin, ours_end); + Span *theirs_begin, *theirs_end; + region.span_buffer_->getSpans(theirs->chunk_handle, theirs_begin, + theirs_end); + if (spansIntersect(ours_begin, ours_end, theirs_begin, theirs_end)) { + return true; + } + if (ours->bottom < theirs->bottom) { + ++ours; + } else { + ++theirs; + } + } } + return false; } std::vector DlRegion::getRects(bool deband) const { std::vector rects; + size_t rect_count = 0; size_t previous_span_end = 0; for (const auto& line : lines_) { - for (const Span& span : *line.spans) { - SkIRect rect{span.left, line.top, span.right, line.bottom}; + rect_count += span_buffer_->getChunkSize(line.chunk_handle); + } + rects.reserve(rect_count); + + for (const auto& line : lines_) { + Span *span_begin, *span_end; + span_buffer_->getSpans(line.chunk_handle, span_begin, span_end); + for (const auto* span = span_begin; span < span_end; ++span) { + SkIRect rect{span->left, line.top, span->right, line.bottom}; if (deband) { auto iter = rects.begin() + previous_span_end; // If there is rectangle previously in rects on which this one is a @@ -60,43 +359,64 @@ std::vector DlRegion::getRects(bool deband) const { return rects; } -void DlRegion::SpanLine::insertSpan(int32_t left, int32_t right) { - auto& spans = *this->spans; - auto size = spans.size(); +void DlRegion::SpanLine::insertSpan(SpanBuffer& span_buffer, + int32_t left, + int32_t right) { + Span *span_begin, *span_end; + chunk_handle = span_buffer.ensureChunkWritable(chunk_handle); + span_buffer.getSpans(chunk_handle, span_begin, span_end); + size_t size = span_end - span_begin; for (size_t i = 0; i < size; ++i) { - Span& span = spans[i]; + Span& span = span_begin[i]; if (right < span.left) { - spans.insert(spans.begin() + i, {left, right}); + span_buffer.insertSpan(chunk_handle, i, {left, right}); return; } if (left > span.right) { continue; } size_t last_index = i; - while (last_index + 1 < size && right >= spans[last_index + 1].left) { + while (last_index + 1 < size && right >= span_begin[last_index + 1].left) { ++last_index; } span.left = std::min(span.left, left); - span.right = std::max(spans[last_index].right, right); + span.right = std::max(span_begin[last_index].right, right); if (last_index > i) { - spans.erase(spans.begin() + i + 1, spans.begin() + last_index + 1); + span_buffer.eraseSpans(chunk_handle, i + 1, last_index + 1); } return; } - spans.push_back({left, right}); + span_buffer.appendSpan(chunk_handle, {left, right}); } -bool DlRegion::SpanLine::spansEqual(const SpanLine& l2) const { - SpanVec& spans = *this->spans; - SpanVec& otherSpans = *l2.spans; +void DlRegion::SpanLine::insertSpans(SpanBuffer& buffer, + const Span* begin, + const Span* end) { + for (auto* span = begin; span != end; ++span) { + insertSpan(buffer, span->left, span->right); + } +} + +bool DlRegion::SpanLine::spansEqual(SpanBuffer& buffer, + const SpanLine& l2) const { FML_DCHECK(this != &l2); - if (spans.size() != otherSpans.size()) { + if (chunk_handle == l2.chunk_handle) { + return true; + } + + Span *our_begin, *our_end, *outher_begin, *other_end; + buffer.getSpans(chunk_handle, our_begin, our_end); + buffer.getSpans(l2.chunk_handle, outher_begin, other_end); + + size_t our_size = our_end - our_begin; + size_t other_size = other_end - outher_begin; + + if (our_size != other_size) { return false; } - return memcmp(spans.data(), otherSpans.data(), spans.size() * sizeof(Span)) == - 0; + return memcmp(our_begin, outher_begin, our_size * sizeof(Span)) == 0; } void DlRegion::insertLine(size_t position, SpanLine line) { @@ -105,7 +425,7 @@ void DlRegion::insertLine(size_t position, SpanLine line) { DlRegion::LineVec::iterator DlRegion::removeLine( DlRegion::LineVec::iterator line) { - spanvec_pool_.push_back(line->spans); + span_buffer_->freeChunk(line->chunk_handle); return lines_.erase(line); } @@ -113,30 +433,137 @@ DlRegion::SpanLine DlRegion::makeLine(int32_t top, int32_t bottom, int32_t spanLeft, int32_t spanRight) { - SpanVec* span_vec; - if (!spanvec_pool_.empty()) { - span_vec = spanvec_pool_.back(); - spanvec_pool_.pop_back(); - span_vec->clear(); - } else { - span_vec = new SpanVec(); + auto handle = span_buffer_->allocateChunk(); + span_buffer_->appendSpan(handle, {spanLeft, spanRight}); + return {top, bottom, handle}; +} + +DlRegion::SpanLine DlRegion::mergeLines(int32_t top, + int32_t bottom, + SpanChunkHandle our_handle, + SpanBuffer* their_span_buffer, + SpanChunkHandle their_handle) { + Span *begin2, *end2; + their_span_buffer->getSpans(their_handle, begin2, end2); + + auto handle = span_buffer_->allocateChunk( + span_buffer_->getChunkSize(our_handle) + (end2 - begin2)); + Span *begin1, *end1; + span_buffer_->getSpans(our_handle, begin1, end1); + + Span *begin, *end; + span_buffer_->getSpans(handle, begin, end); + + while (true) { + if (begin1->right < begin2->left - 1) { + *end = *begin1; + ++begin1; + ++end; + if (begin1 == end1) { + break; + } + } else if (begin2->right < begin1->left) { + *end = *begin2; + ++begin2; + ++end; + if (begin2 == end2) { + break; + } + } else { + break; + } + } + + Span currentSpan{0, 0}; + while (begin1 != end1 && begin2 != end2) { + if (currentSpan.left == currentSpan.right) { + if (begin1->right < begin2->left - 1) { + *end = *begin1; + ++begin1; + ++end; + } else if (begin2->right < begin1->left) { + *end = *begin2; + ++begin2; + ++end; + } else if (begin1->left == begin2->left) { + currentSpan.left = begin1->left; + currentSpan.right = std::max(begin1->right, begin2->right); + ++begin1; + ++begin2; + } else if (begin1->left < begin2->left) { + currentSpan.left = begin1->left; + currentSpan.right = begin1->right; + ++begin1; + } else { + currentSpan.left = begin2->left; + currentSpan.right = begin2->right; + ++begin2; + } + } else if (currentSpan.right >= begin1->left) { + currentSpan.right = std::max(currentSpan.right, begin1->right); + ++begin1; + } else if (currentSpan.right >= begin2->left) { + currentSpan.right = std::max(currentSpan.right, begin2->right); + ++begin2; + } else { + *end = currentSpan; + ++end; + currentSpan.left = currentSpan.right = 0; + } } - span_vec->push_back({spanLeft, spanRight}); - return {top, bottom, span_vec}; + + if (currentSpan.left != currentSpan.right) { + while (begin1 != end1 && currentSpan.right >= begin1->left) { + currentSpan.right = std::max(currentSpan.right, begin1->right); + ++begin1; + } + while (begin2 != end2 && currentSpan.right >= begin2->left) { + currentSpan.right = std::max(currentSpan.right, begin2->right); + ++begin2; + } + + *end = currentSpan; + ++end; + } + + FML_DCHECK(begin1 == end1 || begin2 == end2); + + while (begin1 != end1) { + *end = *begin1; + ++begin1; + ++end; + } + + while (begin2 != end2) { + *end = *begin2; + ++begin2; + ++end; + } + + span_buffer_->updateChunkSize(handle, end - begin); + + return {top, bottom, handle}; } -DlRegion::SpanLine DlRegion::makeLine(int32_t top, - int32_t bottom, - const SpanVec& spans) { - SpanVec* span_vec; - if (!spanvec_pool_.empty()) { - span_vec = spanvec_pool_.back(); - spanvec_pool_.pop_back(); +DlRegion::SpanLine DlRegion::duplicateLine(int32_t top, + int32_t bottom, + SpanBuffer* span_buffer, + SpanChunkHandle handle) { + if (span_buffer == span_buffer_.get()) { + return {top, bottom, span_buffer_->duplicateChunk(handle)}; } else { - span_vec = new SpanVec(); + SpanChunkHandle new_handle; + Span *span_begin, *span_end; + span_buffer->getSpans(handle, span_begin, span_end); + new_handle = span_buffer_->copyChunk(span_begin, span_end); + return {top, bottom, new_handle}; } - *span_vec = spans; - return {top, bottom, span_vec}; +} + +DlRegion::SpanLine DlRegion::duplicateLine(int32_t top, + int32_t bottom, + SpanChunkHandle handle) { + return {top, bottom, span_buffer_->duplicateChunk(handle)}; } void DlRegion::addRects(std::vector&& rects) { @@ -164,6 +591,8 @@ void DlRegion::addRects(std::vector&& rects) { continue; } + bounds_.join(rect); + int32_t y1 = rect.fTop; int32_t y2 = rect.fBottom; @@ -193,22 +622,22 @@ void DlRegion::addRects(std::vector&& rects) { auto prevLineEnd = line.bottom; line.bottom = y1; mark_dirty(i); - insertLine(i + 1, makeLine(y1, prevLineEnd, *line.spans)); + insertLine(i + 1, duplicateLine(y1, prevLineEnd, line.chunk_handle)); continue; } FML_DCHECK(y1 == line.top); if (y2 < line.bottom) { // duplicate line - auto newLine = makeLine(y2, line.bottom, *line.spans); + auto new_line = duplicateLine(y2, line.bottom, line.chunk_handle); line.bottom = y2; - line.insertSpan(rect.fLeft, rect.fRight); - insertLine(i + 1, newLine); + line.insertSpan(*span_buffer_, rect.fLeft, rect.fRight); + insertLine(i + 1, new_line); y1 = y2; mark_dirty(i); break; } FML_DCHECK(y2 >= line.bottom); - line.insertSpan(rect.fLeft, rect.fRight); + line.insertSpan(*span_buffer_, rect.fLeft, rect.fRight); mark_dirty(i); y1 = line.bottom; } @@ -231,7 +660,7 @@ void DlRegion::addRects(std::vector&& rects) { i < lines_.begin() + dirty_end;) { auto& line = *i; auto& next = *(i + 1); - if (line.bottom == next.top && line.spansEqual(next)) { + if (line.bottom == next.top && line.spansEqual(*span_buffer_, next)) { --dirty_end; next.top = line.top; i = removeLine(i); @@ -245,4 +674,95 @@ void DlRegion::addRects(std::vector&& rects) { } } +bool DlRegion::isComplex() const { + return lines_.size() > 1 || + (lines_.size() == 1 && + span_buffer_->getChunkSize(lines_.front().chunk_handle) > 1); +} + +void DlRegion::addRegion(const DlRegion& region) { + bounds_.join(region.bounds_); + + LineVec res; + res.reserve(lines_.size() + region.lines_.size()); + + auto append_line = [&](SpanLine line) { + if (res.empty()) { + res.push_back(line); + } else { + if (res.back().bottom == line.top && + res.back().spansEqual(*span_buffer_, line)) { + res.back().bottom = line.bottom; + span_buffer_->freeChunk(line.chunk_handle); + } else { + res.push_back(line); + } + } + }; + + LineVec::iterator ours = lines_.begin(); + auto theirs_copy = region.lines_; + LineVec::iterator theirs = theirs_copy.begin(); + + auto their_span_buffer = region.span_buffer_.get(); + + while (ours != lines_.end() && theirs != theirs_copy.end()) { + if (ours->bottom <= theirs->top) { + append_line(*ours); + ++ours; + } else if (theirs->bottom <= ours->top) { + append_line(duplicateLine(theirs->top, theirs->bottom, their_span_buffer, + theirs->chunk_handle)); + ++theirs; + } else { + if (ours->top < theirs->top) { + append_line(duplicateLine(ours->top, theirs->top, ours->chunk_handle)); + ours->top = theirs->top; + if (ours->top == ours->bottom) { + span_buffer_->freeChunk(ours->chunk_handle); + ++ours; + } + } else if (theirs->top < ours->top) { + append_line(duplicateLine(theirs->top, ours->top, their_span_buffer, + theirs->chunk_handle)); + theirs->top = ours->top; + if (theirs->top == theirs->bottom) { + ++theirs; + } + } else { + auto new_bottom = std::min(ours->bottom, theirs->bottom); + FML_DCHECK(ours->top == theirs->top); + FML_DCHECK(new_bottom > ours->top); + FML_DCHECK(new_bottom > theirs->top); + append_line(mergeLines(ours->top, new_bottom, ours->chunk_handle, + their_span_buffer, theirs->chunk_handle)); + ours->top = new_bottom; + if (ours->top == ours->bottom) { + span_buffer_->freeChunk(ours->chunk_handle); + ++ours; + } + theirs->top = new_bottom; + if (theirs->top == theirs->bottom) { + ++theirs; + } + } + } + } + + FML_DCHECK(ours == lines_.end() || theirs == theirs_copy.end()); + + while (ours != lines_.end()) { + append_line(*ours); + ++ours; + } + + while (theirs != theirs_copy.end()) { + append_line(duplicateLine(theirs->top, theirs->bottom, their_span_buffer, + theirs->chunk_handle)); + ++theirs; + } + + lines_ = std::move(res); +} + } // namespace flutter diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index 159b32c37d7dd..838ee69100c1b 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -7,20 +7,45 @@ #include "third_party/skia/include/core/SkRect.h" +#include #include namespace flutter { +typedef std::uint32_t SpanChunkHandle; +class SpanBuffer; + /// Represents a region as a collection of non-overlapping rectangles. /// Implements a subset of SkRegion functionality optimized for quickly /// converting set of overlapping rectangles to non-overlapping rectangles. class DlRegion { public: - /// Creates region by bulk adding the rectangles./// Matches - /// SkRegion::op(rect, SkRegion::kUnion_Op) behavior. + /// Creates empty region. + DlRegion(); + + /// Creates region by bulk adding the rectangles. + /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. explicit DlRegion(std::vector&& rects); + + /// Creates copy of the region. + DlRegion(const DlRegion& r) : flutter::DlRegion(r, false) {} + + /// Creates copy of the region. + /// If |share_buffer| is true, the new region will share the same internal + /// span buffer as the original region. This means that both region must + /// be used on the same thread. + DlRegion(const DlRegion&, bool share_buffer); + ~DlRegion(); + /// Adds group of rectangles to the region. + /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. + void addRects(std::vector&& rects); + + /// Adds another region to this region. + /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. + void addRegion(const DlRegion& region); + /// Returns list of non-overlapping rectangles that cover current region. /// If |deband| is false, each span line will result in separate rectangles, /// closely matching SkRegion::Iterator behavior. @@ -28,9 +53,18 @@ class DlRegion { /// merged into single rectange. std::vector getRects(bool deband = true) const; - private: - void addRects(std::vector&& rects); + /// Returns whether this region intersects with a rectangle. + bool intersects(const SkIRect& rect) const; + + /// Returns whether this region intersects with another region. + bool intersects(const DlRegion& region) const; + /// Returns maximum and minimum axis values of rectangles in this region. + /// If region is empty returns SKIRect::MakeEmpty(). + const SkIRect& bounds() const { return bounds_; } + + private: + friend class SpanBuffer; struct Span { int32_t left; int32_t right; @@ -39,16 +73,23 @@ class DlRegion { struct SpanLine { int32_t top; int32_t bottom; - SpanVec* spans; + SpanChunkHandle chunk_handle; - void insertSpan(int32_t left, int32_t right); - bool spansEqual(const SpanLine& l2) const; + void insertSpan(SpanBuffer& span_buffer, int32_t left, int32_t right); + void insertSpans(SpanBuffer& span_buffer, + const Span* begin, + const Span* end); + bool spansEqual(SpanBuffer& span_buffer, const SpanLine& l2) const; }; typedef std::vector LineVec; + bool isEmpty() const { return lines_.empty(); } + bool isComplex() const; + std::vector lines_; - std::vector spanvec_pool_; + + SkIRect bounds_ = SkIRect::MakeEmpty(); void insertLine(size_t position, SpanLine line); LineVec::iterator removeLine(LineVec::iterator position); @@ -57,7 +98,23 @@ class DlRegion { int32_t bottom, int32_t spanLeft, int32_t spanRight); - SpanLine makeLine(int32_t top, int32_t bottom, const SpanVec& spans); + SpanLine duplicateLine(int32_t top, int32_t bottom, SpanChunkHandle handle); + SpanLine duplicateLine(int32_t top, + int32_t bottom, + SpanBuffer* span_buffer, + SpanChunkHandle handle); + SpanLine mergeLines(int32_t top, + int32_t bottom, + SpanChunkHandle our_handle, + SpanBuffer* their_span_buffer, + SpanChunkHandle their_handle); + + bool spansIntersect(const Span* begin1, + const Span* end1, + const Span* begin2, + const Span* end2) const; + + std::shared_ptr span_buffer_; }; } // namespace flutter diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index f9610ff3c5561..f4f03389d31ab 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -13,7 +13,7 @@ namespace flutter { namespace testing { TEST(DisplayListRegion, EmptyRegion) { - DlRegion region({}); + DlRegion region; EXPECT_TRUE(region.getRects().empty()); } @@ -150,12 +150,43 @@ TEST(DisplayListRegion, Deband) { EXPECT_EQ(rects_without_deband, expected_without_deband); } +void CheckEquality(const DlRegion& dl_region, const SkRegion& sk_region) { + EXPECT_EQ(dl_region.bounds(), sk_region.getBounds()); + + // Do not deband the rectangles - identical to SkRegion::Iterator + auto rects = dl_region.getRects(false); + + std::vector skia_rects; + + auto iterator = SkRegion::Iterator(sk_region); + while (!iterator.done()) { + skia_rects.push_back(iterator.rect()); + iterator.next(); + } + + if (rects != skia_rects) { + fprintf(stderr, "----- %zu %zu\n", rects.size(), skia_rects.size()); + for (size_t i = 0; i < std::min(rects.size(), skia_rects.size()); ++i) { + if (rects[i] != skia_rects[i]) { + fprintf(stderr, "A %d %d %d %d\n", rects[i].fLeft, rects[i].fTop, + rects[i].fRight, rects[i].fBottom); + fprintf(stderr, "B %d %d %d %d\n", skia_rects[i].fLeft, + skia_rects[i].fTop, skia_rects[i].fRight, + skia_rects[i].fBottom); + } + } + } + + EXPECT_EQ(rects, skia_rects); +} + TEST(DisplayListRegion, TestAgainstSkRegion) { struct Settings { int max_size; size_t iteration_count; }; std::vector all_settings{ + {100, 1}, // {100, 10}, // {100, 100}, // {100, 1000}, // @@ -170,36 +201,52 @@ TEST(DisplayListRegion, TestAgainstSkRegion) { for (const auto& settings : all_settings) { std::random_device d; std::seed_seq seed{::testing::UnitTest::GetInstance()->random_seed()}; + // std::seed_seq seed{133}; std::mt19937 rng(seed); - SkRegion sk_region; + SkRegion sk_region1; + SkRegion sk_region2; std::uniform_int_distribution pos(0, 4000); std::uniform_int_distribution size(1, settings.max_size); - std::vector rects_in; + std::vector rects_in1; + std::vector rects_in2; for (size_t i = 0; i < settings.iteration_count; ++i) { SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); - rects_in.push_back(rect); - sk_region.op(rect, SkRegion::kUnion_Op); + rects_in1.push_back(rect); + sk_region1.op(rect, SkRegion::kUnion_Op); + + rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects_in2.push_back(rect); + sk_region2.op(rect, SkRegion::kUnion_Op); } - DlRegion region(std::move(rects_in)); + DlRegion region1(std::move(rects_in1)); + CheckEquality(region1, sk_region1); - // Do not deband the rectangles - identical to SkRegion::Iterator - auto rects = region.getRects(false); + DlRegion region2(std::move(rects_in2)); + CheckEquality(region2, sk_region2); - std::vector skia_rects; + auto intersects_1 = region1.intersects(region2); + auto intersects_2 = region2.intersects(region1); + auto sk_intesects = sk_region1.intersects(sk_region2); + EXPECT_EQ(intersects_1, intersects_2); + EXPECT_EQ(intersects_1, sk_intesects); - auto iterator = SkRegion::Iterator(sk_region); - while (!iterator.done()) { - skia_rects.push_back(iterator.rect()); - iterator.next(); + { + auto rects = region2.getRects(true); + for (const auto& r : rects) { + EXPECT_EQ(region1.intersects(r), sk_region1.intersects(r)); + } } - EXPECT_EQ(rects, skia_rects); + region1.addRegion(region2); + sk_region1.op(sk_region2, SkRegion::kUnion_Op); + + CheckEquality(region1, sk_region1); } } From 28cfa129fe1c40bb28199a0905a5ee0e16af9dac Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 8 Jun 2023 11:48:13 +0200 Subject: [PATCH 02/39] Use new AddRects implementation --- .../benchmarking/dl_region_benchmarks.cc | 37 +-- display_list/geometry/dl_region.cc | 230 +++++++++++------- display_list/geometry/dl_region.h | 10 +- 3 files changed, 158 insertions(+), 119 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index 21b2c73ddfff7..d59c7a881d55b 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -13,7 +13,9 @@ namespace { class SkRegionAdapter { public: - void addRects(std::vector&& rects) { + SkRegionAdapter() {} + + explicit SkRegionAdapter(const std::vector& rects) { for (const auto& rect : rects) { region_.op(rect, SkRegion::kUnion_Op); } @@ -47,9 +49,10 @@ class SkRegionAdapter { class DlRegionAdapter { public: - void addRects(std::vector&& rects) { - region_.addRects(std::move(rects)); - } + DlRegionAdapter() {} + + explicit DlRegionAdapter(const std::vector& rects) + : region_(rects) {} void addRegion(const DlRegionAdapter& region) { region_.addRegion(region.region_); @@ -65,8 +68,6 @@ class DlRegionAdapter { std::vector getRects() { return region_.getRects(false); } - DlRegionAdapter() {} - DlRegionAdapter(const DlRegionAdapter& copy) : region_(copy.region_, true) {} private: @@ -89,8 +90,7 @@ void RunAddRectsBenchmark(benchmark::State& state, int maxSize) { } while (state.KeepRunning()) { - Region region; - region.addRects(std::move(rects)); + Region region(std::move(rects)); auto vec2 = region.getRects(); } } @@ -110,18 +110,14 @@ void RunAddRegionBenchmark(benchmark::State& state, int maxSize) { rects.push_back(rect); } - Region base; - - Region region1(base); - region1.addRects(std::move(rects)); + Region region1(rects); rects.clear(); for (int i = 0; i < 500; ++i) { SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); rects.push_back(rect); } - Region region2(base); - region2.addRects(std::move(rects)); + Region region2(rects); while (state.KeepRunning()) { Region copy_of_region1(region1); @@ -146,18 +142,14 @@ void RunIntersectsRegionBenchmark(benchmark::State& state, int maxSize) { rects.push_back(rect); } - Region base; - - Region region1(base); - region1.addRects(std::move(rects)); + Region region1(rects); rects.clear(); for (int i = 0; i < 500; ++i) { SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); rects.push_back(rect); } - Region region2(base); - region2.addRects(std::move(rects)); + Region region2(rects); while (state.KeepRunning()) { region1.intersects(region2); @@ -179,10 +171,7 @@ void RunIntersectsSingleRectBenchmark(benchmark::State& state, int maxSize) { rects.push_back(rect); } - Region base; - - Region region1(base); - region1.addRects(std::move(rects)); + Region region1(rects); rects.clear(); for (int i = 0; i < 100; ++i) { diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 90e7e2586862c..409f481ed8642 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -188,13 +188,13 @@ const uint32_t SpanBuffer::kDefaultCapacity = 16; DlRegion::DlRegion() : span_buffer_(std::make_shared()) {} -DlRegion::DlRegion(std::vector&& rects) +DlRegion::DlRegion(const std::vector& rects) : span_buffer_(std::make_shared()) { // If SpanLines can not be memmoved `addRect` would be significantly slower // due to cost of inserting and removing elements from the `lines_` vector. static_assert(std::is_trivially_constructible::value, "SpanLine must be trivially constructible."); - addRects(std::move(rects)); + addRects(rects); } DlRegion::~DlRegion() { @@ -398,6 +398,17 @@ void DlRegion::SpanLine::insertSpans(SpanBuffer& buffer, } } +bool DlRegion::SpanLine::spansEqual(SpanBuffer& buffer, + const DlRegion::SpanVec& vec) const { + Span *our_begin, *our_end; + buffer.getSpans(chunk_handle, our_begin, our_end); + size_t our_size = our_end - our_begin; + if (our_size != vec.size()) { + return false; + } + return memcmp(our_begin, vec.data(), our_size * sizeof(Span)) == 0; +} + bool DlRegion::SpanLine::spansEqual(SpanBuffer& buffer, const SpanLine& l2) const { FML_DCHECK(this != &l2); @@ -438,6 +449,17 @@ DlRegion::SpanLine DlRegion::makeLine(int32_t top, return {top, bottom, handle}; } +DlRegion::SpanLine DlRegion::makeLine(int32_t top, + int32_t bottom, + const DlRegion::SpanVec& v) { + auto handle = span_buffer_->allocateChunk(v.capacity()); + Span *begin, *end; + span_buffer_->getSpans(handle, begin, end); + memcpy(begin, v.data(), v.size() * sizeof(Span)); + span_buffer_->updateChunkSize(handle, v.size()); + return {top, bottom, handle}; +} + DlRegion::SpanLine DlRegion::mergeLines(int32_t top, int32_t bottom, SpanChunkHandle our_handle, @@ -566,112 +588,140 @@ DlRegion::SpanLine DlRegion::duplicateLine(int32_t top, return {top, bottom, span_buffer_->duplicateChunk(handle)}; } -void DlRegion::addRects(std::vector&& rects) { - std::sort(rects.begin(), rects.end(), [](const SkIRect& a, const SkIRect& b) { - // Sort the rectangles by Y axis. Because the rectangles have varying - // height, they are added to span lines in non-deterministic order and thus - // it makes no difference if they are also sorted by the X axis. - return a.top() < b.top(); +void DlRegion::addRects(const std::vector& unsorted_rects) { + size_t count = unsorted_rects.size(); + std::vector rects(count); + for (size_t i = 0; i < count; i++) { + rects[i] = &unsorted_rects[i]; + bounds_.join(unsorted_rects[i]); + } + std::sort(rects.begin(), rects.end(), [](const SkIRect* a, const SkIRect* b) { + if (a->top() < b->top()) { + return true; + } + if (a->top() > b->top()) { + return false; + } + return a->left() < b->left(); }); - size_t start_index = 0; - - size_t dirty_start = std::numeric_limits::max(); - size_t dirty_end = 0; - - // Marks line as dirty. Dirty lines will be checked for equality - // later and merged as needed. - auto mark_dirty = [&](size_t line) { - dirty_start = std::min(dirty_start, line); - dirty_end = std::max(dirty_end, line); - }; - - for (const SkIRect& rect : rects) { - if (rect.isEmpty()) { - continue; + size_t active_end = 0; + size_t next_rect = 0; + int32_t cur_y = std::numeric_limits::min(); + SpanVec working_spans; + +#ifdef DLREGION2_DO_STATS + size_t active_rect_count = 0; + size_t span_count = 0; + int pass_count = 0; + int line_count = 0; +#endif + + while (next_rect < count || active_end > 0) { + // First prune passed rects out of the active list + size_t preserve_end = 0; + for (size_t i = 0; i < active_end; i++) { + const SkIRect* r = rects[i]; + if (r->bottom() > cur_y) { + rects[preserve_end++] = r; + } } + active_end = preserve_end; - bounds_.join(rect); - - int32_t y1 = rect.fTop; - int32_t y2 = rect.fBottom; - - for (size_t i = start_index; i < lines_.size() && y1 < y2; ++i) { - SpanLine& line = lines_[i]; + // If we have no active rects any more, jump to the top of the + // next available input rect. + if (active_end == 0) { + if (next_rect >= count) { + // No active rects and no more rects to bring in. We are done. + break; + } + cur_y = rects[next_rect]->top(); + } - if (rect.fTop >= line.bottom) { - start_index = i; + // Next, insert any new rects we've reached into the active list + while (next_rect < count) { + const SkIRect* r = rects[next_rect]; + if (r->isEmpty()) { continue; } - - if (y2 <= line.top) { - insertLine(i, makeLine(y1, y2, rect.fLeft, rect.fRight)); - mark_dirty(i); - y1 = y2; + if (r->top() > cur_y) { break; } - if (y1 < line.top) { - auto prevLineStart = line.top; - insertLine(i, makeLine(y1, prevLineStart, rect.fLeft, rect.fRight)); - mark_dirty(i); - y1 = prevLineStart; - continue; + // We now know that we will be inserting this rect into active list + next_rect++; + size_t insert_at = active_end++; + while (insert_at > 0) { + const SkIRect* ir = rects[insert_at - 1]; + if (ir->left() <= r->left()) { + break; + } + rects[insert_at--] = ir; } - if (y1 > line.top) { - // duplicate line - auto prevLineEnd = line.bottom; - line.bottom = y1; - mark_dirty(i); - insertLine(i + 1, duplicateLine(y1, prevLineEnd, line.chunk_handle)); - continue; + rects[insert_at] = r; + } + + // We either preserved some rects in the active list or added more from + // the remaining input rects, or we would have exited the loop above. + FML_DCHECK(active_end != 0); + working_spans.clear(); + FML_DCHECK(working_spans.empty()); + +#ifdef DLREGION2_DO_STATS + active_rect_count += active_end; + pass_count++; +#endif + + // [start_x, end_x) always represents a valid span to be inserted + // [cur_y, end_y) is the intersecting range over which all spans are valid + int32_t start_x = rects[0]->left(); + int32_t end_x = rects[0]->right(); + int32_t end_y = rects[0]->bottom(); + for (size_t i = 1; i < active_end; i++) { + const SkIRect* r = rects[i]; + if (r->left() > end_x) { + working_spans.push_back({start_x, end_x}); + start_x = r->left(); + end_x = r->right(); + } else if (end_x < r->right()) { + end_x = r->right(); } - FML_DCHECK(y1 == line.top); - if (y2 < line.bottom) { - // duplicate line - auto new_line = duplicateLine(y2, line.bottom, line.chunk_handle); - line.bottom = y2; - line.insertSpan(*span_buffer_, rect.fLeft, rect.fRight); - insertLine(i + 1, new_line); - y1 = y2; - mark_dirty(i); - break; + if (end_y > r->bottom()) { + end_y = r->bottom(); } - FML_DCHECK(y2 >= line.bottom); - line.insertSpan(*span_buffer_, rect.fLeft, rect.fRight); - mark_dirty(i); - y1 = line.bottom; } + working_spans.push_back({start_x, end_x}); - if (y1 < y2) { - lines_.push_back(makeLine(y1, y2, rect.fLeft, rect.fRight)); - mark_dirty(lines_.size() - 1); + // end_y must not pass by the top of the next input rect + if (next_rect < count && end_y > rects[next_rect]->top()) { + end_y = rects[next_rect]->top(); } - // Check for duplicate lines and merge them. - if (dirty_start <= dirty_end) { - // Expand the region by one if possible. - if (dirty_start > 0) { - --dirty_start; - } - if (dirty_end + 1 < lines_.size()) { - ++dirty_end; - } - for (auto i = lines_.begin() + dirty_start; - i < lines_.begin() + dirty_end;) { - auto& line = *i; - auto& next = *(i + 1); - if (line.bottom == next.top && line.spansEqual(*span_buffer_, next)) { - --dirty_end; - next.top = line.top; - i = removeLine(i); - } else { - ++i; - } - } + // If all of the rules above work out, we should never collapse the + // current range of Y coordinates to empty + FML_DCHECK(end_y > cur_y); + + if (!lines_.empty() && lines_.back().bottom == cur_y && + lines_.back().spansEqual(*span_buffer_, working_spans)) { + lines_.back().bottom = end_y; + } else { +#ifdef DLREGION2_DO_STATS + span_count += working_spans.size(); + line_count++; +#endif + // lines_.emplace_back(cur_y, end_y, working_spans); + lines_.push_back(makeLine(cur_y, end_y, working_spans)); } - dirty_start = std::numeric_limits::max(); - dirty_end = 0; + cur_y = end_y; } + +#ifdef DLREGION2_DO_STATS + double span_avg = ((double)span_count) / line_count; + double active_avg = ((double)active_rect_count) / pass_count; + FML_LOG(ERROR) << lines_.size() << " lines for " << count + << " input rects, avg " << span_avg + << " spans per line and avg " << active_avg + << " active rects per loop"; +#endif } bool DlRegion::isComplex() const { diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index 838ee69100c1b..00fd1874b5303 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -25,7 +25,7 @@ class DlRegion { /// Creates region by bulk adding the rectangles. /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. - explicit DlRegion(std::vector&& rects); + explicit DlRegion(const std::vector& rects); /// Creates copy of the region. DlRegion(const DlRegion& r) : flutter::DlRegion(r, false) {} @@ -38,10 +38,6 @@ class DlRegion { ~DlRegion(); - /// Adds group of rectangles to the region. - /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. - void addRects(std::vector&& rects); - /// Adds another region to this region. /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. void addRegion(const DlRegion& region); @@ -64,6 +60,8 @@ class DlRegion { const SkIRect& bounds() const { return bounds_; } private: + void addRects(const std::vector& rects); + friend class SpanBuffer; struct Span { int32_t left; @@ -80,6 +78,7 @@ class DlRegion { const Span* begin, const Span* end); bool spansEqual(SpanBuffer& span_buffer, const SpanLine& l2) const; + bool spansEqual(SpanBuffer& span_buffer, const SpanVec& vec) const; }; typedef std::vector LineVec; @@ -98,6 +97,7 @@ class DlRegion { int32_t bottom, int32_t spanLeft, int32_t spanRight); + SpanLine makeLine(int32_t top, int32_t bottom, const SpanVec&); SpanLine duplicateLine(int32_t top, int32_t bottom, SpanChunkHandle handle); SpanLine duplicateLine(int32_t top, int32_t bottom, From 2a38c2008f68ef69ba9add46fac24d566ac9328a Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 8 Jun 2023 14:48:42 +0200 Subject: [PATCH 03/39] Small cleanups --- .../benchmarking/dl_region_benchmarks.cc | 55 +- display_list/geometry/dl_region.cc | 822 +++++++----------- display_list/geometry/dl_region.h | 128 +-- display_list/geometry/dl_region_unittests.cc | 31 +- display_list/geometry/dl_rtree.cc | 2 +- flow/rtree.cc | 2 +- 6 files changed, 401 insertions(+), 639 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index d59c7a881d55b..78bd3aef8228a 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -13,8 +13,6 @@ namespace { class SkRegionAdapter { public: - SkRegionAdapter() {} - explicit SkRegionAdapter(const std::vector& rects) { for (const auto& rect : rects) { region_.op(rect, SkRegion::kUnion_Op); @@ -23,8 +21,11 @@ class SkRegionAdapter { SkIRect getBounds() { return region_.getBounds(); } - void addRegion(const SkRegionAdapter& region) { - region_.op(region.region_, SkRegion::kUnion_Op); + static SkRegionAdapter unionRegions(const SkRegionAdapter& a1, + const SkRegionAdapter& a2) { + SkRegionAdapter result(a1); + result.region_.op(a2.region_, SkRegion::kUnion_Op); + return result; } bool intersects(const SkRegionAdapter& region) { @@ -49,13 +50,13 @@ class SkRegionAdapter { class DlRegionAdapter { public: - DlRegionAdapter() {} - explicit DlRegionAdapter(const std::vector& rects) : region_(rects) {} - void addRegion(const DlRegionAdapter& region) { - region_.addRegion(region.region_); + static DlRegionAdapter unionRegions(const DlRegionAdapter& a1, + const DlRegionAdapter& a2) { + return DlRegionAdapter( + flutter::DlRegion::MakeUnion(a1.region_, a2.region_)); } SkIRect getBounds() { return region_.bounds(); } @@ -68,9 +69,10 @@ class DlRegionAdapter { std::vector getRects() { return region_.getRects(false); } - DlRegionAdapter(const DlRegionAdapter& copy) : region_(copy.region_, true) {} - private: + explicit DlRegionAdapter(flutter::DlRegion&& region) + : region_(std::move(region)) {} + flutter::DlRegion region_; }; @@ -90,13 +92,13 @@ void RunAddRectsBenchmark(benchmark::State& state, int maxSize) { } while (state.KeepRunning()) { - Region region(std::move(rects)); + Region region(rects); auto vec2 = region.getRects(); } } template -void RunAddRegionBenchmark(benchmark::State& state, int maxSize) { +void RunUnionRegionBenchmark(benchmark::State& state, int maxSize) { std::random_device d; std::seed_seq seed{2, 1, 3}; std::mt19937 rng(seed); @@ -120,10 +122,7 @@ void RunAddRegionBenchmark(benchmark::State& state, int maxSize) { Region region2(rects); while (state.KeepRunning()) { - Region copy_of_region1(region1); - copy_of_region1.addRegion(region2); - // Region copy_of_region2(region2); - // copy_of_region2.addRegion(region1); + Region::unionRegions(region1, region2); } } @@ -198,12 +197,12 @@ static void BM_SkRegion_AddRects(benchmark::State& state, int maxSize) { RunAddRectsBenchmark(state, maxSize); } -static void BM_DlRegion_AddRegion(benchmark::State& state, int maxSize) { - RunAddRegionBenchmark(state, maxSize); +static void BM_DlRegion_MakeUnion(benchmark::State& state, int maxSize) { + RunUnionRegionBenchmark(state, maxSize); } -static void BM_SkRegion_AddRegion(benchmark::State& state, int maxSize) { - RunAddRegionBenchmark(state, maxSize); +static void BM_SkRegion_MakeUnion(benchmark::State& state, int maxSize) { + RunUnionRegionBenchmark(state, maxSize); } static void BM_DlRegion_IntersectsRegion(benchmark::State& state, int maxSize) { @@ -258,21 +257,21 @@ BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Large, 1500) BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Large, 1500) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_DlRegion_AddRegion, Tiny, 30) +BENCHMARK_CAPTURE(BM_DlRegion_MakeUnion, Tiny, 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_AddRegion, Tiny, 30) +BENCHMARK_CAPTURE(BM_SkRegion_MakeUnion, Tiny, 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_AddRegion, Small, 100) +BENCHMARK_CAPTURE(BM_DlRegion_MakeUnion, Small, 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_AddRegion, Small, 100) +BENCHMARK_CAPTURE(BM_SkRegion_MakeUnion, Small, 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_AddRegion, Medium, 400) +BENCHMARK_CAPTURE(BM_DlRegion_MakeUnion, Medium, 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_AddRegion, Medium, 400) +BENCHMARK_CAPTURE(BM_SkRegion_MakeUnion, Medium, 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_AddRegion, Large, 1500) +BENCHMARK_CAPTURE(BM_DlRegion_MakeUnion, Large, 1500) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_AddRegion, Large, 1500) +BENCHMARK_CAPTURE(BM_SkRegion_MakeUnion, Large, 1500) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Tiny, 30) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 409f481ed8642..38bac71ddbab2 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -8,473 +8,107 @@ namespace flutter { -class SpanBuffer { - public: - static const uint32_t kDefaultCapacity; - - SpanBuffer() { - free_handles_.reserve(256); - chunks_.reserve(256); - data_.reserve(2048); - } - - ~SpanBuffer() { - // fprintf(stderr, "Allocated size %zu, Handles: %zu, Free handles: %zu\n", - // data_.size(), chunks_.size(), free_handles_.size()); - } - - SpanChunkHandle allocateChunk(uint32_t capacity = kDefaultCapacity) { - for (auto it = free_handles_.rbegin(); it != free_handles_.rend(); ++it) { - SpanChunkInfo& info = chunks_[*it]; - if (info.capacity >= capacity) { - SpanChunkHandle handle = *it; - if (free_handles_.size() > 1) { - *it = free_handles_.back(); - free_handles_.pop_back(); - } else { - free_handles_.clear(); - } - ++info.ref_count; - info.size = 0; - return handle; - } - } - return allocateWithCapacity(capacity); - } - - SpanChunkHandle duplicateChunk(SpanChunkHandle handle) { - SpanChunkInfo& info = chunks_[handle]; - ++info.ref_count; - return handle; - } - - SpanChunkHandle copyChunk(DlRegion::Span* begin, DlRegion::Span* end) { - size_t size = end - begin; - SpanChunkHandle new_handle = allocateChunk(size * 2); - SpanChunkInfo& info = chunks_[new_handle]; - std::memcpy(&data_[info.offset], begin, size * sizeof(DlRegion::Span)); - info.size = size; - return new_handle; - } - - void freeChunk(SpanChunkHandle handle) { - SpanChunkInfo& info = chunks_[handle]; - FML_DCHECK(info.ref_count > 0); - --info.ref_count; - if (info.ref_count == 0) { - free_handles_.push_back(handle); - } - } - - size_t getChunkSize(SpanChunkHandle handle) { return chunks_[handle].size; } - - void updateChunkSize(SpanChunkHandle handle, size_t size) { - FML_DCHECK(chunks_[handle].ref_count == 1); - SpanChunkInfo& info = chunks_[handle]; - FML_DCHECK(size <= info.capacity); - info.size = size; - } - - void getSpans(SpanChunkHandle handle, - DlRegion::Span*& begin, - DlRegion::Span*& end) { - const SpanChunkInfo& info = chunks_[handle]; - begin = &data_[info.offset]; - end = begin + info.size; - } - - void eraseSpans(SpanChunkHandle handle, size_t begin, size_t end) { - FML_DCHECK(chunks_[handle].ref_count == 1); - SpanChunkInfo& info = chunks_[handle]; - - if (end == info.size) { - info.size = begin; - return; - } - - DlRegion::Span* span_begin = &data_[info.offset]; - DlRegion::Span* span_end = span_begin + info.size; - DlRegion::Span* erase_begin = span_begin + begin; - DlRegion::Span* erase_end = span_begin + end; - std::memmove(erase_begin, erase_end, - (span_end - erase_end) * sizeof(DlRegion::Span)); - info.size -= end - begin; - } - - void appendSpan(SpanChunkHandle handle, const DlRegion::Span& span) { - FML_DCHECK(chunks_[handle].ref_count == 1); - ensureSpace(handle); - SpanChunkInfo& info = chunks_[handle]; - DlRegion::Span* begin = &data_[info.offset]; - DlRegion::Span* end = begin + info.size; - *end = span; - ++info.size; - } - - void insertSpan(SpanChunkHandle handle, - size_t index, - const DlRegion::Span& span) { - FML_DCHECK(chunks_[handle].ref_count == 1); - ensureSpace(handle); - SpanChunkInfo& info = chunks_[handle]; - DlRegion::Span* begin = &data_[info.offset]; - DlRegion::Span* end = begin + info.size; - DlRegion::Span* insert = begin + index; - std::memmove(insert + 1, insert, (end - insert) * sizeof(DlRegion::Span)); - *insert = span; - ++info.size; - } - - SpanChunkHandle ensureChunkWritable(SpanChunkHandle handle) { - SpanChunkInfo info = chunks_[handle]; - FML_DCHECK(info.ref_count > 0); - if (info.ref_count == 1) { - return handle; - } else { - SpanChunkHandle new_handle = allocateChunk(info.capacity); - SpanChunkInfo& new_info = chunks_[new_handle]; - new_info.size = info.size; - std::memmove(&data_[new_info.offset], &data_[info.offset], - info.size * sizeof(DlRegion::Span)); - --chunks_[handle].ref_count; - return new_handle; - } - } - - private: - static size_t round_to_8(size_t x) { return ((x + 7) & (-8)); } - - SpanChunkHandle allocateWithCapacity(uint32_t capacity) { - capacity = round_to_8(std::max(capacity, kDefaultCapacity)); - chunks_.push_back({static_cast(data_.size()), 0, capacity, 1}); - data_.resize(data_.size() + capacity); - return chunks_.size() - 1; - } - - void ensureSpace(SpanChunkHandle handle) { - SpanChunkInfo& info = chunks_[handle]; - if (info.size == info.capacity) { - auto previous_info = info; - info.capacity *= 2; - auto prev_offset = info.offset; - info.offset = data_.size(); - data_.resize(data_.size() + info.capacity); - std::memmove(&data_[info.offset], &data_[prev_offset], - info.size * sizeof(DlRegion::Span)); - chunks_.push_back({ - previous_info.offset, - 0, - previous_info.capacity, - 0, - }); - free_handles_.push_back(chunks_.size() - 1); - } - } - - struct SpanChunkInfo { - /// Offset from the beginning of the buffer. - uint32_t offset; - uint32_t size; - uint32_t capacity; - int32_t ref_count; - }; - - std::vector data_; - std::vector chunks_; - std::vector free_handles_; +DlRegion::SpanBuffer::SpanBuffer(DlRegion::SpanBuffer&& m) + : capacity_(m.capacity_), size_(m.size_), spans_(m.spans_) { + m.size_ = 0; + m.capacity_ = 0; + m.spans_ = nullptr; }; -const uint32_t SpanBuffer::kDefaultCapacity = 16; - -DlRegion::DlRegion() : span_buffer_(std::make_shared()) {} - -DlRegion::DlRegion(const std::vector& rects) - : span_buffer_(std::make_shared()) { - // If SpanLines can not be memmoved `addRect` would be significantly slower - // due to cost of inserting and removing elements from the `lines_` vector. - static_assert(std::is_trivially_constructible::value, - "SpanLine must be trivially constructible."); - addRects(rects); +DlRegion::SpanBuffer::~SpanBuffer() { + free(spans_); } -DlRegion::~DlRegion() { - for (auto& line : lines_) { - span_buffer_->freeChunk(line.chunk_handle); - } -} - -DlRegion::DlRegion(const DlRegion& region, bool share_buffer) - : span_buffer_(share_buffer - ? region.span_buffer_ - : std::make_shared(*region.span_buffer_)) { - lines_ = region.lines_; - for (auto& line : lines_) { - line.chunk_handle = span_buffer_->duplicateChunk(line.chunk_handle); - } - bounds_ = region.bounds_; -} - -bool DlRegion::spansIntersect(const Span* begin1, - const Span* end1, - const Span* begin2, - const Span* end2) const { - while (begin1 != end1 && begin2 != end2) { - if (begin1->right <= begin2->left) { - ++begin1; - } else if (begin2->right <= begin1->left) { - ++begin2; - } else { - return true; - } - } - return false; -} - -bool DlRegion::intersects(const SkIRect& rect) const { - if (isEmpty()) { - return false; - } - - auto bounds_intersect = SkIRect::Intersects(bounds_, rect); - - if (!isComplex()) { - return bounds_intersect; - } - - if (!bounds_intersect) { - return false; - } - - auto it = std::upper_bound( - lines_.begin(), lines_.end(), rect.fTop, - [](int32_t i, const SpanLine& line) { return i < line.bottom; }); - - while (it != lines_.end() && it->top < rect.fBottom) { - FML_DCHECK(rect.fTop < it->bottom || it->top < rect.fBottom); - Span *begin, *end; - span_buffer_->getSpans(it->chunk_handle, begin, end); - while (begin != end && begin->left < rect.fRight) { - if (begin->right > rect.fLeft && begin->left < rect.fRight) { - return true; - } - ++begin; - } - ++it; +void DlRegion::SpanBuffer::reserve(size_t capacity) { + if (capacity_ < capacity) { + spans_ = static_cast(std::realloc(spans_, capacity * sizeof(Span))); + capacity_ = capacity; } - - return false; } -bool DlRegion::intersects(const DlRegion& region) const { - if (isEmpty() || region.isEmpty()) { - return false; +DlRegion::SpanChunkHandle DlRegion::SpanBuffer::storeChunk(const Span* begin, + const Span* end) { + size_t chunk_size = end - begin; + size_t min_capacity = size_ + chunk_size + 1; + if (capacity_ < min_capacity) { + size_t new_capacity = std::max(min_capacity, capacity_ * 2); + new_capacity = std::max(new_capacity, size_t(512)); + reserve(new_capacity); } + SpanChunkHandle res = size_; + size_ += chunk_size + 1; + spans_[res].left = chunk_size; - auto our_complex = isComplex(); - auto their_complex = region.isComplex(); - auto bounds_intersect = SkIRect::Intersects(bounds_, region.bounds_); - - if (!our_complex && !their_complex) { - return bounds_intersect; - } + auto* dst = spans_ + res + 1; + memmove(dst, begin, chunk_size * sizeof(Span)); - if (!bounds_intersect) { - return false; - } - - if (!our_complex) { - return region.intersects(bounds_); - } - - if (!their_complex) { - return intersects(region.bounds_); - } - - auto ours = lines_.begin(); - auto theirs = region.lines_.begin(); - - while (ours != lines_.end() && theirs != region.lines_.end()) { - if (ours->bottom <= theirs->top) { - ++ours; - } else if (theirs->bottom <= ours->top) { - ++theirs; - } else { - FML_DCHECK(ours->top < theirs->bottom || theirs->top < ours->bottom); - Span *ours_begin, *ours_end; - span_buffer_->getSpans(ours->chunk_handle, ours_begin, ours_end); - Span *theirs_begin, *theirs_end; - region.span_buffer_->getSpans(theirs->chunk_handle, theirs_begin, - theirs_end); - if (spansIntersect(ours_begin, ours_end, theirs_begin, theirs_end)) { - return true; - } - if (ours->bottom < theirs->bottom) { - ++ours; - } else { - ++theirs; - } - } - } - return false; -} - -std::vector DlRegion::getRects(bool deband) const { - std::vector rects; - size_t rect_count = 0; - size_t previous_span_end = 0; - for (const auto& line : lines_) { - rect_count += span_buffer_->getChunkSize(line.chunk_handle); - } - rects.reserve(rect_count); - - for (const auto& line : lines_) { - Span *span_begin, *span_end; - span_buffer_->getSpans(line.chunk_handle, span_begin, span_end); - for (const auto* span = span_begin; span < span_end; ++span) { - SkIRect rect{span->left, line.top, span->right, line.bottom}; - if (deband) { - auto iter = rects.begin() + previous_span_end; - // If there is rectangle previously in rects on which this one is a - // vertical continuation, remove the previous rectangle and expand - // this one vertically to cover the area. - while (iter != rects.begin()) { - --iter; - if (iter->bottom() < rect.top()) { - // Went all the way to previous span line. - break; - } else if (iter->left() == rect.left() && - iter->right() == rect.right()) { - FML_DCHECK(iter->bottom() == rect.top()); - rect.fTop = iter->fTop; - rects.erase(iter); - --previous_span_end; - break; - } - } - } - rects.push_back(rect); - } - previous_span_end = rects.size(); - } - return rects; + return res; } -void DlRegion::SpanLine::insertSpan(SpanBuffer& span_buffer, - int32_t left, - int32_t right) { - Span *span_begin, *span_end; - chunk_handle = span_buffer.ensureChunkWritable(chunk_handle); - span_buffer.getSpans(chunk_handle, span_begin, span_end); - size_t size = span_end - span_begin; - for (size_t i = 0; i < size; ++i) { - Span& span = span_begin[i]; - if (right < span.left) { - span_buffer.insertSpan(chunk_handle, i, {left, right}); - return; - } - if (left > span.right) { - continue; - } - size_t last_index = i; - while (last_index + 1 < size && right >= span_begin[last_index + 1].left) { - ++last_index; - } - span.left = std::min(span.left, left); - span.right = std::max(span_begin[last_index].right, right); - if (last_index > i) { - span_buffer.eraseSpans(chunk_handle, i + 1, last_index + 1); - } - return; - } - - span_buffer.appendSpan(chunk_handle, {left, right}); +size_t DlRegion::SpanBuffer::getChunkSize(SpanChunkHandle handle) const { + FML_DCHECK(handle < size_); + return spans_[handle].left; } -void DlRegion::SpanLine::insertSpans(SpanBuffer& buffer, - const Span* begin, - const Span* end) { - for (auto* span = begin; span != end; ++span) { - insertSpan(buffer, span->left, span->right); - } +void DlRegion::SpanBuffer::getSpans(SpanChunkHandle handle, + const DlRegion::Span*& begin, + const DlRegion::Span*& end) const { + FML_DCHECK(handle < size_); + auto& info = spans_[handle]; + begin = spans_ + handle + 1; + end = begin + info.left; } -bool DlRegion::SpanLine::spansEqual(SpanBuffer& buffer, - const DlRegion::SpanVec& vec) const { - Span *our_begin, *our_end; - buffer.getSpans(chunk_handle, our_begin, our_end); - size_t our_size = our_end - our_begin; - if (our_size != vec.size()) { - return false; - } - return memcmp(our_begin, vec.data(), our_size * sizeof(Span)) == 0; +DlRegion::DlRegion(const std::vector& rects) { + addRects(rects); } -bool DlRegion::SpanLine::spansEqual(SpanBuffer& buffer, - const SpanLine& l2) const { - FML_DCHECK(this != &l2); - - if (chunk_handle == l2.chunk_handle) { - return true; - } - - Span *our_begin, *our_end, *outher_begin, *other_end; - buffer.getSpans(chunk_handle, our_begin, our_end); - buffer.getSpans(l2.chunk_handle, outher_begin, other_end); +DlRegion::DlRegion() {} +bool DlRegion::spansEqual(SpanLine& line, + const Span* begin, + const Span* end) const { + const Span *our_begin, *our_end; + span_buffer_.getSpans(line.chunk_handle, our_begin, our_end); size_t our_size = our_end - our_begin; - size_t other_size = other_end - outher_begin; - - if (our_size != other_size) { + size_t their_size = end - begin; + if (our_size != their_size) { return false; } - return memcmp(our_begin, outher_begin, our_size * sizeof(Span)) == 0; -} -void DlRegion::insertLine(size_t position, SpanLine line) { - lines_.insert(lines_.begin() + position, line); -} - -DlRegion::LineVec::iterator DlRegion::removeLine( - DlRegion::LineVec::iterator line) { - span_buffer_->freeChunk(line->chunk_handle); - return lines_.erase(line); + return memcmp(our_begin, begin, our_size * sizeof(Span)) == 0; } DlRegion::SpanLine DlRegion::makeLine(int32_t top, int32_t bottom, - int32_t spanLeft, - int32_t spanRight) { - auto handle = span_buffer_->allocateChunk(); - span_buffer_->appendSpan(handle, {spanLeft, spanRight}); - return {top, bottom, handle}; + const SpanVec& v) { + return makeLine(top, bottom, v.data(), v.data() + v.size()); } DlRegion::SpanLine DlRegion::makeLine(int32_t top, int32_t bottom, - const DlRegion::SpanVec& v) { - auto handle = span_buffer_->allocateChunk(v.capacity()); - Span *begin, *end; - span_buffer_->getSpans(handle, begin, end); - memcpy(begin, v.data(), v.size() * sizeof(Span)); - span_buffer_->updateChunkSize(handle, v.size()); + const Span* begin, + const Span* end) { + auto handle = span_buffer_.storeChunk(begin, end); return {top, bottom, handle}; } -DlRegion::SpanLine DlRegion::mergeLines(int32_t top, - int32_t bottom, - SpanChunkHandle our_handle, - SpanBuffer* their_span_buffer, - SpanChunkHandle their_handle) { - Span *begin2, *end2; - their_span_buffer->getSpans(their_handle, begin2, end2); +size_t DlRegion::mergeLines(std::vector& res, + const SpanBuffer& a_buffer, + SpanChunkHandle a_handle, + const SpanBuffer& b_buffer, + SpanChunkHandle b_handle) { + const Span *begin1, *end1; + a_buffer.getSpans(a_handle, begin1, end1); - auto handle = span_buffer_->allocateChunk( - span_buffer_->getChunkSize(our_handle) + (end2 - begin2)); - Span *begin1, *end1; - span_buffer_->getSpans(our_handle, begin1, end1); + const Span *begin2, *end2; + b_buffer.getSpans(b_handle, begin2, end2); - Span *begin, *end; - span_buffer_->getSpans(handle, begin, end); + size_t min_size = (end1 - begin1) + (end2 - begin2); + if (res.size() < min_size) { + res.resize(min_size); + } + Span* end = res.data(); while (true) { if (begin1->right < begin2->left - 1) { @@ -562,30 +196,7 @@ DlRegion::SpanLine DlRegion::mergeLines(int32_t top, ++end; } - span_buffer_->updateChunkSize(handle, end - begin); - - return {top, bottom, handle}; -} - -DlRegion::SpanLine DlRegion::duplicateLine(int32_t top, - int32_t bottom, - SpanBuffer* span_buffer, - SpanChunkHandle handle) { - if (span_buffer == span_buffer_.get()) { - return {top, bottom, span_buffer_->duplicateChunk(handle)}; - } else { - SpanChunkHandle new_handle; - Span *span_begin, *span_end; - span_buffer->getSpans(handle, span_begin, span_end); - new_handle = span_buffer_->copyChunk(span_begin, span_end); - return {top, bottom, new_handle}; - } -} - -DlRegion::SpanLine DlRegion::duplicateLine(int32_t top, - int32_t bottom, - SpanChunkHandle handle) { - return {top, bottom, span_buffer_->duplicateChunk(handle)}; + return end - res.data(); } void DlRegion::addRects(const std::vector& unsorted_rects) { @@ -610,7 +221,7 @@ void DlRegion::addRects(const std::vector& unsorted_rects) { int32_t cur_y = std::numeric_limits::min(); SpanVec working_spans; -#ifdef DLREGION2_DO_STATS +#ifdef DlRegion_DO_STATS size_t active_rect_count = 0; size_t span_count = 0; int pass_count = 0; @@ -666,7 +277,7 @@ void DlRegion::addRects(const std::vector& unsorted_rects) { working_spans.clear(); FML_DCHECK(working_spans.empty()); -#ifdef DLREGION2_DO_STATS +#ifdef DlRegion_DO_STATS active_rect_count += active_end; pass_count++; #endif @@ -679,7 +290,7 @@ void DlRegion::addRects(const std::vector& unsorted_rects) { for (size_t i = 1; i < active_end; i++) { const SkIRect* r = rects[i]; if (r->left() > end_x) { - working_spans.push_back({start_x, end_x}); + working_spans.emplace_back(start_x, end_x); start_x = r->left(); end_x = r->right(); } else if (end_x < r->right()) { @@ -689,7 +300,7 @@ void DlRegion::addRects(const std::vector& unsorted_rects) { end_y = r->bottom(); } } - working_spans.push_back({start_x, end_x}); + working_spans.emplace_back(start_x, end_x); // end_y must not pass by the top of the next input rect if (next_rect < count && end_y > rects[next_rect]->top()) { @@ -701,20 +312,20 @@ void DlRegion::addRects(const std::vector& unsorted_rects) { FML_DCHECK(end_y > cur_y); if (!lines_.empty() && lines_.back().bottom == cur_y && - lines_.back().spansEqual(*span_buffer_, working_spans)) { + spansEqual(lines_.back(), working_spans.data(), + working_spans.data() + working_spans.size())) { lines_.back().bottom = end_y; } else { -#ifdef DLREGION2_DO_STATS +#ifdef DlRegion_DO_STATS span_count += working_spans.size(); line_count++; #endif - // lines_.emplace_back(cur_y, end_y, working_spans); lines_.push_back(makeLine(cur_y, end_y, working_spans)); } cur_y = end_y; } -#ifdef DLREGION2_DO_STATS +#ifdef DlRegion_DO_STATS double span_avg = ((double)span_count) / line_count; double active_avg = ((double)active_rect_count) / pass_count; FML_LOG(ERROR) << lines_.size() << " lines for " << count @@ -724,95 +335,254 @@ void DlRegion::addRects(const std::vector& unsorted_rects) { #endif } -bool DlRegion::isComplex() const { - return lines_.size() > 1 || - (lines_.size() == 1 && - span_buffer_->getChunkSize(lines_.front().chunk_handle) > 1); -} +DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { + DlRegion res; -void DlRegion::addRegion(const DlRegion& region) { - bounds_.join(region.bounds_); + res.span_buffer_.reserve(a.span_buffer_.capacity() + + b.span_buffer_.capacity()); + res.bounds_ = a.bounds_; + res.bounds_.join(b.bounds_); - LineVec res; - res.reserve(lines_.size() + region.lines_.size()); + auto& lines = res.lines_; + lines.reserve(a.lines_.size() + b.lines_.size()); - auto append_line = [&](SpanLine line) { - if (res.empty()) { - res.push_back(line); + auto append_spans = [&](int32_t top, int32_t bottom, const Span* begin, + const Span* end) { + if (lines.empty()) { + lines.push_back(res.makeLine(top, bottom, begin, end)); } else { - if (res.back().bottom == line.top && - res.back().spansEqual(*span_buffer_, line)) { - res.back().bottom = line.bottom; - span_buffer_->freeChunk(line.chunk_handle); + if (lines.back().bottom == top && + res.spansEqual(lines.back(), begin, end)) { + lines.back().bottom = bottom; } else { - res.push_back(line); + lines.push_back(res.makeLine(top, bottom, begin, end)); } } }; - LineVec::iterator ours = lines_.begin(); - auto theirs_copy = region.lines_; - LineVec::iterator theirs = theirs_copy.begin(); + auto append_line = [&](int32_t top, int32_t bottom, const SpanBuffer& buffer, + SpanChunkHandle chunk_handle) { + const Span *begin, *end; + buffer.getSpans(chunk_handle, begin, end); + append_spans(top, bottom, begin, end); + }; - auto their_span_buffer = region.span_buffer_.get(); + auto a_lines = a.lines_; + auto b_lines = b.lines_; - while (ours != lines_.end() && theirs != theirs_copy.end()) { - if (ours->bottom <= theirs->top) { - append_line(*ours); - ++ours; - } else if (theirs->bottom <= ours->top) { - append_line(duplicateLine(theirs->top, theirs->bottom, their_span_buffer, - theirs->chunk_handle)); - ++theirs; + auto a_it = a_lines.begin(); + auto b_it = b_lines.begin(); + + auto& a_buffer = a.span_buffer_; + auto& b_buffer = b.span_buffer_; + + std::vector tmp; + + while (a_it != a_lines.end() && b_it != b_lines.end()) { + if (a_it->bottom <= b_it->top) { + append_line(a_it->top, a_it->bottom, a_buffer, a_it->chunk_handle); + ++a_it; + } else if (b_it->bottom <= a_it->top) { + append_line(b_it->top, b_it->bottom, b_buffer, b_it->chunk_handle); + ++b_it; } else { - if (ours->top < theirs->top) { - append_line(duplicateLine(ours->top, theirs->top, ours->chunk_handle)); - ours->top = theirs->top; - if (ours->top == ours->bottom) { - span_buffer_->freeChunk(ours->chunk_handle); - ++ours; + if (a_it->top < b_it->top) { + append_line(a_it->top, b_it->top, a_buffer, a_it->chunk_handle); + a_it->top = b_it->top; + if (a_it->top == b_it->bottom) { + ++a_it; } - } else if (theirs->top < ours->top) { - append_line(duplicateLine(theirs->top, ours->top, their_span_buffer, - theirs->chunk_handle)); - theirs->top = ours->top; - if (theirs->top == theirs->bottom) { - ++theirs; + } else if (b_it->top < a_it->top) { + append_line(b_it->top, a_it->top, b_buffer, b_it->chunk_handle); + b_it->top = a_it->top; + if (b_it->top == a_it->bottom) { + ++b_it; } } else { - auto new_bottom = std::min(ours->bottom, theirs->bottom); - FML_DCHECK(ours->top == theirs->top); - FML_DCHECK(new_bottom > ours->top); - FML_DCHECK(new_bottom > theirs->top); - append_line(mergeLines(ours->top, new_bottom, ours->chunk_handle, - their_span_buffer, theirs->chunk_handle)); - ours->top = new_bottom; - if (ours->top == ours->bottom) { - span_buffer_->freeChunk(ours->chunk_handle); - ++ours; + auto new_bottom = std::min(a_it->bottom, b_it->bottom); + FML_DCHECK(a_it->top == b_it->top); + FML_DCHECK(new_bottom > a_it->top); + FML_DCHECK(new_bottom > b_it->top); + auto size = mergeLines(tmp, a_buffer, a_it->chunk_handle, b_buffer, + b_it->chunk_handle); + append_spans(a_it->top, new_bottom, tmp.data(), tmp.data() + size); + a_it->top = b_it->top = new_bottom; + if (a_it->top == a_it->bottom) { + ++a_it; } - theirs->top = new_bottom; - if (theirs->top == theirs->bottom) { - ++theirs; + if (b_it->top == b_it->bottom) { + ++b_it; } } } } - FML_DCHECK(ours == lines_.end() || theirs == theirs_copy.end()); + FML_DCHECK(a_it == a_lines.end() || b_it == b_lines.end()); + + while (a_it != a_lines.end()) { + append_line(a_it->top, a_it->bottom, a_buffer, a_it->chunk_handle); + ++a_it; + } + + while (b_it != b_lines.end()) { + append_line(b_it->top, b_it->bottom, b_buffer, b_it->chunk_handle); + ++b_it; + } + + return res; +} + +std::vector DlRegion::getRects(bool deband) const { + std::vector rects; + size_t rect_count = 0; + size_t previous_span_end = 0; + for (const auto& line : lines_) { + rect_count += span_buffer_.getChunkSize(line.chunk_handle); + } + rects.reserve(rect_count); + + for (const auto& line : lines_) { + const Span *span_begin, *span_end; + span_buffer_.getSpans(line.chunk_handle, span_begin, span_end); + for (const auto* span = span_begin; span < span_end; ++span) { + SkIRect rect{span->left, line.top, span->right, line.bottom}; + if (deband) { + auto iter = rects.begin() + previous_span_end; + // If there is rectangle previously in rects on which this one is a + // vertical continuation, remove the previous rectangle and expand + // this one vertically to cover the area. + while (iter != rects.begin()) { + --iter; + if (iter->bottom() < rect.top()) { + // Went all the way to previous span line. + break; + } else if (iter->left() == rect.left() && + iter->right() == rect.right()) { + FML_DCHECK(iter->bottom() == rect.top()); + rect.fTop = iter->fTop; + rects.erase(iter); + --previous_span_end; + break; + } + } + } + rects.push_back(rect); + } + previous_span_end = rects.size(); + } + return rects; +} + +DlRegion::~DlRegion() {} + +bool DlRegion::isComplex() const { + return lines_.size() > 1 || + (lines_.size() == 1 && + span_buffer_.getChunkSize(lines_.front().chunk_handle) > 1); +} + +bool DlRegion::intersects(const SkIRect& rect) const { + if (isEmpty()) { + return false; + } + + auto bounds_intersect = SkIRect::Intersects(bounds_, rect); + + if (!isComplex()) { + return bounds_intersect; + } + + if (!bounds_intersect) { + return false; + } + + auto it = std::upper_bound( + lines_.begin(), lines_.end(), rect.fTop, + [](int32_t i, const SpanLine& line) { return i < line.bottom; }); + + while (it != lines_.end() && it->top < rect.fBottom) { + FML_DCHECK(rect.fTop < it->bottom || it->top < rect.fBottom); + const Span *begin, *end; + span_buffer_.getSpans(it->chunk_handle, begin, end); + while (begin != end && begin->left < rect.fRight) { + if (begin->right > rect.fLeft && begin->left < rect.fRight) { + return true; + } + ++begin; + } + ++it; + } + + return false; +} - while (ours != lines_.end()) { - append_line(*ours); - ++ours; +bool DlRegion::spansIntersect(const Span* begin1, + const Span* end1, + const Span* begin2, + const Span* end2) { + while (begin1 != end1 && begin2 != end2) { + if (begin1->right <= begin2->left) { + ++begin1; + } else if (begin2->right <= begin1->left) { + ++begin2; + } else { + return true; + } } + return false; +} - while (theirs != theirs_copy.end()) { - append_line(duplicateLine(theirs->top, theirs->bottom, their_span_buffer, - theirs->chunk_handle)); - ++theirs; +bool DlRegion::intersects(const DlRegion& region) const { + if (isEmpty() || region.isEmpty()) { + return false; + } + + auto our_complex = isComplex(); + auto their_complex = region.isComplex(); + auto bounds_intersect = SkIRect::Intersects(bounds_, region.bounds_); + + if (!our_complex && !their_complex) { + return bounds_intersect; + } + + if (!bounds_intersect) { + return false; + } + + if (!our_complex) { + return region.intersects(bounds_); + } + + if (!their_complex) { + return intersects(region.bounds_); } - lines_ = std::move(res); + auto ours = lines_.begin(); + auto theirs = region.lines_.begin(); + + while (ours != lines_.end() && theirs != region.lines_.end()) { + if (ours->bottom <= theirs->top) { + ++ours; + } else if (theirs->bottom <= ours->top) { + ++theirs; + } else { + FML_DCHECK(ours->top < theirs->bottom || theirs->top < ours->bottom); + const Span *ours_begin, *ours_end; + span_buffer_.getSpans(ours->chunk_handle, ours_begin, ours_end); + const Span *theirs_begin, *theirs_end; + region.span_buffer_.getSpans(theirs->chunk_handle, theirs_begin, + theirs_end); + if (spansIntersect(ours_begin, ours_end, theirs_begin, theirs_end)) { + return true; + } + if (ours->bottom < theirs->bottom) { + ++ours; + } else { + ++theirs; + } + } + } + return false; } } // namespace flutter diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index 00fd1874b5303..d75834ce539b3 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -12,35 +12,20 @@ namespace flutter { -typedef std::uint32_t SpanChunkHandle; -class SpanBuffer; - /// Represents a region as a collection of non-overlapping rectangles. /// Implements a subset of SkRegion functionality optimized for quickly /// converting set of overlapping rectangles to non-overlapping rectangles. class DlRegion { public: - /// Creates empty region. - DlRegion(); - /// Creates region by bulk adding the rectangles. /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. explicit DlRegion(const std::vector& rects); - /// Creates copy of the region. - DlRegion(const DlRegion& r) : flutter::DlRegion(r, false) {} - - /// Creates copy of the region. - /// If |share_buffer| is true, the new region will share the same internal - /// span buffer as the original region. This means that both region must - /// be used on the same thread. - DlRegion(const DlRegion&, bool share_buffer); - - ~DlRegion(); + DlRegion(DlRegion&&) = default; - /// Adds another region to this region. - /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. - void addRegion(const DlRegion& region); + /// Creates union region of region a and be. + /// Matches SkRegion a; a.op(b, SkRegion::kUnion_Op) behavior. + static DlRegion MakeUnion(const DlRegion& a, const DlRegion& b); /// Returns list of non-overlapping rectangles that cover current region. /// If |deband| is false, each span line will result in separate rectangles, @@ -49,72 +34,93 @@ class DlRegion { /// merged into single rectange. std::vector getRects(bool deband = true) const; + /// Returns maximum and minimum axis values of rectangles in this region. + /// If region is empty returns SKIRect::MakeEmpty(). + const SkIRect& bounds() const { return bounds_; } + /// Returns whether this region intersects with a rectangle. bool intersects(const SkIRect& rect) const; /// Returns whether this region intersects with another region. bool intersects(const DlRegion& region) const; - /// Returns maximum and minimum axis values of rectangles in this region. - /// If region is empty returns SKIRect::MakeEmpty(). - const SkIRect& bounds() const { return bounds_; } + ~DlRegion(); private: - void addRects(const std::vector& rects); + typedef std::uint32_t SpanChunkHandle; - friend class SpanBuffer; struct Span { int32_t left; int32_t right; + + Span() = default; + Span(int32_t left, int32_t right) : left(left), right(right) {} }; - typedef std::vector SpanVec; - struct SpanLine { - int32_t top; - int32_t bottom; - SpanChunkHandle chunk_handle; - void insertSpan(SpanBuffer& span_buffer, int32_t left, int32_t right); - void insertSpans(SpanBuffer& span_buffer, - const Span* begin, - const Span* end); - bool spansEqual(SpanBuffer& span_buffer, const SpanLine& l2) const; - bool spansEqual(SpanBuffer& span_buffer, const SpanVec& vec) const; + /// Holds spands for the region. Having custom allocated memory that doesn't + /// do zero initialization every time the buffer gets resized improves + /// performance measurably. + class SpanBuffer { + public: + SpanBuffer() = default; + SpanBuffer(const SpanBuffer&) = delete; + SpanBuffer(SpanBuffer&& m); + + void reserve(size_t capacity); + size_t capacity() const { return capacity_; } + + SpanChunkHandle storeChunk(const Span* begin, const Span* end); + size_t getChunkSize(SpanChunkHandle handle) const; + void getSpans(SpanChunkHandle handle, + const DlRegion::Span*& begin, + const DlRegion::Span*& end) const; + + ~SpanBuffer(); + + private: + size_t capacity_ = 0; + size_t size_ = 0; + + // Spans for the region chunks. First span in each chunk contains the + // chunk size. + Span* spans_ = nullptr; }; - typedef std::vector LineVec; + DlRegion(); bool isEmpty() const { return lines_.empty(); } bool isComplex() const; - std::vector lines_; - - SkIRect bounds_ = SkIRect::MakeEmpty(); + struct SpanLine { + int32_t top; + int32_t bottom; + SpanChunkHandle chunk_handle; + }; - void insertLine(size_t position, SpanLine line); - LineVec::iterator removeLine(LineVec::iterator position); + void addRects(const std::vector& rects); + typedef std::vector SpanVec; + SpanLine makeLine(int32_t top, int32_t bottom, const SpanVec&); SpanLine makeLine(int32_t top, int32_t bottom, - int32_t spanLeft, - int32_t spanRight); - SpanLine makeLine(int32_t top, int32_t bottom, const SpanVec&); - SpanLine duplicateLine(int32_t top, int32_t bottom, SpanChunkHandle handle); - SpanLine duplicateLine(int32_t top, - int32_t bottom, - SpanBuffer* span_buffer, - SpanChunkHandle handle); - SpanLine mergeLines(int32_t top, - int32_t bottom, - SpanChunkHandle our_handle, - SpanBuffer* their_span_buffer, - SpanChunkHandle their_handle); - - bool spansIntersect(const Span* begin1, - const Span* end1, - const Span* begin2, - const Span* end2) const; - - std::shared_ptr span_buffer_; + const Span* begin, + const Span* end); + static size_t mergeLines(std::vector& res, + const SpanBuffer& a_buffer, + SpanChunkHandle a_handle, + const SpanBuffer& b_buffer, + SpanChunkHandle b_handle); + + bool spansEqual(SpanLine& line, const Span* begin, const Span* end) const; + + static bool spansIntersect(const Span* begin1, + const Span* end1, + const Span* begin2, + const Span* end2); + + std::vector lines_; + SkIRect bounds_ = SkIRect::MakeEmpty(); + SpanBuffer span_buffer_; }; } // namespace flutter diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index f4f03389d31ab..8c0f22a1ea1ad 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -13,7 +13,7 @@ namespace flutter { namespace testing { TEST(DisplayListRegion, EmptyRegion) { - DlRegion region; + DlRegion region(std::vector{}); EXPECT_TRUE(region.getRects().empty()); } @@ -30,7 +30,7 @@ TEST(DisplayListRegion, NonOverlappingRectangles1) { SkIRect rect = SkIRect::MakeXYWH(50 * i, 50 * i, 50, 50); rects_in.push_back(rect); } - DlRegion region(std::move(rects_in)); + DlRegion region(rects_in); auto rects = region.getRects(); std::vector expected{ {0, 0, 50, 50}, {50, 50, 100, 100}, {100, 100, 150, 150}, @@ -111,7 +111,7 @@ TEST(DisplayListRegion, OverlappingRectangles) { SkIRect rect = SkIRect::MakeXYWH(10 * i, 10 * i, 50, 50); rects_in.push_back(rect); } - DlRegion region(std::move(rects_in)); + DlRegion region(rects_in); auto rects = region.getRects(); std::vector expected{ {0, 0, 50, 10}, {0, 10, 60, 20}, {0, 20, 70, 30}, @@ -164,19 +164,6 @@ void CheckEquality(const DlRegion& dl_region, const SkRegion& sk_region) { iterator.next(); } - if (rects != skia_rects) { - fprintf(stderr, "----- %zu %zu\n", rects.size(), skia_rects.size()); - for (size_t i = 0; i < std::min(rects.size(), skia_rects.size()); ++i) { - if (rects[i] != skia_rects[i]) { - fprintf(stderr, "A %d %d %d %d\n", rects[i].fLeft, rects[i].fTop, - rects[i].fRight, rects[i].fBottom); - fprintf(stderr, "B %d %d %d %d\n", skia_rects[i].fLeft, - skia_rects[i].fTop, skia_rects[i].fRight, - skia_rects[i].fBottom); - } - } - } - EXPECT_EQ(rects, skia_rects); } @@ -201,7 +188,6 @@ TEST(DisplayListRegion, TestAgainstSkRegion) { for (const auto& settings : all_settings) { std::random_device d; std::seed_seq seed{::testing::UnitTest::GetInstance()->random_seed()}; - // std::seed_seq seed{133}; std::mt19937 rng(seed); SkRegion sk_region1; @@ -224,10 +210,10 @@ TEST(DisplayListRegion, TestAgainstSkRegion) { sk_region2.op(rect, SkRegion::kUnion_Op); } - DlRegion region1(std::move(rects_in1)); + DlRegion region1(rects_in1); CheckEquality(region1, sk_region1); - DlRegion region2(std::move(rects_in2)); + DlRegion region2(rects_in2); CheckEquality(region2, sk_region2); auto intersects_1 = region1.intersects(region2); @@ -243,10 +229,11 @@ TEST(DisplayListRegion, TestAgainstSkRegion) { } } - region1.addRegion(region2); - sk_region1.op(sk_region2, SkRegion::kUnion_Op); + DlRegion dl_union = DlRegion::MakeUnion(region1, region2); + SkRegion sk_union(sk_region1); + sk_union.op(sk_region2, SkRegion::kUnion_Op); - CheckEquality(region1, sk_region1); + CheckEquality(dl_union, sk_union); } } diff --git a/display_list/geometry/dl_rtree.cc b/display_list/geometry/dl_rtree.cc index d5fb8ad64d1ff..14cff7e321e66 100644 --- a/display_list/geometry/dl_rtree.cc +++ b/display_list/geometry/dl_rtree.cc @@ -173,7 +173,7 @@ std::list DlRTree::searchAndConsolidateRects(const SkRect& query, bounds(index).roundOut(¤t_record_rect); rects.push_back(current_record_rect); } - DlRegion region(std::move(rects)); + DlRegion region(rects); auto non_overlapping_rects = region.getRects(deband); std::list final_results; diff --git a/flow/rtree.cc b/flow/rtree.cc index 5e2630f867960..ff66d52c1762c 100644 --- a/flow/rtree.cc +++ b/flow/rtree.cc @@ -54,7 +54,7 @@ std::list RTree::searchNonOverlappingDrawnRects( rects.push_back(current_record_rect); } - DlRegion region(std::move(rects)); + DlRegion region(rects); auto non_overlapping_rects = region.getRects(true); std::list final_results; for (const auto& rect : non_overlapping_rects) { From adc9a4a17382fbdf60c333906c3a44de0df22241 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 9 Jun 2023 17:21:58 +0200 Subject: [PATCH 04/39] Address feedback --- .../benchmarking/dl_region_benchmarks.cc | 2 - display_list/geometry/dl_region.cc | 87 +++++++++---------- display_list/geometry/dl_region.h | 2 +- 3 files changed, 40 insertions(+), 51 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index 78bd3aef8228a..90babe119c7d7 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -140,7 +140,6 @@ void RunIntersectsRegionBenchmark(benchmark::State& state, int maxSize) { SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); rects.push_back(rect); } - Region region1(rects); rects.clear(); @@ -169,7 +168,6 @@ void RunIntersectsSingleRectBenchmark(benchmark::State& state, int maxSize) { SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); rects.push_back(rect); } - Region region1(rects); rects.clear(); diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 38bac71ddbab2..6ab88ae1de0ea 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -108,20 +108,18 @@ size_t DlRegion::mergeLines(std::vector& res, if (res.size() < min_size) { res.resize(min_size); } - Span* end = res.data(); + + // Pointer to the next span to be written. + Span* new_span = res.data(); while (true) { - if (begin1->right < begin2->left - 1) { - *end = *begin1; - ++begin1; - ++end; + if (begin1->right < begin2->left) { + *new_span++ = *begin1++; if (begin1 == end1) { break; } } else if (begin2->right < begin1->left) { - *end = *begin2; - ++begin2; - ++end; + *new_span++ = *begin2++; if (begin2 == end2) { break; } @@ -130,73 +128,66 @@ size_t DlRegion::mergeLines(std::vector& res, } } - Span currentSpan{0, 0}; + Span current_span{0, 0}; while (begin1 != end1 && begin2 != end2) { - if (currentSpan.left == currentSpan.right) { - if (begin1->right < begin2->left - 1) { - *end = *begin1; - ++begin1; - ++end; + if (current_span.left == current_span.right) { + if (begin1->right < begin2->left) { + *new_span++ = *begin1++; } else if (begin2->right < begin1->left) { - *end = *begin2; - ++begin2; - ++end; - } else if (begin1->left == begin2->left) { - currentSpan.left = begin1->left; - currentSpan.right = std::max(begin1->right, begin2->right); - ++begin1; - ++begin2; + *new_span++ = *begin2++; } else if (begin1->left < begin2->left) { - currentSpan.left = begin1->left; - currentSpan.right = begin1->right; + current_span = *begin1; ++begin1; + } else if (begin2->left < begin1->left) { + current_span = *begin2; + ++begin2; } else { - currentSpan.left = begin2->left; - currentSpan.right = begin2->right; + FML_DCHECK(begin1->left == begin2->left); + current_span.left = begin1->left; + current_span.right = std::max(begin1->right, begin2->right); + ++begin1; ++begin2; } - } else if (currentSpan.right >= begin1->left) { - currentSpan.right = std::max(currentSpan.right, begin1->right); + } else if (current_span.right >= begin1->left) { + current_span.right = std::max(current_span.right, begin1->right); ++begin1; - } else if (currentSpan.right >= begin2->left) { - currentSpan.right = std::max(currentSpan.right, begin2->right); + } else if (current_span.right >= begin2->left) { + current_span.right = std::max(current_span.right, begin2->right); ++begin2; } else { - *end = currentSpan; - ++end; - currentSpan.left = currentSpan.right = 0; + *new_span = current_span; + ++new_span; + current_span.left = current_span.right = 0; } } - if (currentSpan.left != currentSpan.right) { - while (begin1 != end1 && currentSpan.right >= begin1->left) { - currentSpan.right = std::max(currentSpan.right, begin1->right); + if (current_span.left != current_span.right) { + while (begin1 != end1 && current_span.right >= begin1->left) { + current_span.right = std::max(current_span.right, begin1->right); ++begin1; } - while (begin2 != end2 && currentSpan.right >= begin2->left) { - currentSpan.right = std::max(currentSpan.right, begin2->right); + while (begin2 != end2 && current_span.right >= begin2->left) { + current_span.right = std::max(current_span.right, begin2->right); ++begin2; } - *end = currentSpan; - ++end; + *new_span = current_span; + ++new_span; } FML_DCHECK(begin1 == end1 || begin2 == end2); + // At most one of these loops will execute while (begin1 != end1) { - *end = *begin1; - ++begin1; - ++end; + *new_span++ = *begin1++; } - while (begin2 != end2) { - *end = *begin2; - ++begin2; - ++end; + *new_span++ = *begin2++; } - return end - res.data(); + FML_DCHECK(begin1 == end1 && begin2 == end2); + + return new_span - res.data(); } void DlRegion::addRects(const std::vector& unsorted_rects) { diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index d75834ce539b3..383a98102b0c1 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -57,7 +57,7 @@ class DlRegion { Span(int32_t left, int32_t right) : left(left), right(right) {} }; - /// Holds spands for the region. Having custom allocated memory that doesn't + /// Holds spans for the region. Having custom allocated memory that doesn't /// do zero initialization every time the buffer gets resized improves /// performance measurably. class SpanBuffer { From c92fc2feee400c67849de99cd5243c69d1abb562 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 9 Jun 2023 17:23:00 +0200 Subject: [PATCH 05/39] Rename --- display_list/geometry/dl_region.cc | 14 +++++++------- display_list/geometry/dl_region.h | 10 +++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 6ab88ae1de0ea..de4e3cc1019c0 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -93,11 +93,11 @@ DlRegion::SpanLine DlRegion::makeLine(int32_t top, return {top, bottom, handle}; } -size_t DlRegion::mergeLines(std::vector& res, - const SpanBuffer& a_buffer, - SpanChunkHandle a_handle, - const SpanBuffer& b_buffer, - SpanChunkHandle b_handle) { +size_t DlRegion::unionLineSpans(std::vector& res, + const SpanBuffer& a_buffer, + SpanChunkHandle a_handle, + const SpanBuffer& b_buffer, + SpanChunkHandle b_handle) { const Span *begin1, *end1; a_buffer.getSpans(a_handle, begin1, end1); @@ -394,8 +394,8 @@ DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { FML_DCHECK(a_it->top == b_it->top); FML_DCHECK(new_bottom > a_it->top); FML_DCHECK(new_bottom > b_it->top); - auto size = mergeLines(tmp, a_buffer, a_it->chunk_handle, b_buffer, - b_it->chunk_handle); + auto size = unionLineSpans(tmp, a_buffer, a_it->chunk_handle, b_buffer, + b_it->chunk_handle); append_spans(a_it->top, new_bottom, tmp.data(), tmp.data() + size); a_it->top = b_it->top = new_bottom; if (a_it->top == a_it->bottom) { diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index 383a98102b0c1..8f0ca0f5f3744 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -105,11 +105,11 @@ class DlRegion { int32_t bottom, const Span* begin, const Span* end); - static size_t mergeLines(std::vector& res, - const SpanBuffer& a_buffer, - SpanChunkHandle a_handle, - const SpanBuffer& b_buffer, - SpanChunkHandle b_handle); + static size_t unionLineSpans(std::vector& res, + const SpanBuffer& a_buffer, + SpanChunkHandle a_handle, + const SpanBuffer& b_buffer, + SpanChunkHandle b_handle); bool spansEqual(SpanLine& line, const Span* begin, const Span* end) const; From a9f8f4cc67a0abe60db401fe439cc345eb9e04dd Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 9 Jun 2023 17:31:22 +0200 Subject: [PATCH 06/39] Move the DCHECK --- display_list/geometry/dl_region.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index de4e3cc1019c0..59f5802b318f9 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -161,6 +161,8 @@ size_t DlRegion::unionLineSpans(std::vector& res, } } + FML_DCHECK(begin1 == end1 || begin2 == end2); + if (current_span.left != current_span.right) { while (begin1 != end1 && current_span.right >= begin1->left) { current_span.right = std::max(current_span.right, begin1->right); @@ -175,8 +177,6 @@ size_t DlRegion::unionLineSpans(std::vector& res, ++new_span; } - FML_DCHECK(begin1 == end1 || begin2 == end2); - // At most one of these loops will execute while (begin1 != end1) { *new_span++ = *begin1++; From a7f45a8082f0ae002da88c0df6fc6c92273fe6c0 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 9 Jun 2023 22:52:52 +0200 Subject: [PATCH 07/39] Add more tests --- display_list/geometry/dl_region_unittests.cc | 141 +++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index 8c0f22a1ea1ad..272013f1d5127 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -150,6 +150,147 @@ TEST(DisplayListRegion, Deband) { EXPECT_EQ(rects_without_deband, expected_without_deband); } +TEST(DisplayListRegion, Intersects1) { + DlRegion region1({ + SkIRect::MakeXYWH(0, 0, 20, 20), + SkIRect::MakeXYWH(20, 20, 20, 20), + }); + DlRegion region2({ + SkIRect::MakeXYWH(20, 0, 20, 20), + SkIRect::MakeXYWH(0, 20, 20, 20), + }); + EXPECT_FALSE(region1.intersects(region2)); + EXPECT_FALSE(region2.intersects(region1)); + + EXPECT_TRUE(region1.intersects(region2.bounds())); + EXPECT_TRUE(region2.intersects(region1.bounds())); + + EXPECT_TRUE(region1.intersects(SkIRect::MakeXYWH(0, 0, 20, 20))); + EXPECT_FALSE(region1.intersects(SkIRect::MakeXYWH(20, 0, 20, 20))); + + EXPECT_TRUE(region1.intersects( + DlRegion(std::vector{SkIRect::MakeXYWH(0, 0, 20, 20)}))); + EXPECT_FALSE(region1.intersects( + DlRegion(std::vector{SkIRect::MakeXYWH(20, 0, 20, 20)}))); + + EXPECT_FALSE(region1.intersects(SkIRect::MakeXYWH(-1, -1, 1, 1))); + EXPECT_TRUE(region1.intersects(SkIRect::MakeXYWH(0, 0, 1, 1))); + + EXPECT_FALSE(region1.intersects(SkIRect::MakeXYWH(40, 40, 1, 1))); + EXPECT_TRUE(region1.intersects(SkIRect::MakeXYWH(39, 39, 1, 1))); +} + +TEST(DisplayListRegion, Intersects2) { + DlRegion region1({ + SkIRect::MakeXYWH(-10, -10, 20, 20), + SkIRect::MakeXYWH(-30, -30, 20, 20), + }); + DlRegion region2({ + SkIRect::MakeXYWH(20, 20, 5, 5), + SkIRect::MakeXYWH(0, 0, 20, 20), + }); + EXPECT_TRUE(region1.intersects(region2)); + EXPECT_TRUE(region2.intersects(region1)); +} + +TEST(DisplayListRegion, Union1) { + DlRegion region1({ + SkIRect::MakeXYWH(0, 0, 20, 20), + SkIRect::MakeXYWH(20, 20, 20, 20), + }); + DlRegion region2({ + SkIRect::MakeXYWH(20, 0, 20, 20), + SkIRect::MakeXYWH(0, 20, 20, 20), + }); + DlRegion u = DlRegion::MakeUnion(region1, region2); + EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 40, 40)); + auto rects = u.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(0, 0, 40, 40), // + }; + EXPECT_EQ(rects, expected); +} + +TEST(DisplayListRegion, Union2) { + DlRegion region1({ + SkIRect::MakeXYWH(0, 0, 20, 20), + SkIRect::MakeXYWH(21, 21, 20, 20), + }); + DlRegion region2({ + SkIRect::MakeXYWH(21, 0, 20, 20), + SkIRect::MakeXYWH(0, 21, 20, 20), + }); + DlRegion u = DlRegion::MakeUnion(region1, region2); + EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 41, 41)); + auto rects = u.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(0, 0, 20, 20), + SkIRect::MakeXYWH(21, 0, 20, 20), + SkIRect::MakeXYWH(0, 21, 20, 20), + SkIRect::MakeXYWH(21, 21, 20, 20), + }; + EXPECT_EQ(rects, expected); +} + +TEST(DisplayListRegion, Union3) { + DlRegion region1({ + SkIRect::MakeXYWH(-10, -10, 20, 20), + }); + DlRegion region2({ + SkIRect::MakeXYWH(0, 0, 20, 20), + }); + DlRegion u = DlRegion::MakeUnion(region1, region2); + EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(-10, -10, 30, 30)); + auto rects = u.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(-10, -10, 20, 10), + SkIRect::MakeXYWH(-10, 0, 30, 10), + SkIRect::MakeXYWH(0, 10, 20, 10), + }; + EXPECT_EQ(rects, expected); + // for (auto& rect : rects) { + // printf("SkIRect::MakeXYWH(%d, %d, %d, %d,),\n", rect.fLeft, rect.fTop, + // rect.width(), + // rect.height()); + // } +} + +TEST(DisplayListRegion, UnionEmpty) { + { + DlRegion region1(std::vector{}); + DlRegion region2(std::vector{}); + DlRegion u = DlRegion::MakeUnion(region1, region2); + EXPECT_EQ(u.bounds(), SkIRect::MakeEmpty()); + auto rects = u.getRects(); + std::vector expected{}; + EXPECT_EQ(rects, expected); + } + { + DlRegion region1(std::vector{}); + DlRegion region2({ + SkIRect::MakeXYWH(0, 0, 20, 20), + }); + DlRegion u = DlRegion::MakeUnion(region1, region2); + EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 20, 20)); + auto rects = u.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(0, 0, 20, 20), + }; + } + { + DlRegion region1({ + SkIRect::MakeXYWH(0, 0, 20, 20), + }); + DlRegion region2(std::vector{}); + DlRegion u = DlRegion::MakeUnion(region1, region2); + EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 20, 20)); + auto rects = u.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(0, 0, 20, 20), + }; + } +} + void CheckEquality(const DlRegion& dl_region, const SkRegion& sk_region) { EXPECT_EQ(dl_region.bounds(), sk_region.getBounds()); From ff16ef0a0be7d615b5971788fa40f9443c4282c9 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 9 Jun 2023 22:59:59 +0200 Subject: [PATCH 08/39] Split AddRect/GetRects benchmarks --- .../benchmarking/dl_region_benchmarks.cc | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index 90babe119c7d7..0d999de6ed653 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -93,6 +93,27 @@ void RunAddRectsBenchmark(benchmark::State& state, int maxSize) { while (state.KeepRunning()) { Region region(rects); + } +} + +template +void RunGetRectsBenchmark(benchmark::State& state, int maxSize) { + std::random_device d; + std::seed_seq seed{2, 1, 3}; + std::mt19937 rng(seed); + + std::uniform_int_distribution pos(0, 4000); + std::uniform_int_distribution size(1, maxSize); + + std::vector rects; + for (int i = 0; i < 2000; ++i) { + SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects.push_back(rect); + } + + Region region(rects); + + while (state.KeepRunning()) { auto vec2 = region.getRects(); } } @@ -195,6 +216,14 @@ static void BM_SkRegion_AddRects(benchmark::State& state, int maxSize) { RunAddRectsBenchmark(state, maxSize); } +static void BM_DlRegion_GetRects(benchmark::State& state, int maxSize) { + RunGetRectsBenchmark(state, maxSize); +} + +static void BM_SkRegion_GetRects(benchmark::State& state, int maxSize) { + RunGetRectsBenchmark(state, maxSize); +} + static void BM_DlRegion_MakeUnion(benchmark::State& state, int maxSize) { RunUnionRegionBenchmark(state, maxSize); } @@ -289,4 +318,21 @@ BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Large, 1500) BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Large, 1500) ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_GetRects, Tiny, 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_GetRects, Tiny, 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_GetRects, Small, 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_GetRects, Small, 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_GetRects, Medium, 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_GetRects, Medium, 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_GetRects, Large, 1500) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_GetRects, Large, 1500) + ->Unit(benchmark::kMicrosecond); + } // namespace flutter \ No newline at end of file From 3d1f592dec3b303869c6d0abc682834ae354cd3f Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 10 Jun 2023 10:31:29 +0200 Subject: [PATCH 09/39] Add DlRegion::MakeIntersection --- .../benchmarking/dl_region_benchmarks.cc | 99 ++++++++++-- display_list/geometry/dl_region.cc | 151 ++++++++++++++---- display_list/geometry/dl_region.h | 22 +++ display_list/geometry/dl_region_unittests.cc | 6 +- 4 files changed, 231 insertions(+), 47 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index 0d999de6ed653..a61241da9ebdc 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -28,6 +28,13 @@ class SkRegionAdapter { return result; } + static SkRegionAdapter intersectRegions(const SkRegionAdapter& a1, + const SkRegionAdapter& a2) { + SkRegionAdapter result(a1); + result.region_.op(a2.region_, SkRegion::kIntersect_Op); + return result; + } + bool intersects(const SkRegionAdapter& region) { return region_.intersects(region.region_); } @@ -59,6 +66,12 @@ class DlRegionAdapter { flutter::DlRegion::MakeUnion(a1.region_, a2.region_)); } + static DlRegionAdapter intersectRegions(const DlRegionAdapter& a1, + const DlRegionAdapter& a2) { + return DlRegionAdapter( + flutter::DlRegion::MakeIntersection(a1.region_, a2.region_)); + } + SkIRect getBounds() { return region_.bounds(); } bool intersects(const DlRegionAdapter& region) { @@ -118,8 +131,10 @@ void RunGetRectsBenchmark(benchmark::State& state, int maxSize) { } } +enum RegionOp { kUnion, kIntersection }; + template -void RunUnionRegionBenchmark(benchmark::State& state, int maxSize) { +void RunRegionOpBenchmark(benchmark::State& state, RegionOp op, int maxSize) { std::random_device d; std::seed_seq seed{2, 1, 3}; std::mt19937 rng(seed); @@ -142,8 +157,17 @@ void RunUnionRegionBenchmark(benchmark::State& state, int maxSize) { } Region region2(rects); - while (state.KeepRunning()) { - Region::unionRegions(region1, region2); + switch (op) { + case kUnion: + while (state.KeepRunning()) { + Region::unionRegions(region1, region2); + } + break; + case kIntersection: + while (state.KeepRunning()) { + Region::intersectRegions(region1, region2); + } + break; } } @@ -224,12 +248,16 @@ static void BM_SkRegion_GetRects(benchmark::State& state, int maxSize) { RunGetRectsBenchmark(state, maxSize); } -static void BM_DlRegion_MakeUnion(benchmark::State& state, int maxSize) { - RunUnionRegionBenchmark(state, maxSize); +static void BM_DlRegion_Operation(benchmark::State& state, + RegionOp op, + int maxSize) { + RunRegionOpBenchmark(state, op, maxSize); } -static void BM_SkRegion_MakeUnion(benchmark::State& state, int maxSize) { - RunUnionRegionBenchmark(state, maxSize); +static void BM_SkRegion_Operation(benchmark::State& state, + RegionOp op, + int maxSize) { + RunRegionOpBenchmark(state, op, maxSize); } static void BM_DlRegion_IntersectsRegion(benchmark::State& state, int maxSize) { @@ -284,21 +312,62 @@ BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Large, 1500) BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Large, 1500) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_DlRegion_MakeUnion, Tiny, 30) +BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Tiny, RegionOp::kUnion, 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Tiny, RegionOp::kUnion, 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Small, RegionOp::kUnion, 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Small, RegionOp::kUnion, 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Medium, RegionOp::kUnion, 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Medium, RegionOp::kUnion, 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Large, RegionOp::kUnion, 1500) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Large, RegionOp::kUnion, 1500) + ->Unit(benchmark::kMicrosecond); + +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_Tiny, + RegionOp::kIntersection, + 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_MakeUnion, Tiny, 30) +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_Tiny, + RegionOp::kIntersection, + 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_MakeUnion, Small, 100) +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_Small, + RegionOp::kIntersection, + 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_MakeUnion, Small, 100) +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_Small, + RegionOp::kIntersection, + 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_MakeUnion, Medium, 400) +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_Medium, + RegionOp::kIntersection, + 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_MakeUnion, Medium, 400) +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_Medium, + RegionOp::kIntersection, + 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_MakeUnion, Large, 1500) +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_Large, + RegionOp::kIntersection, + 1500) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_MakeUnion, Large, 1500) +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_Large, + RegionOp::kIntersection, + 1500) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Tiny, 30) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 59f5802b318f9..d515c0c4213eb 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -190,6 +190,51 @@ size_t DlRegion::unionLineSpans(std::vector& res, return new_span - res.data(); } +size_t DlRegion::intersectLineSpans(std::vector& res, + const SpanBuffer& a_buffer, + SpanChunkHandle a_handle, + const SpanBuffer& b_buffer, + SpanChunkHandle b_handle) { + const Span *begin1, *end1; + a_buffer.getSpans(a_handle, begin1, end1); + + const Span *begin2, *end2; + b_buffer.getSpans(b_handle, begin2, end2); + + // Worst case scenario, interleaved overlapping spans + // AAAA BBBB CCCC + // XXX YYYY XXXX + size_t min_size = (end1 - begin1) + (end2 - begin2) - 1; + if (res.size() < min_size) { + res.resize(min_size); + } + + // Pointer to the next span to be written. + Span* new_span = res.data(); + + while (begin1 != end1 && begin2 != end2) { + if (begin1->right <= begin2->left) { + ++begin1; + } else if (begin2->right <= begin1->left) { + ++begin2; + } else { + int32_t left = std::max(begin1->left, begin2->left); + int32_t right = std::min(begin1->right, begin2->right); + FML_DCHECK(left < right); + FML_DCHECK(new_span < res.data() + res.size()); + *new_span++ = {left, right}; + if (begin1->right == right) { + ++begin1; + } + if (begin2->right == right) { + ++begin2; + } + } + } + + return new_span - res.data(); +} + void DlRegion::addRects(const std::vector& unsorted_rects) { size_t count = unsorted_rects.size(); std::vector rects(count); @@ -326,41 +371,35 @@ void DlRegion::addRects(const std::vector& unsorted_rects) { #endif } +void DlRegion::appendLine(int32_t top, + int32_t bottom, + const Span* begin, + const Span* end) { + if (lines_.empty()) { + lines_.push_back(makeLine(top, bottom, begin, end)); + } else { + if (lines_.back().bottom == top && spansEqual(lines_.back(), begin, end)) { + lines_.back().bottom = bottom; + } else { + lines_.push_back(makeLine(top, bottom, begin, end)); + } + } +} + DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { DlRegion res; - res.span_buffer_.reserve(a.span_buffer_.capacity() + - b.span_buffer_.capacity()); res.bounds_ = a.bounds_; res.bounds_.join(b.bounds_); + res.span_buffer_.reserve(a.span_buffer_.capacity() + + b.span_buffer_.capacity()); + auto& lines = res.lines_; lines.reserve(a.lines_.size() + b.lines_.size()); - auto append_spans = [&](int32_t top, int32_t bottom, const Span* begin, - const Span* end) { - if (lines.empty()) { - lines.push_back(res.makeLine(top, bottom, begin, end)); - } else { - if (lines.back().bottom == top && - res.spansEqual(lines.back(), begin, end)) { - lines.back().bottom = bottom; - } else { - lines.push_back(res.makeLine(top, bottom, begin, end)); - } - } - }; - - auto append_line = [&](int32_t top, int32_t bottom, const SpanBuffer& buffer, - SpanChunkHandle chunk_handle) { - const Span *begin, *end; - buffer.getSpans(chunk_handle, begin, end); - append_spans(top, bottom, begin, end); - }; - auto a_lines = a.lines_; auto b_lines = b.lines_; - auto a_it = a_lines.begin(); auto b_it = b_lines.begin(); @@ -371,20 +410,20 @@ DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { while (a_it != a_lines.end() && b_it != b_lines.end()) { if (a_it->bottom <= b_it->top) { - append_line(a_it->top, a_it->bottom, a_buffer, a_it->chunk_handle); + res.appendLine(a_it->top, a_it->bottom, a_buffer, a_it->chunk_handle); ++a_it; } else if (b_it->bottom <= a_it->top) { - append_line(b_it->top, b_it->bottom, b_buffer, b_it->chunk_handle); + res.appendLine(b_it->top, b_it->bottom, b_buffer, b_it->chunk_handle); ++b_it; } else { if (a_it->top < b_it->top) { - append_line(a_it->top, b_it->top, a_buffer, a_it->chunk_handle); + res.appendLine(a_it->top, b_it->top, a_buffer, a_it->chunk_handle); a_it->top = b_it->top; if (a_it->top == b_it->bottom) { ++a_it; } } else if (b_it->top < a_it->top) { - append_line(b_it->top, a_it->top, b_buffer, b_it->chunk_handle); + res.appendLine(b_it->top, a_it->top, b_buffer, b_it->chunk_handle); b_it->top = a_it->top; if (b_it->top == a_it->bottom) { ++b_it; @@ -396,7 +435,7 @@ DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { FML_DCHECK(new_bottom > b_it->top); auto size = unionLineSpans(tmp, a_buffer, a_it->chunk_handle, b_buffer, b_it->chunk_handle); - append_spans(a_it->top, new_bottom, tmp.data(), tmp.data() + size); + res.appendLine(a_it->top, new_bottom, tmp.data(), tmp.data() + size); a_it->top = b_it->top = new_bottom; if (a_it->top == a_it->bottom) { ++a_it; @@ -411,18 +450,68 @@ DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { FML_DCHECK(a_it == a_lines.end() || b_it == b_lines.end()); while (a_it != a_lines.end()) { - append_line(a_it->top, a_it->bottom, a_buffer, a_it->chunk_handle); + res.appendLine(a_it->top, a_it->bottom, a_buffer, a_it->chunk_handle); ++a_it; } while (b_it != b_lines.end()) { - append_line(b_it->top, b_it->bottom, b_buffer, b_it->chunk_handle); + res.appendLine(b_it->top, b_it->bottom, b_buffer, b_it->chunk_handle); ++b_it; } return res; } +DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { + DlRegion res; + if (!SkIRect::Intersects(a.bounds_, b.bounds_)) { + return res; + } + + res.span_buffer_.reserve( + std::max(a.span_buffer_.capacity(), b.span_buffer_.capacity())); + + auto& lines = res.lines_; + lines.reserve(std::min(a.lines_.size(), b.lines_.size())); + + auto a_lines = a.lines_; + auto b_lines = b.lines_; + auto a_it = a_lines.begin(); + auto b_it = b_lines.begin(); + + auto& a_buffer = a.span_buffer_; + auto& b_buffer = b.span_buffer_; + + std::vector tmp; + + while (a_it != a_lines.end() && b_it != b_lines.end()) { + if (a_it->bottom <= b_it->top) { + ++a_it; + } else if (b_it->bottom <= a_it->top) { + ++b_it; + } else { + auto top = std::max(a_it->top, b_it->top); + auto bottom = std::min(a_it->bottom, b_it->bottom); + auto size = intersectLineSpans(tmp, a_buffer, a_it->chunk_handle, + b_buffer, b_it->chunk_handle); + if (size > 0) { + res.appendLine(top, bottom, tmp.data(), tmp.data() + size); + res.bounds_.join(SkIRect::MakeLTRB( + tmp.data()->left, top, (tmp.data() + size - 1)->right, bottom)); + } + a_it->top = b_it->top = bottom; + if (a_it->top == a_it->bottom) { + ++a_it; + } + if (b_it->top == b_it->bottom) { + ++b_it; + } + } + } + FML_DCHECK(a_it == a_lines.end() || b_it == b_lines.end()); + return res; +} + std::vector DlRegion::getRects(bool deband) const { std::vector rects; size_t rect_count = 0; diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index 8f0ca0f5f3744..f77532c37d2cf 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -27,6 +27,10 @@ class DlRegion { /// Matches SkRegion a; a.op(b, SkRegion::kUnion_Op) behavior. static DlRegion MakeUnion(const DlRegion& a, const DlRegion& b); + /// Creates intersection region of region a and be. + /// Matches SkRegion a; a.op(b, SkRegion::kIntersect_Op) behavior. + static DlRegion MakeIntersection(const DlRegion& a, const DlRegion& b); + /// Returns list of non-overlapping rectangles that cover current region. /// If |deband| is false, each span line will result in separate rectangles, /// closely matching SkRegion::Iterator behavior. @@ -99,6 +103,19 @@ class DlRegion { void addRects(const std::vector& rects); + void appendLine(int32_t top, + int32_t bottom, + const Span* begin, + const Span* end); + void appendLine(int32_t top, + int32_t bottom, + const SpanBuffer& buffer, + SpanChunkHandle handle) { + const Span *begin, *end; + buffer.getSpans(handle, begin, end); + appendLine(top, bottom, begin, end); + } + typedef std::vector SpanVec; SpanLine makeLine(int32_t top, int32_t bottom, const SpanVec&); SpanLine makeLine(int32_t top, @@ -110,6 +127,11 @@ class DlRegion { SpanChunkHandle a_handle, const SpanBuffer& b_buffer, SpanChunkHandle b_handle); + static size_t intersectLineSpans(std::vector& res, + const SpanBuffer& a_buffer, + SpanChunkHandle a_handle, + const SpanBuffer& b_buffer, + SpanChunkHandle b_handle); bool spansEqual(SpanLine& line, const Span* begin, const Span* end) const; diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index 272013f1d5127..82b41032615b0 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -373,8 +373,12 @@ TEST(DisplayListRegion, TestAgainstSkRegion) { DlRegion dl_union = DlRegion::MakeUnion(region1, region2); SkRegion sk_union(sk_region1); sk_union.op(sk_region2, SkRegion::kUnion_Op); - CheckEquality(dl_union, sk_union); + + DlRegion dl_intersection = DlRegion::MakeIntersection(region1, region2); + SkRegion sk_intersection(sk_region1); + sk_intersection.op(sk_region2, SkRegion::kIntersect_Op); + CheckEquality(dl_intersection, sk_intersection); } } From 730a23ae943ebcbc366cfe224c7150723a11b2e5 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 10 Jun 2023 10:52:31 +0200 Subject: [PATCH 10/39] Rename AddRects in benchmarks to FromRects --- .../benchmarking/dl_region_benchmarks.cc | 26 +++++++++---------- display_list/geometry/dl_region.cc | 3 +++ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index a61241da9ebdc..3c7dbb7e50ec0 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -90,7 +90,7 @@ class DlRegionAdapter { }; template -void RunAddRectsBenchmark(benchmark::State& state, int maxSize) { +void RunFromRectsBenchmark(benchmark::State& state, int maxSize) { std::random_device d; std::seed_seq seed{2, 1, 3}; std::mt19937 rng(seed); @@ -232,12 +232,12 @@ void RunIntersectsSingleRectBenchmark(benchmark::State& state, int maxSize) { namespace flutter { -static void BM_DlRegion_AddRects(benchmark::State& state, int maxSize) { - RunAddRectsBenchmark(state, maxSize); +static void BM_DlRegion_FromRects(benchmark::State& state, int maxSize) { + RunFromRectsBenchmark(state, maxSize); } -static void BM_SkRegion_AddRects(benchmark::State& state, int maxSize) { - RunAddRectsBenchmark(state, maxSize); +static void BM_SkRegion_FromRects(benchmark::State& state, int maxSize) { + RunFromRectsBenchmark(state, maxSize); } static void BM_DlRegion_GetRects(benchmark::State& state, int maxSize) { @@ -370,21 +370,21 @@ BENCHMARK_CAPTURE(BM_SkRegion_Operation, 1500) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Tiny, 30) +BENCHMARK_CAPTURE(BM_DlRegion_FromRects, Tiny, 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Tiny, 30) +BENCHMARK_CAPTURE(BM_SkRegion_FromRects, Tiny, 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Small, 100) +BENCHMARK_CAPTURE(BM_DlRegion_FromRects, Small, 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Small, 100) +BENCHMARK_CAPTURE(BM_SkRegion_FromRects, Small, 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Medium, 400) +BENCHMARK_CAPTURE(BM_DlRegion_FromRects, Medium, 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Medium, 400) +BENCHMARK_CAPTURE(BM_SkRegion_FromRects, Medium, 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_AddRects, Large, 1500) +BENCHMARK_CAPTURE(BM_DlRegion_FromRects, Large, 1500) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_AddRects, Large, 1500) +BENCHMARK_CAPTURE(BM_SkRegion_FromRects, Large, 1500) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_GetRects, Tiny, 30) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index d515c0c4213eb..9e3b8f74aa893 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -236,6 +236,9 @@ size_t DlRegion::intersectLineSpans(std::vector& res, } void DlRegion::addRects(const std::vector& unsorted_rects) { + // addRects can only be called on empty regions. + FML_DCHECK(lines_.empty()); + size_t count = unsorted_rects.size(); std::vector rects(count); for (size_t i = 0; i < count; i++) { From d6c81db09439f4cd0742a897a58c47aa8383a0e7 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 10 Jun 2023 17:05:16 +0200 Subject: [PATCH 11/39] Add more tests --- display_list/geometry/dl_region_unittests.cc | 57 ++++++++++++++++++-- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index 82b41032615b0..b69ed53453f7c 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -193,6 +193,58 @@ TEST(DisplayListRegion, Intersects2) { EXPECT_TRUE(region2.intersects(region1)); } +TEST(DisplayListRegion, Intersection1) { + DlRegion region1({ + SkIRect::MakeXYWH(0, 0, 20, 20), + SkIRect::MakeXYWH(20, 20, 20, 20), + }); + DlRegion region2({ + SkIRect::MakeXYWH(20, 0, 20, 20), + SkIRect::MakeXYWH(0, 20, 20, 20), + }); + DlRegion i = DlRegion::MakeIntersection(region1, region2); + EXPECT_EQ(i.bounds(), SkIRect::MakeEmpty()); + auto rects = i.getRects(); + EXPECT_TRUE(rects.empty()); +} + +TEST(DisplayListRegion, Intersection2) { + DlRegion region1({ + SkIRect::MakeXYWH(0, 0, 20, 20), + SkIRect::MakeXYWH(20, 20, 20, 20), + }); + DlRegion region2({ + SkIRect::MakeXYWH(0, 0, 20, 20), + SkIRect::MakeXYWH(20, 20, 20, 20), + }); + DlRegion i = DlRegion::MakeIntersection(region1, region2); + EXPECT_EQ(i.bounds(), SkIRect::MakeXYWH(0, 0, 40, 40)); + auto rects = i.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(0, 0, 20, 20), + SkIRect::MakeXYWH(20, 20, 20, 20), + }; + EXPECT_EQ(rects, expected); +} + +TEST(DisplayListRegion, Intersection3) { + DlRegion region1({ + SkIRect::MakeXYWH(0, 0, 20, 20), + }); + DlRegion region2({ + SkIRect::MakeXYWH(-10, -10, 20, 20), + SkIRect::MakeXYWH(10, 10, 20, 20), + }); + DlRegion i = DlRegion::MakeIntersection(region1, region2); + EXPECT_EQ(i.bounds(), SkIRect::MakeXYWH(0, 0, 20, 20)); + auto rects = i.getRects(); + std::vector expected{ + SkIRect::MakeXYWH(0, 0, 10, 10), + SkIRect::MakeXYWH(10, 10, 10, 10), + }; + EXPECT_EQ(rects, expected); +} + TEST(DisplayListRegion, Union1) { DlRegion region1({ SkIRect::MakeXYWH(0, 0, 20, 20), @@ -248,11 +300,6 @@ TEST(DisplayListRegion, Union3) { SkIRect::MakeXYWH(0, 10, 20, 10), }; EXPECT_EQ(rects, expected); - // for (auto& rect : rects) { - // printf("SkIRect::MakeXYWH(%d, %d, %d, %d,),\n", rect.fLeft, rect.fTop, - // rect.width(), - // rect.height()); - // } } TEST(DisplayListRegion, UnionEmpty) { From 48a740b611ad65f0d58a985abb7fd6ddb776dfce Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 13 Jun 2023 15:14:47 +0200 Subject: [PATCH 12/39] Fix comments --- display_list/geometry/dl_region.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index f77532c37d2cf..90f806b19994f 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -23,11 +23,11 @@ class DlRegion { DlRegion(DlRegion&&) = default; - /// Creates union region of region a and be. + /// Creates union region of region a and b. /// Matches SkRegion a; a.op(b, SkRegion::kUnion_Op) behavior. static DlRegion MakeUnion(const DlRegion& a, const DlRegion& b); - /// Creates intersection region of region a and be. + /// Creates intersection region of region a and b. /// Matches SkRegion a; a.op(b, SkRegion::kIntersect_Op) behavior. static DlRegion MakeIntersection(const DlRegion& a, const DlRegion& b); From c47663d98957faeb25b08b20560bf89a8d61b97c Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 13 Jun 2023 15:15:43 +0200 Subject: [PATCH 13/39] Remove unnecessary lines_ copy --- display_list/geometry/dl_region.cc | 92 +++++++++++++++++------------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 9e3b8f74aa893..30e26b905844f 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -401,64 +401,70 @@ DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { auto& lines = res.lines_; lines.reserve(a.lines_.size() + b.lines_.size()); - auto a_lines = a.lines_; - auto b_lines = b.lines_; - auto a_it = a_lines.begin(); - auto b_it = b_lines.begin(); + auto a_it = a.lines_.begin(); + auto b_it = b.lines_.begin(); + auto a_end = a.lines_.end(); + auto b_end = b.lines_.end(); auto& a_buffer = a.span_buffer_; auto& b_buffer = b.span_buffer_; std::vector tmp; - while (a_it != a_lines.end() && b_it != b_lines.end()) { - if (a_it->bottom <= b_it->top) { - res.appendLine(a_it->top, a_it->bottom, a_buffer, a_it->chunk_handle); + int32_t cur_top = std::numeric_limits::min(); + + while (a_it != a_end && b_it != b_end) { + auto a_top = std::max(cur_top, a_it->top); + auto b_top = std::max(cur_top, b_it->top); + if (a_it->bottom <= b_top) { + res.appendLine(a_top, a_it->bottom, a_buffer, a_it->chunk_handle); ++a_it; - } else if (b_it->bottom <= a_it->top) { - res.appendLine(b_it->top, b_it->bottom, b_buffer, b_it->chunk_handle); + } else if (b_it->bottom <= a_top) { + res.appendLine(b_top, b_it->bottom, b_buffer, b_it->chunk_handle); ++b_it; } else { - if (a_it->top < b_it->top) { - res.appendLine(a_it->top, b_it->top, a_buffer, a_it->chunk_handle); - a_it->top = b_it->top; - if (a_it->top == b_it->bottom) { + if (a_top < b_top) { + res.appendLine(a_top, b_top, a_buffer, a_it->chunk_handle); + cur_top = b_top; + if (cur_top == a_it->bottom) { ++a_it; } - } else if (b_it->top < a_it->top) { - res.appendLine(b_it->top, a_it->top, b_buffer, b_it->chunk_handle); - b_it->top = a_it->top; - if (b_it->top == a_it->bottom) { + } else if (b_top < a_top) { + res.appendLine(b_top, a_top, b_buffer, b_it->chunk_handle); + cur_top = a_top; + if (cur_top == b_it->bottom) { ++b_it; } } else { auto new_bottom = std::min(a_it->bottom, b_it->bottom); - FML_DCHECK(a_it->top == b_it->top); - FML_DCHECK(new_bottom > a_it->top); - FML_DCHECK(new_bottom > b_it->top); + FML_DCHECK(a_top == b_top); + FML_DCHECK(new_bottom > a_top); + FML_DCHECK(new_bottom > b_top); auto size = unionLineSpans(tmp, a_buffer, a_it->chunk_handle, b_buffer, b_it->chunk_handle); - res.appendLine(a_it->top, new_bottom, tmp.data(), tmp.data() + size); - a_it->top = b_it->top = new_bottom; - if (a_it->top == a_it->bottom) { + res.appendLine(a_top, new_bottom, tmp.data(), tmp.data() + size); + cur_top = new_bottom; + if (cur_top == a_it->bottom) { ++a_it; } - if (b_it->top == b_it->bottom) { + if (cur_top == b_it->bottom) { ++b_it; } } } } - FML_DCHECK(a_it == a_lines.end() || b_it == b_lines.end()); + FML_DCHECK(a_it == a_end || b_it == b_end); - while (a_it != a_lines.end()) { - res.appendLine(a_it->top, a_it->bottom, a_buffer, a_it->chunk_handle); + while (a_it != a_end) { + auto a_top = std::max(cur_top, a_it->top); + res.appendLine(a_top, a_it->bottom, a_buffer, a_it->chunk_handle); ++a_it; } - while (b_it != b_lines.end()) { - res.appendLine(b_it->top, b_it->bottom, b_buffer, b_it->chunk_handle); + while (b_it != b_end) { + auto b_top = std::max(cur_top, b_it->top); + res.appendLine(b_top, b_it->bottom, b_buffer, b_it->chunk_handle); ++b_it; } @@ -477,23 +483,27 @@ DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { auto& lines = res.lines_; lines.reserve(std::min(a.lines_.size(), b.lines_.size())); - auto a_lines = a.lines_; - auto b_lines = b.lines_; - auto a_it = a_lines.begin(); - auto b_it = b_lines.begin(); + auto a_it = a.lines_.begin(); + auto a_end = a.lines_.end(); + auto b_it = b.lines_.begin(); + auto b_end = b.lines_.end(); auto& a_buffer = a.span_buffer_; auto& b_buffer = b.span_buffer_; std::vector tmp; - while (a_it != a_lines.end() && b_it != b_lines.end()) { - if (a_it->bottom <= b_it->top) { + int32_t cur_top = std::numeric_limits::min(); + + while (a_it != a_end && b_it != b_end) { + auto a_top = std::max(cur_top, a_it->top); + auto b_top = std::max(cur_top, b_it->top); + if (a_it->bottom <= b_top) { ++a_it; - } else if (b_it->bottom <= a_it->top) { + } else if (b_it->bottom <= a_top) { ++b_it; } else { - auto top = std::max(a_it->top, b_it->top); + auto top = std::max(a_top, b_top); auto bottom = std::min(a_it->bottom, b_it->bottom); auto size = intersectLineSpans(tmp, a_buffer, a_it->chunk_handle, b_buffer, b_it->chunk_handle); @@ -502,16 +512,16 @@ DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { res.bounds_.join(SkIRect::MakeLTRB( tmp.data()->left, top, (tmp.data() + size - 1)->right, bottom)); } - a_it->top = b_it->top = bottom; - if (a_it->top == a_it->bottom) { + cur_top = bottom; + if (cur_top == a_it->bottom) { ++a_it; } - if (b_it->top == b_it->bottom) { + if (cur_top == b_it->bottom) { ++b_it; } } } - FML_DCHECK(a_it == a_lines.end() || b_it == b_lines.end()); + FML_DCHECK(a_it == a_end || b_it == b_end); return res; } From 40615276695719cfa41c94fa9187a4093f89743f Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 13 Jun 2023 15:21:45 +0200 Subject: [PATCH 14/39] Add setChunkSize, use getChunkSize also internally --- display_list/geometry/dl_region.cc | 10 +++++++--- display_list/geometry/dl_region.h | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 30e26b905844f..078bedb8941c9 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -37,7 +37,7 @@ DlRegion::SpanChunkHandle DlRegion::SpanBuffer::storeChunk(const Span* begin, } SpanChunkHandle res = size_; size_ += chunk_size + 1; - spans_[res].left = chunk_size; + setChunkSize(res, chunk_size); auto* dst = spans_ + res + 1; memmove(dst, begin, chunk_size * sizeof(Span)); @@ -50,13 +50,17 @@ size_t DlRegion::SpanBuffer::getChunkSize(SpanChunkHandle handle) const { return spans_[handle].left; } +void DlRegion::SpanBuffer::setChunkSize(SpanChunkHandle handle, size_t size) { + FML_DCHECK(handle < size_); + spans_[handle].left = size; +} + void DlRegion::SpanBuffer::getSpans(SpanChunkHandle handle, const DlRegion::Span*& begin, const DlRegion::Span*& end) const { FML_DCHECK(handle < size_); - auto& info = spans_[handle]; begin = spans_ + handle + 1; - end = begin + info.left; + end = begin + getChunkSize(handle); } DlRegion::DlRegion(const std::vector& rects) { diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index 90f806b19994f..1318f71be04f0 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -82,6 +82,8 @@ class DlRegion { ~SpanBuffer(); private: + void setChunkSize(SpanChunkHandle handle, size_t size); + size_t capacity_ = 0; size_t size_ = 0; From 518bf1dc9e18746aa80312313ced0aa120c41453 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 13 Jun 2023 15:30:23 +0200 Subject: [PATCH 15/39] Add DCHECK --- display_list/geometry/dl_region.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 078bedb8941c9..a8c59a9636640 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -509,6 +509,7 @@ DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { } else { auto top = std::max(a_top, b_top); auto bottom = std::min(a_it->bottom, b_it->bottom); + FML_DCHECK(top < bottom); auto size = intersectLineSpans(tmp, a_buffer, a_it->chunk_handle, b_buffer, b_it->chunk_handle); if (size > 0) { From d4d001e778ab11098ac8229e4b85193f1caa605e Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 13 Jun 2023 15:31:22 +0200 Subject: [PATCH 16/39] Remove redundant check --- display_list/geometry/dl_region.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index a8c59a9636640..a3a38b30d3c7c 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -603,7 +603,7 @@ bool DlRegion::intersects(const SkIRect& rect) const { const Span *begin, *end; span_buffer_.getSpans(it->chunk_handle, begin, end); while (begin != end && begin->left < rect.fRight) { - if (begin->right > rect.fLeft && begin->left < rect.fRight) { + if (begin->right > rect.fLeft) { return true; } ++begin; From e5a42d04f86ac7a627f2f285474db7c0544aa8ab Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 13 Jun 2023 15:42:21 +0200 Subject: [PATCH 17/39] Simplify unionLineSpans --- display_list/geometry/dl_region.cc | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index a3a38b30d3c7c..edbf84219992b 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -139,18 +139,11 @@ size_t DlRegion::unionLineSpans(std::vector& res, *new_span++ = *begin1++; } else if (begin2->right < begin1->left) { *new_span++ = *begin2++; - } else if (begin1->left < begin2->left) { - current_span = *begin1; - ++begin1; - } else if (begin2->left < begin1->left) { - current_span = *begin2; - ++begin2; } else { - FML_DCHECK(begin1->left == begin2->left); - current_span.left = begin1->left; - current_span.right = std::max(begin1->right, begin2->right); - ++begin1; - ++begin2; + current_span = {std::min(begin1->left, begin2->left), + std::max(begin1->right, begin2->right)}; + begin1++; + begin2++; } } else if (current_span.right >= begin1->left) { current_span.right = std::max(current_span.right, begin1->right); @@ -159,8 +152,7 @@ size_t DlRegion::unionLineSpans(std::vector& res, current_span.right = std::max(current_span.right, begin2->right); ++begin2; } else { - *new_span = current_span; - ++new_span; + *new_span++ = current_span; current_span.left = current_span.right = 0; } } From 3dfb25c3a207f83d63428f4d9868c661ff67b125 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 13 Jun 2023 23:12:33 +0200 Subject: [PATCH 18/39] Use SkRegion::setRects --- display_list/geometry/dl_region_unittests.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index b69ed53453f7c..2ad4a54ab0f22 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -391,17 +391,16 @@ TEST(DisplayListRegion, TestAgainstSkRegion) { SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); rects_in1.push_back(rect); - sk_region1.op(rect, SkRegion::kUnion_Op); - rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); rects_in2.push_back(rect); - sk_region2.op(rect, SkRegion::kUnion_Op); } DlRegion region1(rects_in1); + sk_region1.setRects(rects_in1.data(), rects_in1.size()); CheckEquality(region1, sk_region1); DlRegion region2(rects_in2); + sk_region2.setRects(rects_in2.data(), rects_in2.size()); CheckEquality(region2, sk_region2); auto intersects_1 = region1.intersects(region2); From c40bb7cb5cea0ca63d95e6e24c43c94bde578f09 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 16 Jun 2023 10:48:24 +0200 Subject: [PATCH 19/39] Fix typo. --- display_list/geometry/dl_region.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index 1318f71be04f0..4dc7adf87be16 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -35,7 +35,7 @@ class DlRegion { /// If |deband| is false, each span line will result in separate rectangles, /// closely matching SkRegion::Iterator behavior. /// If |deband| is true, matching rectangles from adjacent span lines will be - /// merged into single rectange. + /// merged into single rectangle. std::vector getRects(bool deband = true) const; /// Returns maximum and minimum axis values of rectangles in this region. From d63212ef2927e18ad955f5518084617651c06e68 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 16 Jun 2023 19:42:34 +0200 Subject: [PATCH 20/39] Union and intersction optimisations --- .../benchmarking/dl_region_benchmarks.cc | 118 ++++++++++++++++-- display_list/geometry/dl_region.cc | 63 ++++++++-- display_list/geometry/dl_region.h | 9 +- 3 files changed, 168 insertions(+), 22 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index 3c7dbb7e50ec0..69fb1b89d1c38 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -134,7 +134,10 @@ void RunGetRectsBenchmark(benchmark::State& state, int maxSize) { enum RegionOp { kUnion, kIntersection }; template -void RunRegionOpBenchmark(benchmark::State& state, RegionOp op, int maxSize) { +void RunRegionOpBenchmark(benchmark::State& state, + RegionOp op, + bool withSingleRect, + int maxSize) { std::random_device d; std::seed_seq seed{2, 1, 3}; std::mt19937 rng(seed); @@ -151,7 +154,7 @@ void RunRegionOpBenchmark(benchmark::State& state, RegionOp op, int maxSize) { Region region1(rects); rects.clear(); - for (int i = 0; i < 500; ++i) { + for (int i = 0; i < (withSingleRect ? 1 : 500); ++i) { SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); rects.push_back(rect); } @@ -250,14 +253,16 @@ static void BM_SkRegion_GetRects(benchmark::State& state, int maxSize) { static void BM_DlRegion_Operation(benchmark::State& state, RegionOp op, + bool withSingleRect, int maxSize) { - RunRegionOpBenchmark(state, op, maxSize); + RunRegionOpBenchmark(state, op, withSingleRect, maxSize); } static void BM_SkRegion_Operation(benchmark::State& state, RegionOp op, + bool withSingleRect, int maxSize) { - RunRegionOpBenchmark(state, op, maxSize); + RunRegionOpBenchmark(state, op, withSingleRect, maxSize); } static void BM_DlRegion_IntersectsRegion(benchmark::State& state, int maxSize) { @@ -312,61 +317,150 @@ BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Large, 1500) BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Large, 1500) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Tiny, RegionOp::kUnion, 30) +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Union_Tiny, + RegionOp::kUnion, + false, + 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Tiny, RegionOp::kUnion, 30) +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Union_Tiny, + RegionOp::kUnion, + false, + 30) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Small, RegionOp::kUnion, 100) +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Union_Small, + RegionOp::kUnion, + false, + 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Small, RegionOp::kUnion, 100) +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Union_Small, + RegionOp::kUnion, + false, + 100) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Medium, RegionOp::kUnion, 400) +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Union_Medium, + RegionOp::kUnion, + false, + 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Medium, RegionOp::kUnion, 400) +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Union_Medium, + RegionOp::kUnion, + false, + 400) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Large, RegionOp::kUnion, 1500) +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Union_Large, + RegionOp::kUnion, + false, + 1500) ->Unit(benchmark::kMicrosecond); -BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Large, RegionOp::kUnion, 1500) +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Union_Large, + RegionOp::kUnion, + false, + 1500) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_Tiny, RegionOp::kIntersection, + false, 30) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_Tiny, RegionOp::kIntersection, + false, 30) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_Small, RegionOp::kIntersection, + false, 100) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_Small, RegionOp::kIntersection, + false, 100) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_Medium, RegionOp::kIntersection, + false, 400) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_Medium, RegionOp::kIntersection, + false, 400) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_Large, RegionOp::kIntersection, + false, 1500) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_Large, RegionOp::kIntersection, + false, + 1500) + ->Unit(benchmark::kMicrosecond); + +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_SingleRect_Tiny, + RegionOp::kIntersection, + true, + 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_SingleRect_Tiny, + RegionOp::kIntersection, + true, + 30) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_SingleRect_Small, + RegionOp::kIntersection, + true, + 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_SingleRect_Small, + RegionOp::kIntersection, + true, + 100) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_SingleRect_Medium, + RegionOp::kIntersection, + true, + 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_SingleRect_Medium, + RegionOp::kIntersection, + true, + 400) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_SingleRect_Large, + RegionOp::kIntersection, + true, + 1500) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_SingleRect_Large, + RegionOp::kIntersection, + true, 1500) ->Unit(benchmark::kMicrosecond); diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index edbf84219992b..61fbc1ffe2318 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -15,6 +15,16 @@ DlRegion::SpanBuffer::SpanBuffer(DlRegion::SpanBuffer&& m) m.spans_ = nullptr; }; +DlRegion::SpanBuffer::SpanBuffer(const DlRegion::SpanBuffer& m) + : capacity_(m.capacity_), size_(m.size_) { + if (m.spans_ == nullptr) { + spans_ = nullptr; + } else { + spans_ = static_cast(std::malloc(capacity_ * sizeof(Span))); + memcpy(spans_, m.spans_, size_ * sizeof(Span)); + } +}; + DlRegion::SpanBuffer::~SpanBuffer() { free(spans_); } @@ -64,11 +74,16 @@ void DlRegion::SpanBuffer::getSpans(SpanChunkHandle handle, } DlRegion::DlRegion(const std::vector& rects) { - addRects(rects); + setRects(rects); } DlRegion::DlRegion() {} +DlRegion::DlRegion(const SkIRect& rect) : bounds_(rect) { + Span span{rect.left(), rect.right()}; + lines_.push_back(makeLine(rect.top(), rect.bottom(), &span, &span + 1)); +} + bool DlRegion::spansEqual(SpanLine& line, const Span* begin, const Span* end) const { @@ -231,8 +246,8 @@ size_t DlRegion::intersectLineSpans(std::vector& res, return new_span - res.data(); } -void DlRegion::addRects(const std::vector& unsorted_rects) { - // addRects can only be called on empty regions. +void DlRegion::setRects(const std::vector& unsorted_rects) { + // setRects can only be called on empty regions. FML_DCHECK(lines_.empty()); size_t count = unsorted_rects.size(); @@ -386,11 +401,15 @@ void DlRegion::appendLine(int32_t top, } DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { - DlRegion res; + if (a.isSimple() && a.bounds_.contains(b.bounds_)) { + return a; + } else if (b.isSimple() && b.bounds_.contains(a.bounds_)) { + return b; + } + DlRegion res; res.bounds_ = a.bounds_; res.bounds_.join(b.bounds_); - res.span_buffer_.reserve(a.span_buffer_.capacity() + b.span_buffer_.capacity()); @@ -468,11 +487,20 @@ DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { } DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { - DlRegion res; if (!SkIRect::Intersects(a.bounds_, b.bounds_)) { - return res; + return DlRegion(); + } else if (a.isSimple() && b.isSimple()) { + SkIRect r(a.bounds_); + auto res = r.intersect(b.bounds_); + FML_DCHECK(res); + return DlRegion(r); + } else if (a.isSimple() && a.bounds_.contains(b.bounds_)) { + return b; + } else if (b.isSimple() && b.bounds_.contains(a.bounds_)) { + return a; } + DlRegion res; res.span_buffer_.reserve( std::max(a.span_buffer_.capacity(), b.span_buffer_.capacity())); @@ -484,6 +512,18 @@ DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { auto b_it = b.lines_.begin(); auto b_end = b.lines_.end(); + // When intersecting single rectangle with a complex region use binary + // search to find the first line that intersects the rectangle. + if (b_it == b_end - 1) { + a_it = std::upper_bound( + a_it, a_end, b_it->top, + [](int32_t top, const SpanLine& line) { return top < line.bottom; }); + } else if (a_it == a_end - 1) { + b_it = std::upper_bound( + b_it, b_end, a_it->top, + [](int32_t top, const SpanLine& line) { return top < line.bottom; }); + } + auto& a_buffer = a.span_buffer_; auto& b_buffer = b.span_buffer_; @@ -524,6 +564,13 @@ DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { std::vector DlRegion::getRects(bool deband) const { std::vector rects; + if (isEmpty()) { + return rects; + } else if (isSimple()) { + rects.push_back(bounds_); + return rects; + } + size_t rect_count = 0; size_t previous_span_end = 0; for (const auto& line : lines_) { @@ -578,7 +625,7 @@ bool DlRegion::intersects(const SkIRect& rect) const { auto bounds_intersect = SkIRect::Intersects(bounds_, rect); - if (!isComplex()) { + if (isSimple()) { return bounds_intersect; } diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index 4dc7adf87be16..ac361de3c50b8 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -21,6 +21,10 @@ class DlRegion { /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. explicit DlRegion(const std::vector& rects); + /// Creates region covering area of a rectangle. + explicit DlRegion(const SkIRect& rect); + + DlRegion(const DlRegion&) = default; DlRegion(DlRegion&&) = default; /// Creates union region of region a and b. @@ -67,7 +71,7 @@ class DlRegion { class SpanBuffer { public: SpanBuffer() = default; - SpanBuffer(const SpanBuffer&) = delete; + SpanBuffer(const SpanBuffer&); SpanBuffer(SpanBuffer&& m); void reserve(size_t capacity); @@ -96,6 +100,7 @@ class DlRegion { bool isEmpty() const { return lines_.empty(); } bool isComplex() const; + bool isSimple() const { return !isComplex(); } struct SpanLine { int32_t top; @@ -103,7 +108,7 @@ class DlRegion { SpanChunkHandle chunk_handle; }; - void addRects(const std::vector& rects); + void setRects(const std::vector& rects); void appendLine(int32_t top, int32_t bottom, From bc2ab15742c1dc5875036f0dda6ec58c39e14d67 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 16 Jun 2023 21:29:40 +0200 Subject: [PATCH 21/39] Fix comment typos --- display_list/geometry/dl_rtree.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/display_list/geometry/dl_rtree.h b/display_list/geometry/dl_rtree.h index 0de686abc52d5..3ac4d63475653 100644 --- a/display_list/geometry/dl_rtree.h +++ b/display_list/geometry/dl_rtree.h @@ -50,7 +50,7 @@ class DlRTree : public SkRefCnt { /// If an array of IDs is not specified then all leaf nodes will be /// represented by the |invalid_id| (which defaults to -1). /// - /// Invallid rects that are empty or contain a NaN value will not be + /// Invalid rects that are empty or contain a NaN value will not be /// stored in the R-Tree. And, if a |predicate| function is provided, /// that function will be called to query if the rectangle associated /// with the ID should be included. @@ -73,8 +73,8 @@ class DlRTree : public SkRefCnt { /// The returned indices may not themselves be in numerical order, /// but they will represent the rectangles and IDs in the order in /// which they were passed into the constructor. The actual rectangle - /// and ID associated with each index can be retreived using the - /// |DlRTree::id| and |DlRTree::bouds| methods. + /// and ID associated with each index can be retrieved using the + /// |DlRTree::id| and |DlRTree::bounds| methods. void search(const SkRect& query, std::vector* results) const; /// Return the ID for the indicated result of a query or From 5d53c350e7ad9cf88d706a6bcc1606f5daf5edd3 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 16 Jun 2023 22:47:02 +0200 Subject: [PATCH 22/39] Expose region in DlRTree --- display_list/geometry/dl_rtree.cc | 12 ++++++++++++ display_list/geometry/dl_rtree.h | 13 +++++++++++++ display_list/geometry/dl_rtree_unittests.cc | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/display_list/geometry/dl_rtree.cc b/display_list/geometry/dl_rtree.cc index 14cff7e321e66..a4a805957bcc3 100644 --- a/display_list/geometry/dl_rtree.cc +++ b/display_list/geometry/dl_rtree.cc @@ -201,6 +201,18 @@ void DlRTree::search(const Node& parent, } } +const DlRegion& DlRTree::region() const { + if (!region_) { + std::vector rects; + rects.resize(leaf_count_); + for (int i = 0; i < leaf_count_; i++) { + nodes_[i].bounds.roundOut(&rects[i]); + } + region_.emplace(rects); + } + return *region_; +} + const SkRect& DlRTree::bounds() const { if (!nodes_.empty()) { return nodes_.back().bounds; diff --git a/display_list/geometry/dl_rtree.h b/display_list/geometry/dl_rtree.h index 3ac4d63475653..f12486edcce76 100644 --- a/display_list/geometry/dl_rtree.h +++ b/display_list/geometry/dl_rtree.h @@ -6,8 +6,10 @@ #define FLUTTER_DISPLAY_LIST_GEOMETRY_DL_RTREE_H_ #include +#include #include +#include "flutter/display_list/geometry/dl_region.h" #include "flutter/fml/logging.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkRefCnt.h" @@ -123,6 +125,16 @@ class DlRTree : public SkRefCnt { std::list searchAndConsolidateRects(const SkRect& query, bool deband = true) const; + /// Returns DlRegion that represents the union of all rectangles in the + /// R-Tree. + const DlRegion& region() const; + + /// Returns DlRegion that represents the union of all rectangles in the + /// R-Tree intersected with the query rect. + DlRegion region(const SkRect& query) const { + return DlRegion::MakeIntersection(region(), DlRegion(query.roundOut())); + } + private: static constexpr SkRect empty_ = SkRect::MakeEmpty(); @@ -133,6 +145,7 @@ class DlRTree : public SkRefCnt { std::vector nodes_; int leaf_count_; int invalid_id_; + mutable std::optional region_; }; } // namespace flutter diff --git a/display_list/geometry/dl_rtree_unittests.cc b/display_list/geometry/dl_rtree_unittests.cc index 80677931f8c31..ed16a1429bf27 100644 --- a/display_list/geometry/dl_rtree_unittests.cc +++ b/display_list/geometry/dl_rtree_unittests.cc @@ -301,5 +301,23 @@ TEST(DisplayListRTree, OverlappingRects) { EXPECT_EQ(list.front(), SkRect::MakeLTRB(0, 0, 70, 70)); } +TEST(DisplayListRTree, Region) { + SkRect rect[9]; + for (int i = 0; i < 9; i++) { + rect[i] = SkRect::MakeXYWH(i * 10, i * 10, 20, 20); + } + DlRTree rtree(rect, 9); + auto region = rtree.region(); + auto rects = region.getRects(true); + std::vector expected_rects{ + SkIRect::MakeLTRB(0, 0, 20, 10), SkIRect::MakeLTRB(0, 10, 30, 20), + SkIRect::MakeLTRB(10, 20, 40, 30), SkIRect::MakeLTRB(20, 30, 50, 40), + SkIRect::MakeLTRB(30, 40, 60, 50), SkIRect::MakeLTRB(40, 50, 70, 60), + SkIRect::MakeLTRB(50, 60, 80, 70), SkIRect::MakeLTRB(60, 70, 90, 80), + SkIRect::MakeLTRB(70, 80, 100, 90), SkIRect::MakeLTRB(80, 90, 100, 100), + }; + EXPECT_EQ(rects.size(), expected_rects.size()); +} + } // namespace testing } // namespace flutter From 95105b5648ac704e8883dbcf54d6532c9852996e Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 16 Jun 2023 22:51:28 +0200 Subject: [PATCH 23/39] Use DlRegion from R-Tree in raster cache --- display_list/geometry/dl_region.cc | 2 ++ flow/raster_cache.cc | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 61fbc1ffe2318..6ffef9433a5f7 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -62,6 +62,8 @@ size_t DlRegion::SpanBuffer::getChunkSize(SpanChunkHandle handle) const { void DlRegion::SpanBuffer::setChunkSize(SpanChunkHandle handle, size_t size) { FML_DCHECK(handle < size_); + FML_DCHECK(spans_ != nullptr); + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) spans_[handle].left = size; } diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index 90f49d0371c14..ab1f185fcd264 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -53,16 +53,17 @@ void RasterCacheResult::draw(DlCanvas& canvas, // On some platforms RTree from overlay layers is used for unobstructed // platform views and hit testing. To preserve the RTree raster cache must // paint individual rects instead of the whole image. - auto rects = rtree_->searchAndConsolidateRects(kGiantRect); + auto rects = rtree_->region().getRects(true); canvas.Translate(bounds.fLeft, bounds.fTop); SkRect rtree_bounds = RasterCacheUtil::GetRoundedOutDeviceBounds(rtree_->bounds(), matrix); for (auto rect : rects) { - rect = RasterCacheUtil::GetRoundedOutDeviceBounds(rect, matrix); - rect.offset(-rtree_bounds.fLeft, -rtree_bounds.fTop); - canvas.DrawImageRect(image_, rect, rect, + SkRect device_rect = RasterCacheUtil::GetRoundedOutDeviceBounds( + SkRect::Make(rect), matrix); + device_rect.offset(-rtree_bounds.fLeft, -rtree_bounds.fTop); + canvas.DrawImageRect(image_, device_rect, device_rect, DlImageSampling::kNearestNeighbor, paint); } } From f6e2a4288482ca77708ba12962449d55ec0d568d Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 23 Jun 2023 19:47:25 +0200 Subject: [PATCH 24/39] Add empty constructor and assignment operators --- display_list/geometry/dl_region.cc | 17 +++++++++++++++-- display_list/geometry/dl_region.h | 10 ++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 6ffef9433a5f7..d29cacc3ca159 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -25,6 +25,21 @@ DlRegion::SpanBuffer::SpanBuffer(const DlRegion::SpanBuffer& m) } }; +DlRegion::SpanBuffer& DlRegion::SpanBuffer::operator=( + const DlRegion::SpanBuffer& buffer) { + SpanBuffer copy(buffer); + std::swap(*this, copy); + return *this; +} + +DlRegion::SpanBuffer& DlRegion::SpanBuffer::operator=( + DlRegion::SpanBuffer&& buffer) { + std::swap(capacity_, buffer.capacity_); + std::swap(size_, buffer.size_); + std::swap(spans_, buffer.spans_); + return *this; +} + DlRegion::SpanBuffer::~SpanBuffer() { free(spans_); } @@ -79,8 +94,6 @@ DlRegion::DlRegion(const std::vector& rects) { setRects(rects); } -DlRegion::DlRegion() {} - DlRegion::DlRegion(const SkIRect& rect) : bounds_(rect) { Span span{rect.left(), rect.right()}; lines_.push_back(makeLine(rect.top(), rect.bottom(), &span, &span + 1)); diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index ac361de3c50b8..b7b529f74a344 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -17,6 +17,9 @@ namespace flutter { /// converting set of overlapping rectangles to non-overlapping rectangles. class DlRegion { public: + /// Creates an empty region. + DlRegion() = default; + /// Creates region by bulk adding the rectangles. /// Matches SkRegion::op(rect, SkRegion::kUnion_Op) behavior. explicit DlRegion(const std::vector& rects); @@ -27,6 +30,9 @@ class DlRegion { DlRegion(const DlRegion&) = default; DlRegion(DlRegion&&) = default; + DlRegion& operator=(const DlRegion&) = default; + DlRegion& operator=(DlRegion&&) = default; + /// Creates union region of region a and b. /// Matches SkRegion a; a.op(b, SkRegion::kUnion_Op) behavior. static DlRegion MakeUnion(const DlRegion& a, const DlRegion& b); @@ -73,6 +79,8 @@ class DlRegion { SpanBuffer() = default; SpanBuffer(const SpanBuffer&); SpanBuffer(SpanBuffer&& m); + SpanBuffer& operator=(const SpanBuffer&); + SpanBuffer& operator=(SpanBuffer&& m); void reserve(size_t capacity); size_t capacity() const { return capacity_; } @@ -96,8 +104,6 @@ class DlRegion { Span* spans_ = nullptr; }; - DlRegion(); - bool isEmpty() const { return lines_.empty(); } bool isComplex() const; bool isSimple() const { return !isComplex(); } From 9132921e449bf37ae6f29f76d88e7da18145a882 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 23 Jun 2023 19:50:20 +0200 Subject: [PATCH 25/39] Fix clang-tidy warning --- display_list/geometry/dl_region.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index d29cacc3ca159..ac90847fe8860 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -507,6 +507,7 @@ DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { } else if (a.isSimple() && b.isSimple()) { SkIRect r(a.bounds_); auto res = r.intersect(b.bounds_); + (void)res; // Suppress unused variable warning in release builds. FML_DCHECK(res); return DlRegion(r); } else if (a.isSimple() && a.bounds_.contains(b.bounds_)) { From abca8ef18ad8c33c737746513c2a32f06a7cc6c2 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 24 Jun 2023 11:44:40 +0200 Subject: [PATCH 26/39] Use SkRegion::setRects in benchmarks --- display_list/benchmarking/dl_region_benchmarks.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index 69fb1b89d1c38..717f741e15d45 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -14,9 +14,7 @@ namespace { class SkRegionAdapter { public: explicit SkRegionAdapter(const std::vector& rects) { - for (const auto& rect : rects) { - region_.op(rect, SkRegion::kUnion_Op); - } + region_.setRects(rects.data(), rects.size()); } SkIRect getBounds() { return region_.getBounds(); } From 0a5ca5f4167b8311c95be329eeff49a25ac9cc04 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 24 Jun 2023 11:46:31 +0200 Subject: [PATCH 27/39] Nits --- display_list/geometry/dl_region.cc | 12 +++++++----- display_list/geometry/dl_region.h | 14 +++++++++----- display_list/geometry/dl_region_unittests.cc | 6 +++--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index ac90847fe8860..f86f6e00f352f 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -127,6 +127,8 @@ DlRegion::SpanLine DlRegion::makeLine(int32_t top, return {top, bottom, handle}; } +// Returns number of valid spans in res. For performance reasons res is never +// downsized. size_t DlRegion::unionLineSpans(std::vector& res, const SpanBuffer& a_buffer, SpanChunkHandle a_handle, @@ -626,8 +628,6 @@ std::vector DlRegion::getRects(bool deband) const { return rects; } -DlRegion::~DlRegion() {} - bool DlRegion::isComplex() const { return lines_.size() > 1 || (lines_.size() == 1 && @@ -654,7 +654,7 @@ bool DlRegion::intersects(const SkIRect& rect) const { [](int32_t i, const SpanLine& line) { return i < line.bottom; }); while (it != lines_.end() && it->top < rect.fBottom) { - FML_DCHECK(rect.fTop < it->bottom || it->top < rect.fBottom); + FML_DCHECK(rect.fTop < it->bottom && it->top < rect.fBottom); const Span *begin, *end; span_buffer_.getSpans(it->chunk_handle, begin, end); while (begin != end && begin->left < rect.fRight) { @@ -711,15 +711,17 @@ bool DlRegion::intersects(const DlRegion& region) const { } auto ours = lines_.begin(); + auto ours_end = lines_.end(); auto theirs = region.lines_.begin(); + auto theirs_end = region.lines_.end(); - while (ours != lines_.end() && theirs != region.lines_.end()) { + while (ours != ours_end && theirs != theirs_end) { if (ours->bottom <= theirs->top) { ++ours; } else if (theirs->bottom <= ours->top) { ++theirs; } else { - FML_DCHECK(ours->top < theirs->bottom || theirs->top < ours->bottom); + FML_DCHECK(ours->top < theirs->bottom && theirs->top < ours->bottom); const Span *ours_begin, *ours_end; span_buffer_.getSpans(ours->chunk_handle, ours_begin, ours_end); const Span *theirs_begin, *theirs_end; diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index b7b529f74a344..be046db3c8092 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -58,7 +58,15 @@ class DlRegion { /// Returns whether this region intersects with another region. bool intersects(const DlRegion& region) const; - ~DlRegion(); + /// Returns true if region is empty (contains no rectangles). + bool isEmpty() const { return lines_.empty(); } + + /// Returns true if region is not empty and contains more than one rectangle. + bool isComplex() const; + + /// Returns true if region can be represented by single rectangle (or is + /// empty). + bool isSimple() const { return !isComplex(); } private: typedef std::uint32_t SpanChunkHandle; @@ -104,10 +112,6 @@ class DlRegion { Span* spans_ = nullptr; }; - bool isEmpty() const { return lines_.empty(); } - bool isComplex() const; - bool isSimple() const { return !isComplex(); } - struct SpanLine { int32_t top; int32_t bottom; diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index 2ad4a54ab0f22..47d9d463ec7bd 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -13,7 +13,7 @@ namespace flutter { namespace testing { TEST(DisplayListRegion, EmptyRegion) { - DlRegion region(std::vector{}); + DlRegion region; EXPECT_TRUE(region.getRects().empty()); } @@ -256,6 +256,7 @@ TEST(DisplayListRegion, Union1) { }); DlRegion u = DlRegion::MakeUnion(region1, region2); EXPECT_EQ(u.bounds(), SkIRect::MakeXYWH(0, 0, 40, 40)); + EXPECT_TRUE(u.isSimple()); auto rects = u.getRects(); std::vector expected{ SkIRect::MakeXYWH(0, 0, 40, 40), // @@ -309,8 +310,7 @@ TEST(DisplayListRegion, UnionEmpty) { DlRegion u = DlRegion::MakeUnion(region1, region2); EXPECT_EQ(u.bounds(), SkIRect::MakeEmpty()); auto rects = u.getRects(); - std::vector expected{}; - EXPECT_EQ(rects, expected); + EXPECT_TRUE(rects.empty()); } { DlRegion region1(std::vector{}); From 47a257d6e9227294f075949685c735f86b75429c Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 24 Jun 2023 11:47:19 +0200 Subject: [PATCH 28/39] Use OrderedSpanAccumulator --- display_list/geometry/dl_region.cc | 87 ++++++++++++------------------ 1 file changed, 34 insertions(+), 53 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index f86f6e00f352f..d1cdab3e956bf 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -134,6 +134,28 @@ size_t DlRegion::unionLineSpans(std::vector& res, SpanChunkHandle a_handle, const SpanBuffer& b_buffer, SpanChunkHandle b_handle) { + class OrderedSpanAccumulator { + public: + explicit OrderedSpanAccumulator(std::vector& res) : res(res) {} + + void accumulate(const Span& span) { + if (span.left > last_ || len == 0) { + res[len++] = span; + last_ = span.right; + } else if (span.right > last_) { + FML_DCHECK(len > 0); + res[len - 1].right = span.right; + last_ = span.right; + } + } + + size_t len = 0; + std::vector& res; + + private: + int32_t last_ = std::numeric_limits::min(); + }; + const Span *begin1, *end1; a_buffer.getSpans(a_handle, begin1, end1); @@ -145,77 +167,36 @@ size_t DlRegion::unionLineSpans(std::vector& res, res.resize(min_size); } - // Pointer to the next span to be written. - Span* new_span = res.data(); + OrderedSpanAccumulator accumulator(res); while (true) { - if (begin1->right < begin2->left) { - *new_span++ = *begin1++; + if (begin1->left < begin2->left) { + accumulator.accumulate(*begin1++); if (begin1 == end1) { break; } - } else if (begin2->right < begin1->left) { - *new_span++ = *begin2++; + } else { + // Either 2 is first, or they are equal, in which case add 2 now + // and we might combine 1 with it next time around + accumulator.accumulate(*begin2++); if (begin2 == end2) { break; } - } else { - break; - } - } - - Span current_span{0, 0}; - while (begin1 != end1 && begin2 != end2) { - if (current_span.left == current_span.right) { - if (begin1->right < begin2->left) { - *new_span++ = *begin1++; - } else if (begin2->right < begin1->left) { - *new_span++ = *begin2++; - } else { - current_span = {std::min(begin1->left, begin2->left), - std::max(begin1->right, begin2->right)}; - begin1++; - begin2++; - } - } else if (current_span.right >= begin1->left) { - current_span.right = std::max(current_span.right, begin1->right); - ++begin1; - } else if (current_span.right >= begin2->left) { - current_span.right = std::max(current_span.right, begin2->right); - ++begin2; - } else { - *new_span++ = current_span; - current_span.left = current_span.right = 0; } } FML_DCHECK(begin1 == end1 || begin2 == end2); - if (current_span.left != current_span.right) { - while (begin1 != end1 && current_span.right >= begin1->left) { - current_span.right = std::max(current_span.right, begin1->right); - ++begin1; - } - while (begin2 != end2 && current_span.right >= begin2->left) { - current_span.right = std::max(current_span.right, begin2->right); - ++begin2; - } - - *new_span = current_span; - ++new_span; + while (begin1 < end1) { + accumulator.accumulate(*begin1++); } - - // At most one of these loops will execute - while (begin1 != end1) { - *new_span++ = *begin1++; - } - while (begin2 != end2) { - *new_span++ = *begin2++; + while (begin2 < end2) { + accumulator.accumulate(*begin2++); } FML_DCHECK(begin1 == end1 && begin2 == end2); - return new_span - res.data(); + return accumulator.len; } size_t DlRegion::intersectLineSpans(std::vector& res, From a274cec3c928402e15a23827b94432e28d922c48 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 24 Jun 2023 11:47:54 +0200 Subject: [PATCH 29/39] Replace upper_bound with lower_bound --- display_list/geometry/dl_region.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index d1cdab3e956bf..0b2935ec0421d 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -630,9 +630,9 @@ bool DlRegion::intersects(const SkIRect& rect) const { return false; } - auto it = std::upper_bound( + auto it = std::lower_bound( lines_.begin(), lines_.end(), rect.fTop, - [](int32_t i, const SpanLine& line) { return i < line.bottom; }); + [](const SpanLine& line, int32_t i) { return line.bottom <= i; }); while (it != lines_.end() && it->top < rect.fBottom) { FML_DCHECK(rect.fTop < it->bottom && it->top < rect.fBottom); From 3ccbf2d918131ea5c887b8c8722e8c02a975659e Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 24 Jun 2023 14:22:47 +0200 Subject: [PATCH 30/39] Fix comment. --- display_list/geometry/dl_region.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index be046db3c8092..de92229fd9dec 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -64,8 +64,8 @@ class DlRegion { /// Returns true if region is not empty and contains more than one rectangle. bool isComplex() const; - /// Returns true if region can be represented by single rectangle (or is - /// empty). + /// Returns true if region can be represented by single rectangle or is + /// empty. bool isSimple() const { return !isComplex(); } private: From 9326b4fa8d8d556e567fc1f3d3a631123bd922f2 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 24 Jun 2023 17:28:07 +0200 Subject: [PATCH 31/39] i => top --- display_list/geometry/dl_region.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 0b2935ec0421d..2a100498b3484 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -632,7 +632,7 @@ bool DlRegion::intersects(const SkIRect& rect) const { auto it = std::lower_bound( lines_.begin(), lines_.end(), rect.fTop, - [](const SpanLine& line, int32_t i) { return line.bottom <= i; }); + [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); while (it != lines_.end() && it->top < rect.fBottom) { FML_DCHECK(rect.fTop < it->bottom && it->top < rect.fBottom); From ca48f44a0a668b4e1cbd4c0c603eb95709846773 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Jun 2023 18:59:18 +0200 Subject: [PATCH 32/39] Use assymetric sizes when unit testing --- display_list/geometry/dl_region_unittests.cc | 122 +++++++++---------- 1 file changed, 60 insertions(+), 62 deletions(-) diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index 47d9d463ec7bd..17145758c137b 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -358,73 +358,71 @@ void CheckEquality(const DlRegion& dl_region, const SkRegion& sk_region) { TEST(DisplayListRegion, TestAgainstSkRegion) { struct Settings { int max_size; - size_t iteration_count; }; - std::vector all_settings{ - {100, 1}, // - {100, 10}, // - {100, 100}, // - {100, 1000}, // - {400, 10}, // - {400, 100}, // - {400, 1000}, // - {800, 10}, // - {800, 100}, // - {800, 1000}, - }; - - for (const auto& settings : all_settings) { - std::random_device d; - std::seed_seq seed{::testing::UnitTest::GetInstance()->random_seed()}; - std::mt19937 rng(seed); - - SkRegion sk_region1; - SkRegion sk_region2; + std::vector all_settings{{100}, {400}, {800}}; - std::uniform_int_distribution pos(0, 4000); - std::uniform_int_distribution size(1, settings.max_size); - - std::vector rects_in1; - std::vector rects_in2; - - for (size_t i = 0; i < settings.iteration_count; ++i) { - SkIRect rect = - SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); - rects_in1.push_back(rect); - rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); - rects_in2.push_back(rect); - } + std::vector iterations{1, 10, 100, 1000}; - DlRegion region1(rects_in1); - sk_region1.setRects(rects_in1.data(), rects_in1.size()); - CheckEquality(region1, sk_region1); - - DlRegion region2(rects_in2); - sk_region2.setRects(rects_in2.data(), rects_in2.size()); - CheckEquality(region2, sk_region2); - - auto intersects_1 = region1.intersects(region2); - auto intersects_2 = region2.intersects(region1); - auto sk_intesects = sk_region1.intersects(sk_region2); - EXPECT_EQ(intersects_1, intersects_2); - EXPECT_EQ(intersects_1, sk_intesects); - - { - auto rects = region2.getRects(true); - for (const auto& r : rects) { - EXPECT_EQ(region1.intersects(r), sk_region1.intersects(r)); + for (const auto& settings : all_settings) { + for (const auto iterations_1 : iterations) { + for (const auto iterations_2 : iterations) { + std::random_device d; + std::seed_seq seed{::testing::UnitTest::GetInstance()->random_seed()}; + std::mt19937 rng(seed); + + SkRegion sk_region1; + SkRegion sk_region2; + + std::uniform_int_distribution pos(0, 4000); + std::uniform_int_distribution size(1, settings.max_size); + + std::vector rects_in1; + std::vector rects_in2; + + for (size_t i = 0; i < iterations_1; ++i) { + SkIRect rect = + SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects_in1.push_back(rect); + } + + for (size_t i = 0; i < iterations_2; ++i) { + SkIRect rect = + SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); + rects_in2.push_back(rect); + } + + DlRegion region1(rects_in1); + sk_region1.setRects(rects_in1.data(), rects_in1.size()); + CheckEquality(region1, sk_region1); + + DlRegion region2(rects_in2); + sk_region2.setRects(rects_in2.data(), rects_in2.size()); + CheckEquality(region2, sk_region2); + + auto intersects_1 = region1.intersects(region2); + auto intersects_2 = region2.intersects(region1); + auto sk_intesects = sk_region1.intersects(sk_region2); + EXPECT_EQ(intersects_1, intersects_2); + EXPECT_EQ(intersects_1, sk_intesects); + + { + auto rects = region2.getRects(true); + for (const auto& r : rects) { + EXPECT_EQ(region1.intersects(r), sk_region1.intersects(r)); + } + } + + DlRegion dl_union = DlRegion::MakeUnion(region1, region2); + SkRegion sk_union(sk_region1); + sk_union.op(sk_region2, SkRegion::kUnion_Op); + CheckEquality(dl_union, sk_union); + + DlRegion dl_intersection = DlRegion::MakeIntersection(region1, region2); + SkRegion sk_intersection(sk_region1); + sk_intersection.op(sk_region2, SkRegion::kIntersect_Op); + CheckEquality(dl_intersection, sk_intersection); } } - - DlRegion dl_union = DlRegion::MakeUnion(region1, region2); - SkRegion sk_union(sk_region1); - sk_union.op(sk_region2, SkRegion::kUnion_Op); - CheckEquality(dl_union, sk_union); - - DlRegion dl_intersection = DlRegion::MakeIntersection(region1, region2); - SkRegion sk_intersection(sk_region1); - sk_intersection.op(sk_region2, SkRegion::kIntersect_Op); - CheckEquality(dl_intersection, sk_intersection); } } From e7e4fa46e17085ad766c782debd59b64cb4b5113 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Jun 2023 19:00:06 +0200 Subject: [PATCH 33/39] Benchmark speed on assymetric regions (one significantly larger than the other) --- .../benchmarking/dl_region_benchmarks.cc | 347 ++++++++++++++---- 1 file changed, 280 insertions(+), 67 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index 717f741e15d45..c565e464ebeb4 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -5,12 +5,49 @@ #include "flutter/benchmarking/benchmarking.h" #include "flutter/display_list/geometry/dl_region.h" +#include "flutter/fml/logging.h" #include "third_party/skia/include/core/SkRegion.h" #include namespace { +template +std::vector GenerateRects(RNG& rng, + const SkIRect& bounds, + int numRects, + int maxSize) { + auto max_size_x = std::min(maxSize, bounds.width()); + auto max_size_y = std::min(maxSize, bounds.height()); + + std::uniform_int_distribution pos_x(bounds.fLeft, bounds.fRight - max_size_x); + std::uniform_int_distribution pos_y(bounds.fTop, bounds.fBottom - max_size_y); + std::uniform_int_distribution size_x(1, max_size_x); + std::uniform_int_distribution size_y(1, max_size_y); + + std::vector rects; + for (int i = 0; i < numRects; ++i) { + SkIRect rect = + SkIRect::MakeXYWH(pos_x(rng), pos_y(rng), size_x(rng), size_y(rng)); + rects.push_back(rect); + } + return rects; +} + +template +SkIRect RandomSubRect(RNG& rng, const SkIRect& rect, double size_factor) { + FML_DCHECK(size_factor <= 1); + + int32_t width = rect.width() * size_factor; + int32_t height = rect.height() * size_factor; + + std::uniform_int_distribution pos_x(0, rect.width() - width); + std::uniform_int_distribution pos_y(0, rect.height() - height); + + return SkIRect::MakeXYWH(rect.fLeft + pos_x(rng), rect.fTop + pos_y(rng), + width, height); +} + class SkRegionAdapter { public: explicit SkRegionAdapter(const std::vector& rects) { @@ -135,27 +172,20 @@ template void RunRegionOpBenchmark(benchmark::State& state, RegionOp op, bool withSingleRect, - int maxSize) { + int maxSize, + double sizeFactor) { std::random_device d; std::seed_seq seed{2, 1, 3}; std::mt19937 rng(seed); - std::uniform_int_distribution pos(0, 4000); - std::uniform_int_distribution size(1, maxSize); - - std::vector rects; - for (int i = 0; i < 500; ++i) { - SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); - rects.push_back(rect); - } + SkIRect bounds1 = SkIRect::MakeWH(4000, 4000); + SkIRect bounds2 = RandomSubRect(rng, bounds1, sizeFactor); + auto rects = GenerateRects(rng, bounds1, 500, maxSize); Region region1(rects); - rects.clear(); - for (int i = 0; i < (withSingleRect ? 1 : 500); ++i) { - SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); - rects.push_back(rect); - } + rects = GenerateRects(rng, bounds2, withSingleRect ? 1 : 500 * sizeFactor, + maxSize); Region region2(rects); switch (op) { @@ -173,26 +203,20 @@ void RunRegionOpBenchmark(benchmark::State& state, } template -void RunIntersectsRegionBenchmark(benchmark::State& state, int maxSize) { +void RunIntersectsRegionBenchmark(benchmark::State& state, + int maxSize, + double sizeFactor) { std::random_device d; std::seed_seq seed{2, 1, 3}; std::mt19937 rng(seed); - std::uniform_int_distribution pos(0, 4000); - std::uniform_int_distribution size(1, maxSize); + SkIRect bounds1 = SkIRect::MakeWH(4000, 4000); + SkIRect bounds2 = RandomSubRect(rng, bounds1, sizeFactor); - std::vector rects; - for (int i = 0; i < 500; ++i) { - SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); - rects.push_back(rect); - } + auto rects = GenerateRects(rng, bounds1, 500, maxSize); Region region1(rects); - rects.clear(); - for (int i = 0; i < 500; ++i) { - SkIRect rect = SkIRect::MakeXYWH(pos(rng), pos(rng), size(rng), size(rng)); - rects.push_back(rect); - } + rects = GenerateRects(rng, bounds2, 500 * sizeFactor, maxSize); Region region2(rects); while (state.KeepRunning()) { @@ -252,23 +276,31 @@ static void BM_SkRegion_GetRects(benchmark::State& state, int maxSize) { static void BM_DlRegion_Operation(benchmark::State& state, RegionOp op, bool withSingleRect, - int maxSize) { - RunRegionOpBenchmark(state, op, withSingleRect, maxSize); + int maxSize, + double sizeFactor) { + RunRegionOpBenchmark(state, op, withSingleRect, maxSize, + sizeFactor); } static void BM_SkRegion_Operation(benchmark::State& state, RegionOp op, bool withSingleRect, - int maxSize) { - RunRegionOpBenchmark(state, op, withSingleRect, maxSize); + int maxSize, + double sizeFactor) { + RunRegionOpBenchmark(state, op, withSingleRect, maxSize, + sizeFactor); } -static void BM_DlRegion_IntersectsRegion(benchmark::State& state, int maxSize) { - RunIntersectsRegionBenchmark(state, maxSize); +static void BM_DlRegion_IntersectsRegion(benchmark::State& state, + int maxSize, + double sizeFactor) { + RunIntersectsRegionBenchmark(state, maxSize, sizeFactor); } -static void BM_SkRegion_IntersectsRegion(benchmark::State& state, int maxSize) { - RunIntersectsRegionBenchmark(state, maxSize); +static void BM_SkRegion_IntersectsRegion(benchmark::State& state, + int maxSize, + double sizeFactor) { + RunIntersectsRegionBenchmark(state, maxSize, sizeFactor); } static void BM_DlRegion_IntersectsSingleRect(benchmark::State& state, @@ -281,6 +313,8 @@ static void BM_SkRegion_IntersectsSingleRect(benchmark::State& state, RunIntersectsSingleRectBenchmark(state, maxSize); } +const double kSizeFactorSmall = 0.3; + BENCHMARK_CAPTURE(BM_DlRegion_IntersectsSingleRect, Tiny, 30) ->Unit(benchmark::kNanosecond); BENCHMARK_CAPTURE(BM_SkRegion_IntersectsSingleRect, Tiny, 30) @@ -298,168 +332,347 @@ BENCHMARK_CAPTURE(BM_DlRegion_IntersectsSingleRect, Large, 1500) BENCHMARK_CAPTURE(BM_SkRegion_IntersectsSingleRect, Large, 1500) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Tiny, 30) +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Tiny, 30, 1.0) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Tiny, 30, 1.0) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Small, 100, 1.0) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Small, 100, 1.0) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Medium, 400, 1.0) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Medium, 400, 1.0) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Large, 1500, 1.0) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Large, 1500, 1.0) + ->Unit(benchmark::kNanosecond); + +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, + TinyAsymmetric, + 30, + kSizeFactorSmall) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Tiny, 30) +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, + TinyAsymmetric, + 30, + kSizeFactorSmall) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Small, 100) +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, + SmallAsymmetric, + 100, + kSizeFactorSmall) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Small, 100) +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, + SmallAsymmetric, + 100, + kSizeFactorSmall) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Medium, 400) +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, + MediumAsymmetric, + 400, + kSizeFactorSmall) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Medium, 400) +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, + MediumAsymmetric, + 400, + kSizeFactorSmall) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, Large, 1500) +BENCHMARK_CAPTURE(BM_DlRegion_IntersectsRegion, + LargeAsymmetric, + 1500, + kSizeFactorSmall) ->Unit(benchmark::kNanosecond); -BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, Large, 1500) +BENCHMARK_CAPTURE(BM_SkRegion_IntersectsRegion, + LargeAsymmetric, + 1500, + kSizeFactorSmall) ->Unit(benchmark::kNanosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Tiny, RegionOp::kUnion, false, - 30) + 30, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Tiny, RegionOp::kUnion, false, - 30) + 30, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Small, RegionOp::kUnion, false, - 100) + 100, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Small, RegionOp::kUnion, false, - 100) + 100, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Medium, RegionOp::kUnion, false, - 400) + 400, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Medium, RegionOp::kUnion, false, - 400) + 400, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Union_Large, RegionOp::kUnion, false, - 1500) + 1500, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Union_Large, RegionOp::kUnion, false, - 1500) + 1500, + 1.0) + ->Unit(benchmark::kMicrosecond); + +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Union_TinyAsymmetric, + RegionOp::kUnion, + false, + 30, + kSizeFactorSmall) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Union_TinyAsymmetric, + RegionOp::kUnion, + false, + 30, + kSizeFactorSmall) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Union_SmallAsymmetric, + RegionOp::kUnion, + false, + 100, + kSizeFactorSmall) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Union_SmallAsymmetric, + RegionOp::kUnion, + false, + 100, + kSizeFactorSmall) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Union_MediumAsymmetric, + RegionOp::kUnion, + false, + 400, + kSizeFactorSmall) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Union_MediumAsymmetric, + RegionOp::kUnion, + false, + 400, + kSizeFactorSmall) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Union_LargeAsymmetric, + RegionOp::kUnion, + false, + 1500, + kSizeFactorSmall) + ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Union_LargeAsymmetric, + RegionOp::kUnion, + false, + 1500, + kSizeFactorSmall) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_Tiny, RegionOp::kIntersection, false, - 30) + 30, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_Tiny, RegionOp::kIntersection, false, - 30) + 30, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_Small, RegionOp::kIntersection, false, - 100) + 100, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_Small, RegionOp::kIntersection, false, - 100) + 100, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_Medium, RegionOp::kIntersection, false, - 400) + 400, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_Medium, RegionOp::kIntersection, false, - 400) + 400, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_Large, RegionOp::kIntersection, false, - 1500) + 1500, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_Large, RegionOp::kIntersection, false, - 1500) + 1500, + 1.0) ->Unit(benchmark::kMicrosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_TinyAssymetric, + RegionOp::kIntersection, + false, + 30, + kSizeFactorSmall) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_TinyAssymetric, + RegionOp::kIntersection, + false, + 30, + kSizeFactorSmall) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_SmallAssymetric, + RegionOp::kIntersection, + false, + 100, + kSizeFactorSmall) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_SmallAssymetric, + RegionOp::kIntersection, + false, + 100, + kSizeFactorSmall) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_MediumAssymetric, + RegionOp::kIntersection, + false, + 400, + kSizeFactorSmall) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_MediumAssymetric, + RegionOp::kIntersection, + false, + 400, + kSizeFactorSmall) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_DlRegion_Operation, + Intersection_LargeAssymetric, + RegionOp::kIntersection, + false, + 1500, + kSizeFactorSmall) + ->Unit(benchmark::kNanosecond); +BENCHMARK_CAPTURE(BM_SkRegion_Operation, + Intersection_LargeAssymetric, + RegionOp::kIntersection, + false, + 1500, + kSizeFactorSmall) + ->Unit(benchmark::kNanosecond); + BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_SingleRect_Tiny, RegionOp::kIntersection, true, - 30) + 30, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_SingleRect_Tiny, RegionOp::kIntersection, true, - 30) + 30, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_SingleRect_Small, RegionOp::kIntersection, true, - 100) + 100, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_SingleRect_Small, RegionOp::kIntersection, true, - 100) + 100, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_SingleRect_Medium, RegionOp::kIntersection, true, - 400) + 400, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_SingleRect_Medium, RegionOp::kIntersection, true, - 400) + 400, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_SingleRect_Large, RegionOp::kIntersection, true, - 1500) + 1500, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_SingleRect_Large, RegionOp::kIntersection, true, - 1500) + 1500, + 1.0) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_FromRects, Tiny, 30) From 134bc4afdc86fb7fd3430f22177263566ec529e3 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Jun 2023 19:00:31 +0200 Subject: [PATCH 34/39] Use binary search for intersection --- display_list/geometry/dl_region.cc | 57 ++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 2a100498b3484..d5901e3af5670 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -8,6 +8,10 @@ namespace flutter { +// Threshold for switching from linear search through span lines to binary +// search. +const int kBinarySearchThreshold = 10; + DlRegion::SpanBuffer::SpanBuffer(DlRegion::SpanBuffer&& m) : capacity_(m.capacity_), size_(m.size_), spans_(m.spans_) { m.size_ = 0; @@ -399,7 +403,11 @@ void DlRegion::appendLine(int32_t top, } DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { - if (a.isSimple() && a.bounds_.contains(b.bounds_)) { + if (a.isEmpty()) { + return b; + } else if (b.isEmpty()) { + return a; + } else if (a.isSimple() && a.bounds_.contains(b.bounds_)) { return a; } else if (b.isSimple() && b.bounds_.contains(a.bounds_)) { return b; @@ -419,6 +427,8 @@ DlRegion DlRegion::MakeUnion(const DlRegion& a, const DlRegion& b) { auto a_end = a.lines_.end(); auto b_end = b.lines_.end(); + FML_DCHECK(a_it != a_end && b_it != b_end); + auto& a_buffer = a.span_buffer_; auto& b_buffer = b.span_buffer_; @@ -511,16 +521,23 @@ DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { auto b_it = b.lines_.begin(); auto b_end = b.lines_.end(); - // When intersecting single rectangle with a complex region use binary - // search to find the first line that intersects the rectangle. - if (b_it == b_end - 1) { - a_it = std::upper_bound( - a_it, a_end, b_it->top, - [](int32_t top, const SpanLine& line) { return top < line.bottom; }); - } else if (a_it == a_end - 1) { - b_it = std::upper_bound( - b_it, b_end, a_it->top, - [](int32_t top, const SpanLine& line) { return top < line.bottom; }); + FML_DCHECK(a_it != a_end && b_it != b_end); + + auto a_len = a_end - a_it; + auto b_len = b_end - b_it; + + if (a_len > kBinarySearchThreshold && + a_it[kBinarySearchThreshold].bottom <= b_it->top) { + a_it = std::lower_bound( + a.lines_.begin() + kBinarySearchThreshold + 1, a.lines_.end(), + b_it->top, + [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); + } else if (b_len > kBinarySearchThreshold && + b_it[kBinarySearchThreshold].bottom <= a_it->top) { + b_it = std::lower_bound( + b.lines_.begin() + kBinarySearchThreshold + 1, b.lines_.end(), + a_it->top, + [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); } auto& a_buffer = a.span_buffer_; @@ -696,6 +713,24 @@ bool DlRegion::intersects(const DlRegion& region) const { auto theirs = region.lines_.begin(); auto theirs_end = region.lines_.end(); + FML_DCHECK(ours != ours_end && theirs != theirs_end); + + auto ours_len = ours_end - ours; + auto their_len = theirs_end - theirs; + + if (ours_len > kBinarySearchThreshold && + ours[kBinarySearchThreshold].bottom <= theirs->top) { + ours = std::lower_bound( + lines_.begin() + kBinarySearchThreshold + 1, lines_.end(), theirs->top, + [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); + } else if (their_len > kBinarySearchThreshold && + theirs[kBinarySearchThreshold].bottom <= ours->top) { + theirs = std::lower_bound( + region.lines_.begin() + kBinarySearchThreshold + 1, region.lines_.end(), + ours->top, + [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); + } + while (ours != ours_end && theirs != theirs_end) { if (ours->bottom <= theirs->top) { ++ours; From 7bd2ae49b01850192c79f0d9e529b7ed8ae9b681 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Jun 2023 19:07:58 +0200 Subject: [PATCH 35/39] Switch units back to microsecond --- .../benchmarking/dl_region_benchmarks.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index c565e464ebeb4..452d0ee0c97a8 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -567,56 +567,56 @@ BENCHMARK_CAPTURE(BM_DlRegion_Operation, false, 30, kSizeFactorSmall) - ->Unit(benchmark::kNanosecond); + ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_TinyAssymetric, RegionOp::kIntersection, false, 30, kSizeFactorSmall) - ->Unit(benchmark::kNanosecond); + ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_SmallAssymetric, RegionOp::kIntersection, false, 100, kSizeFactorSmall) - ->Unit(benchmark::kNanosecond); + ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_SmallAssymetric, RegionOp::kIntersection, false, 100, kSizeFactorSmall) - ->Unit(benchmark::kNanosecond); + ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_MediumAssymetric, RegionOp::kIntersection, false, 400, kSizeFactorSmall) - ->Unit(benchmark::kNanosecond); + ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_MediumAssymetric, RegionOp::kIntersection, false, 400, kSizeFactorSmall) - ->Unit(benchmark::kNanosecond); + ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_LargeAssymetric, RegionOp::kIntersection, false, 1500, kSizeFactorSmall) - ->Unit(benchmark::kNanosecond); + ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, Intersection_LargeAssymetric, RegionOp::kIntersection, false, 1500, kSizeFactorSmall) - ->Unit(benchmark::kNanosecond); + ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, Intersection_SingleRect_Tiny, From 1df34acd0fce40aa6cf661b3bc34c6890653d86c Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Jun 2023 21:04:11 +0200 Subject: [PATCH 36/39] Add isEmpty() checks --- display_list/geometry/dl_region_unittests.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/display_list/geometry/dl_region_unittests.cc b/display_list/geometry/dl_region_unittests.cc index 17145758c137b..1f03f89fc6d5a 100644 --- a/display_list/geometry/dl_region_unittests.cc +++ b/display_list/geometry/dl_region_unittests.cc @@ -14,6 +14,7 @@ namespace testing { TEST(DisplayListRegion, EmptyRegion) { DlRegion region; + EXPECT_TRUE(region.isEmpty()); EXPECT_TRUE(region.getRects().empty()); } @@ -204,6 +205,7 @@ TEST(DisplayListRegion, Intersection1) { }); DlRegion i = DlRegion::MakeIntersection(region1, region2); EXPECT_EQ(i.bounds(), SkIRect::MakeEmpty()); + EXPECT_TRUE(i.isEmpty()); auto rects = i.getRects(); EXPECT_TRUE(rects.empty()); } @@ -309,6 +311,7 @@ TEST(DisplayListRegion, UnionEmpty) { DlRegion region2(std::vector{}); DlRegion u = DlRegion::MakeUnion(region1, region2); EXPECT_EQ(u.bounds(), SkIRect::MakeEmpty()); + EXPECT_TRUE(u.isEmpty()); auto rects = u.getRects(); EXPECT_TRUE(rects.empty()); } From 618c831de99c4aeca67d5db175932afccca4f686 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Jun 2023 21:36:39 +0200 Subject: [PATCH 37/39] Correct spelling --- .../benchmarking/dl_region_benchmarks.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/display_list/benchmarking/dl_region_benchmarks.cc b/display_list/benchmarking/dl_region_benchmarks.cc index 452d0ee0c97a8..bf169baf7db1d 100644 --- a/display_list/benchmarking/dl_region_benchmarks.cc +++ b/display_list/benchmarking/dl_region_benchmarks.cc @@ -562,56 +562,56 @@ BENCHMARK_CAPTURE(BM_SkRegion_Operation, ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, - Intersection_TinyAssymetric, + Intersection_TinyAsymmetric, RegionOp::kIntersection, false, 30, kSizeFactorSmall) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, - Intersection_TinyAssymetric, + Intersection_TinyAsymmetric, RegionOp::kIntersection, false, 30, kSizeFactorSmall) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, - Intersection_SmallAssymetric, + Intersection_SmallAsymmetric, RegionOp::kIntersection, false, 100, kSizeFactorSmall) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, - Intersection_SmallAssymetric, + Intersection_SmallAsymmetric, RegionOp::kIntersection, false, 100, kSizeFactorSmall) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, - Intersection_MediumAssymetric, + Intersection_MediumAsymmetric, RegionOp::kIntersection, false, 400, kSizeFactorSmall) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, - Intersection_MediumAssymetric, + Intersection_MediumAsymmetric, RegionOp::kIntersection, false, 400, kSizeFactorSmall) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_DlRegion_Operation, - Intersection_LargeAssymetric, + Intersection_LargeAsymmetric, RegionOp::kIntersection, false, 1500, kSizeFactorSmall) ->Unit(benchmark::kMicrosecond); BENCHMARK_CAPTURE(BM_SkRegion_Operation, - Intersection_LargeAssymetric, + Intersection_LargeAsymmetric, RegionOp::kIntersection, false, 1500, From 88b501277687aca0e954a0c2975e8a6b041e85fb Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Jun 2023 22:36:07 +0200 Subject: [PATCH 38/39] Extract binary search into separate method --- display_list/geometry/dl_region.cc | 80 +++++++++++++----------------- display_list/geometry/dl_region.h | 6 +++ 2 files changed, 41 insertions(+), 45 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index d5901e3af5670..41c6b715b27de 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -8,10 +8,6 @@ namespace flutter { -// Threshold for switching from linear search through span lines to binary -// search. -const int kBinarySearchThreshold = 10; - DlRegion::SpanBuffer::SpanBuffer(DlRegion::SpanBuffer&& m) : capacity_(m.capacity_), size_(m.size_), spans_(m.spans_) { m.size_ = 0; @@ -516,30 +512,12 @@ DlRegion DlRegion::MakeIntersection(const DlRegion& a, const DlRegion& b) { auto& lines = res.lines_; lines.reserve(std::min(a.lines_.size(), b.lines_.size())); - auto a_it = a.lines_.begin(); + std::vector::const_iterator a_it, b_it; + getIntersectionIterators(a.lines_, b.lines_, a_it, b_it); + auto a_end = a.lines_.end(); - auto b_it = b.lines_.begin(); auto b_end = b.lines_.end(); - FML_DCHECK(a_it != a_end && b_it != b_end); - - auto a_len = a_end - a_it; - auto b_len = b_end - b_it; - - if (a_len > kBinarySearchThreshold && - a_it[kBinarySearchThreshold].bottom <= b_it->top) { - a_it = std::lower_bound( - a.lines_.begin() + kBinarySearchThreshold + 1, a.lines_.end(), - b_it->top, - [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); - } else if (b_len > kBinarySearchThreshold && - b_it[kBinarySearchThreshold].bottom <= a_it->top) { - b_it = std::lower_bound( - b.lines_.begin() + kBinarySearchThreshold + 1, b.lines_.end(), - a_it->top, - [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); - } - auto& a_buffer = a.span_buffer_; auto& b_buffer = b.span_buffer_; @@ -683,6 +661,36 @@ bool DlRegion::spansIntersect(const Span* begin1, return false; } +void DlRegion::getIntersectionIterators( + const std::vector& a_lines, + const std::vector& b_lines, + std::vector::const_iterator& a_it, + std::vector::const_iterator& b_it) { + a_it = a_lines.begin(); + auto a_end = a_lines.end(); + b_it = b_lines.begin(); + auto b_end = b_lines.end(); + + FML_DCHECK(a_it != a_end && b_it != b_end); + + auto a_len = a_end - a_it; + auto b_len = b_end - b_it; + + const int kBinarySearchThreshold = 10; + + if (a_len > kBinarySearchThreshold && + a_it[kBinarySearchThreshold].bottom <= b_it->top) { + a_it = std::lower_bound( + a_lines.begin() + kBinarySearchThreshold + 1, a_lines.end(), b_it->top, + [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); + } else if (b_len > kBinarySearchThreshold && + b_it[kBinarySearchThreshold].bottom <= a_it->top) { + b_it = std::lower_bound( + b_lines.begin() + kBinarySearchThreshold + 1, b_lines.end(), a_it->top, + [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); + } +} + bool DlRegion::intersects(const DlRegion& region) const { if (isEmpty() || region.isEmpty()) { return false; @@ -708,29 +716,11 @@ bool DlRegion::intersects(const DlRegion& region) const { return intersects(region.bounds_); } - auto ours = lines_.begin(); + std::vector::const_iterator ours, theirs; + getIntersectionIterators(lines_, region.lines_, ours, theirs); auto ours_end = lines_.end(); - auto theirs = region.lines_.begin(); auto theirs_end = region.lines_.end(); - FML_DCHECK(ours != ours_end && theirs != theirs_end); - - auto ours_len = ours_end - ours; - auto their_len = theirs_end - theirs; - - if (ours_len > kBinarySearchThreshold && - ours[kBinarySearchThreshold].bottom <= theirs->top) { - ours = std::lower_bound( - lines_.begin() + kBinarySearchThreshold + 1, lines_.end(), theirs->top, - [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); - } else if (their_len > kBinarySearchThreshold && - theirs[kBinarySearchThreshold].bottom <= ours->top) { - theirs = std::lower_bound( - region.lines_.begin() + kBinarySearchThreshold + 1, region.lines_.end(), - ours->top, - [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); - } - while (ours != ours_end && theirs != theirs_end) { if (ours->bottom <= theirs->top) { ++ours; diff --git a/display_list/geometry/dl_region.h b/display_list/geometry/dl_region.h index de92229fd9dec..dd75b35129042 100644 --- a/display_list/geometry/dl_region.h +++ b/display_list/geometry/dl_region.h @@ -157,6 +157,12 @@ class DlRegion { const Span* begin2, const Span* end2); + static void getIntersectionIterators( + const std::vector& a_lines, + const std::vector& b_lines, + std::vector::const_iterator& a_it, + std::vector::const_iterator& b_it); + std::vector lines_; SkIRect bounds_ = SkIRect::MakeEmpty(); SpanBuffer span_buffer_; From 9568dd2b823e4d64db743e7c810cabb3c3649afe Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sun, 25 Jun 2023 22:53:49 +0200 Subject: [PATCH 39/39] Add threshold check for single rect intersect --- display_list/geometry/dl_region.cc | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/display_list/geometry/dl_region.cc b/display_list/geometry/dl_region.cc index 41c6b715b27de..3e900972fd71e 100644 --- a/display_list/geometry/dl_region.cc +++ b/display_list/geometry/dl_region.cc @@ -8,6 +8,10 @@ namespace flutter { +// Threshold for switching from linear search through span lines to binary +// search. +const int kBinarySearchThreshold = 10; + DlRegion::SpanBuffer::SpanBuffer(DlRegion::SpanBuffer&& m) : capacity_(m.capacity_), size_(m.size_), spans_(m.spans_) { m.size_ = 0; @@ -625,11 +629,20 @@ bool DlRegion::intersects(const SkIRect& rect) const { return false; } - auto it = std::lower_bound( - lines_.begin(), lines_.end(), rect.fTop, - [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); - - while (it != lines_.end() && it->top < rect.fBottom) { + auto it = lines_.begin(); + auto end = lines_.end(); + if (lines_.size() > kBinarySearchThreshold && + it[kBinarySearchThreshold].bottom <= rect.fTop) { + it = std::lower_bound( + lines_.begin() + kBinarySearchThreshold + 1, lines_.end(), rect.fTop, + [](const SpanLine& line, int32_t top) { return line.bottom <= top; }); + } else { + while (it != end && it->bottom <= rect.fTop) { + ++it; + continue; + } + } + while (it != end && it->top < rect.fBottom) { FML_DCHECK(rect.fTop < it->bottom && it->top < rect.fBottom); const Span *begin, *end; span_buffer_.getSpans(it->chunk_handle, begin, end); @@ -676,8 +689,6 @@ void DlRegion::getIntersectionIterators( auto a_len = a_end - a_it; auto b_len = b_end - b_it; - const int kBinarySearchThreshold = 10; - if (a_len > kBinarySearchThreshold && a_it[kBinarySearchThreshold].bottom <= b_it->top) { a_it = std::lower_bound(