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

Commit c99eda4

Browse files
authored
[Impeller] Reland 3: Implement draw order optimization. (#54673)
For each clip scope, draw opaque items in reverse order and translucent/backdrop-independent items in their original order afterwards. Clips are treated as translucent by the parent scope. Respects clips, subpass collapse, and the clear color optimization.
1 parent 80c3579 commit c99eda4

17 files changed

+671
-88
lines changed

ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@
168168
../../../flutter/impeller/entity/contents/host_buffer_unittests.cc
169169
../../../flutter/impeller/entity/contents/test
170170
../../../flutter/impeller/entity/contents/tiled_texture_contents_unittests.cc
171+
../../../flutter/impeller/entity/draw_order_resolver_unittests.cc
171172
../../../flutter/impeller/entity/entity_pass_target_unittests.cc
172173
../../../flutter/impeller/entity/entity_pass_unittests.cc
173174
../../../flutter/impeller/entity/entity_unittests.cc

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42831,6 +42831,8 @@ ORIGIN: ../../../flutter/impeller/entity/contents/tiled_texture_contents.cc + ..
4283142831
ORIGIN: ../../../flutter/impeller/entity/contents/tiled_texture_contents.h + ../../../flutter/LICENSE
4283242832
ORIGIN: ../../../flutter/impeller/entity/contents/vertices_contents.cc + ../../../flutter/LICENSE
4283342833
ORIGIN: ../../../flutter/impeller/entity/contents/vertices_contents.h + ../../../flutter/LICENSE
42834+
ORIGIN: ../../../flutter/impeller/entity/draw_order_resolver.cc + ../../../flutter/LICENSE
42835+
ORIGIN: ../../../flutter/impeller/entity/draw_order_resolver.h + ../../../flutter/LICENSE
4283442836
ORIGIN: ../../../flutter/impeller/entity/entity.cc + ../../../flutter/LICENSE
4283542837
ORIGIN: ../../../flutter/impeller/entity/entity.h + ../../../flutter/LICENSE
4283642838
ORIGIN: ../../../flutter/impeller/entity/entity_pass.cc + ../../../flutter/LICENSE
@@ -45714,6 +45716,8 @@ FILE: ../../../flutter/impeller/entity/contents/tiled_texture_contents.cc
4571445716
FILE: ../../../flutter/impeller/entity/contents/tiled_texture_contents.h
4571545717
FILE: ../../../flutter/impeller/entity/contents/vertices_contents.cc
4571645718
FILE: ../../../flutter/impeller/entity/contents/vertices_contents.h
45719+
FILE: ../../../flutter/impeller/entity/draw_order_resolver.cc
45720+
FILE: ../../../flutter/impeller/entity/draw_order_resolver.h
4571745721
FILE: ../../../flutter/impeller/entity/entity.cc
4571845722
FILE: ../../../flutter/impeller/entity/entity.h
4571945723
FILE: ../../../flutter/impeller/entity/entity_pass.cc

impeller/display_list/aiks_dl_unittests.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,5 +890,39 @@ TEST_P(AiksTest, DispatcherDoesNotCullPerspectiveTransformedChildDisplayLists) {
890890
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
891891
}
892892

893+
// Results in a 100x100 green square. If any red is drawn, there is a bug.
894+
TEST_P(AiksTest, BackdropRestoreUsesCorrectCoverageForFirstRestoredClip) {
895+
DisplayListBuilder builder;
896+
897+
DlPaint paint;
898+
// Add a difference clip that cuts out the bottom right corner
899+
builder.ClipRect(SkRect::MakeLTRB(50, 50, 100, 100),
900+
DlCanvas::ClipOp::kDifference);
901+
902+
// Draw a red rectangle that's going to be completely covered by green later.
903+
paint.setColor(DlColor::kRed());
904+
builder.DrawRect(SkRect::MakeLTRB(0, 0, 100, 100), paint);
905+
906+
// Add a clip restricting the backdrop filter to the top right corner.
907+
auto count = builder.GetSaveCount();
908+
builder.Save();
909+
{
910+
builder.ClipRect(SkRect::MakeLTRB(0, 0, 100, 100));
911+
{
912+
// Create a save layer with a backdrop blur filter.
913+
auto backdrop_filter =
914+
DlBlurImageFilter::Make(10.0, 10.0, DlTileMode::kDecal);
915+
builder.SaveLayer(nullptr, nullptr, backdrop_filter.get());
916+
}
917+
}
918+
builder.RestoreToCount(count);
919+
920+
// Finally, overwrite all the previous stuff with green.
921+
paint.setColor(DlColor::kGreen());
922+
builder.DrawRect(SkRect::MakeLTRB(0, 0, 100, 100), paint);
923+
924+
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
925+
}
926+
893927
} // namespace testing
894928
} // namespace impeller

impeller/entity/BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ impeller_component("entity") {
166166
"contents/tiled_texture_contents.h",
167167
"contents/vertices_contents.cc",
168168
"contents/vertices_contents.h",
169+
"draw_order_resolver.cc",
170+
"draw_order_resolver.h",
169171
"entity.cc",
170172
"entity.h",
171173
"entity_pass.cc",
@@ -250,6 +252,7 @@ impeller_component("entity_unittests") {
250252
"contents/filters/matrix_filter_contents_unittests.cc",
251253
"contents/host_buffer_unittests.cc",
252254
"contents/tiled_texture_contents_unittests.cc",
255+
"draw_order_resolver_unittests.cc",
253256
"entity_pass_target_unittests.cc",
254257
"entity_pass_unittests.cc",
255258
"entity_playground.cc",

impeller/entity/contents/color_source_contents.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ class ColorSourceContents : public Contents {
213213
pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
214214
options.primitive_type = geometry_result.type;
215215

216+
// Enable depth writing for all opaque entities in order to allow
217+
// reordering. Opaque entities are coerced to source blending by
218+
// `EntityPass::AddEntity`.
219+
options.depth_write_enabled = options.blend_mode == BlendMode::kSource;
220+
216221
// Take the pre-populated vertex shader uniform struct and set managed
217222
// values.
218223
frame_info.mvp = geometry_result.transform;

impeller/entity/contents/filters/gaussian_blur_filter_contents.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -575,16 +575,16 @@ Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style,
575575
const ContentContext& renderer, const Entity& entity,
576576
RenderPass& pass) mutable {
577577
bool result = true;
578-
blur_entity.SetClipDepth(entity.GetClipDepth());
579-
blur_entity.SetTransform(entity.GetTransform() *
580-
blurred_transform);
581-
result = result && blur_entity.Render(renderer, pass);
582578
snapshot_entity.SetTransform(
583579
entity.GetTransform() *
584580
Matrix::MakeScale(1.f / source_space_scalar) *
585581
snapshot_transform);
586582
snapshot_entity.SetClipDepth(entity.GetClipDepth());
587583
result = result && snapshot_entity.Render(renderer, pass);
584+
blur_entity.SetClipDepth(entity.GetClipDepth());
585+
blur_entity.SetTransform(entity.GetTransform() *
586+
blurred_transform);
587+
result = result && blur_entity.Render(renderer, pass);
588588
return result;
589589
}),
590590
fml::MakeCopyable([blur_entity = blur_entity.Clone(),

impeller/entity/contents/texture_contents.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ bool TextureContents::Render(const ContentContext& renderer,
153153
}
154154
pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;
155155

156+
pipeline_options.depth_write_enabled =
157+
stencil_enabled_ && pipeline_options.blend_mode == BlendMode::kSource;
158+
156159
pass.SetPipeline(strict_source_rect_enabled_
157160
? renderer.GetTextureStrictSrcPipeline(pipeline_options)
158161
: renderer.GetTexturePipeline(pipeline_options));
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/entity/draw_order_resolver.h"
6+
7+
#include "flutter/fml/logging.h"
8+
#include "impeller/base/validation.h"
9+
10+
namespace impeller {
11+
12+
DrawOrderResolver::DrawOrderResolver() : draw_order_layers_({{}}){};
13+
14+
void DrawOrderResolver::AddElement(size_t element_index, bool is_opaque) {
15+
DrawOrderLayer& layer = draw_order_layers_.back();
16+
if (is_opaque) {
17+
layer.opaque_elements.push_back(element_index);
18+
} else {
19+
layer.dependent_elements.push_back(element_index);
20+
}
21+
}
22+
void DrawOrderResolver::PushClip(size_t element_index) {
23+
draw_order_layers_.back().dependent_elements.push_back(element_index);
24+
draw_order_layers_.push_back({});
25+
};
26+
27+
void DrawOrderResolver::PopClip() {
28+
if (draw_order_layers_.size() == 1u) {
29+
// This is likely recoverable, so don't assert.
30+
VALIDATION_LOG << "Attemped to pop the first draw order clip layer. This "
31+
"may be a bug in `EntityPass`.";
32+
return;
33+
}
34+
35+
DrawOrderLayer& layer = draw_order_layers_.back();
36+
DrawOrderLayer& parent_layer =
37+
draw_order_layers_[draw_order_layers_.size() - 2];
38+
39+
layer.WriteCombinedDraws(parent_layer.dependent_elements, 0, 0);
40+
41+
draw_order_layers_.pop_back();
42+
}
43+
44+
void DrawOrderResolver::Flush() {
45+
FML_DCHECK(draw_order_layers_.size() >= 1u);
46+
47+
size_t layer_count = draw_order_layers_.size();
48+
49+
// Pop all clip layers.
50+
while (draw_order_layers_.size() > 1u) {
51+
PopClip();
52+
}
53+
54+
// Move the root layer items into the sorted list.
55+
DrawOrderLayer& layer = draw_order_layers_.back();
56+
if (!first_root_flush_.has_value()) {
57+
// Record the first flush.
58+
first_root_flush_ = std::move(layer);
59+
layer = {};
60+
} else {
61+
// Write subsequent flushes into the sorted root list.
62+
layer.WriteCombinedDraws(sorted_elements_, 0, 0);
63+
layer.opaque_elements.clear();
64+
layer.dependent_elements.clear();
65+
}
66+
67+
// Refill with empty layers.
68+
draw_order_layers_.resize(layer_count);
69+
}
70+
71+
DrawOrderResolver::ElementRefs DrawOrderResolver::GetSortedDraws(
72+
size_t opaque_skip_count,
73+
size_t translucent_skip_count) const {
74+
FML_DCHECK(draw_order_layers_.size() == 1u)
75+
<< "Attempted to get sorted draws before all clips were popped.";
76+
77+
ElementRefs sorted_elements;
78+
sorted_elements.reserve(
79+
(first_root_flush_.has_value()
80+
? first_root_flush_->opaque_elements.size() +
81+
first_root_flush_->dependent_elements.size()
82+
: 0u) +
83+
sorted_elements_.size() +
84+
draw_order_layers_.back().opaque_elements.size() +
85+
draw_order_layers_.back().dependent_elements.size());
86+
87+
// Write all flushed items.
88+
if (first_root_flush_.has_value()) {
89+
first_root_flush_->WriteCombinedDraws(sorted_elements, opaque_skip_count,
90+
translucent_skip_count);
91+
}
92+
sorted_elements.insert(sorted_elements.end(), sorted_elements_.begin(),
93+
sorted_elements_.end());
94+
95+
// Write any remaining non-flushed items.
96+
draw_order_layers_.back().WriteCombinedDraws(
97+
sorted_elements, first_root_flush_.has_value() ? 0 : opaque_skip_count,
98+
first_root_flush_.has_value() ? 0 : translucent_skip_count);
99+
100+
return sorted_elements;
101+
}
102+
103+
void DrawOrderResolver::DrawOrderLayer::WriteCombinedDraws(
104+
ElementRefs& destination,
105+
size_t opaque_skip_count,
106+
size_t translucent_skip_count) const {
107+
FML_DCHECK(opaque_skip_count <= opaque_elements.size());
108+
FML_DCHECK(translucent_skip_count <= dependent_elements.size());
109+
110+
destination.reserve(destination.size() + //
111+
opaque_elements.size() - opaque_skip_count + //
112+
dependent_elements.size() - translucent_skip_count);
113+
114+
// Draw backdrop-independent elements in reverse order first.
115+
destination.insert(destination.end(), opaque_elements.rbegin(),
116+
opaque_elements.rend() - opaque_skip_count);
117+
// Then, draw backdrop-dependent elements in their original order.
118+
destination.insert(destination.end(),
119+
dependent_elements.begin() + translucent_skip_count,
120+
dependent_elements.end());
121+
}
122+
123+
} // namespace impeller

impeller/entity/draw_order_resolver.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_IMPELLER_ENTITY_DRAW_ORDER_RESOLVER_H_
6+
#define FLUTTER_IMPELLER_ENTITY_DRAW_ORDER_RESOLVER_H_
7+
8+
#include <optional>
9+
#include <vector>
10+
11+
namespace impeller {
12+
13+
/// Helper that records draw indices in painter's order and sorts the draws into
14+
/// an optimized order based on translucency and clips.
15+
class DrawOrderResolver {
16+
public:
17+
using ElementRefs = std::vector<size_t>;
18+
19+
DrawOrderResolver();
20+
21+
void AddElement(size_t element_index, bool is_opaque);
22+
23+
void PushClip(size_t element_index);
24+
25+
void PopClip();
26+
27+
void Flush();
28+
29+
//-------------------------------------------------------------------------
30+
/// @brief Returns the sorted draws for the current draw order layer.
31+
/// This should only be called after all recording has finished.
32+
///
33+
/// @param[in] opaque_skip_count The number of opaque elements to skip
34+
/// when appending the combined elements.
35+
/// This is used for the "clear color"
36+
/// optimization.
37+
/// @param[in] translucent_skip_count The number of translucent elements to
38+
/// skip when appending the combined
39+
/// elements. This is used for the
40+
/// "clear color" optimization.
41+
///
42+
ElementRefs GetSortedDraws(size_t opaque_skip_count,
43+
size_t translucent_skip_count) const;
44+
45+
private:
46+
/// A data structure for collecting sorted draws for a given "draw order
47+
/// layer". Currently these layers just correspond to the local clip stack.
48+
struct DrawOrderLayer {
49+
/// The list of backdrop-independent elements (always just opaque). These
50+
/// are order independent, and so we render these elements in reverse
51+
/// painter's order so that they cull one another.
52+
ElementRefs opaque_elements;
53+
54+
/// The list of backdrop-dependent elements with respect to this draw
55+
/// order layer. These elements are drawn after all of the independent
56+
/// elements.
57+
ElementRefs dependent_elements;
58+
59+
//-----------------------------------------------------------------------
60+
/// @brief Appends the combined opaque and transparent elements into
61+
/// a final destination buffer.
62+
///
63+
/// @param[in] destination The buffer to append the combined
64+
/// elements to.
65+
/// @param[in] opaque_skip_count The number of opaque elements to
66+
/// skip when appending the combined
67+
/// elements. This is used for the
68+
/// "clear color" optimization.
69+
/// @param[in] translucent_skip_count The number of translucent elements
70+
/// to skip when appending the combined
71+
/// elements. This is used for the
72+
/// "clear color" optimization.
73+
///
74+
void WriteCombinedDraws(ElementRefs& destination,
75+
size_t opaque_skip_count,
76+
size_t translucent_skip_count) const;
77+
};
78+
std::vector<DrawOrderLayer> draw_order_layers_;
79+
80+
// The first time the root layer is flushed, the layer contents are stored
81+
// here. This is done to enable element skipping for the clear color
82+
// optimization.
83+
std::optional<DrawOrderLayer> first_root_flush_;
84+
// All subsequent root flushes are stored here.
85+
ElementRefs sorted_elements_;
86+
87+
DrawOrderResolver(const DrawOrderResolver&) = delete;
88+
89+
DrawOrderResolver& operator=(const DrawOrderResolver&) = delete;
90+
};
91+
92+
} // namespace impeller
93+
94+
#endif // FLUTTER_IMPELLER_ENTITY_DRAW_ORDER_RESOLVER_H_

0 commit comments

Comments
 (0)