Skip to content

Commit

Permalink
[Impeller] Reland 2: Implement draw order optimization. (#54268)
Browse files Browse the repository at this point in the history
This time for sure!

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.

Attempt 1: #54136
Revert 1: #54067
Attempt 2: #54215
Revert 2: #54261
  • Loading branch information
bdero authored Aug 1, 2024
1 parent f546fef commit 4dc94d6
Show file tree
Hide file tree
Showing 15 changed files with 635 additions and 88 deletions.
1 change: 1 addition & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
../../../flutter/impeller/entity/contents/host_buffer_unittests.cc
../../../flutter/impeller/entity/contents/test
../../../flutter/impeller/entity/contents/tiled_texture_contents_unittests.cc
../../../flutter/impeller/entity/draw_order_resolver_unittests.cc
../../../flutter/impeller/entity/entity_pass_target_unittests.cc
../../../flutter/impeller/entity/entity_pass_unittests.cc
../../../flutter/impeller/entity/entity_unittests.cc
Expand Down
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -42128,6 +42128,8 @@ ORIGIN: ../../../flutter/impeller/entity/contents/tiled_texture_contents.cc + ..
ORIGIN: ../../../flutter/impeller/entity/contents/tiled_texture_contents.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/vertices_contents.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/contents/vertices_contents.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/draw_order_resolver.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/draw_order_resolver.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/entity/entity_pass.cc + ../../../flutter/LICENSE
Expand Down Expand Up @@ -45009,6 +45011,8 @@ FILE: ../../../flutter/impeller/entity/contents/tiled_texture_contents.cc
FILE: ../../../flutter/impeller/entity/contents/tiled_texture_contents.h
FILE: ../../../flutter/impeller/entity/contents/vertices_contents.cc
FILE: ../../../flutter/impeller/entity/contents/vertices_contents.h
FILE: ../../../flutter/impeller/entity/draw_order_resolver.cc
FILE: ../../../flutter/impeller/entity/draw_order_resolver.h
FILE: ../../../flutter/impeller/entity/entity.cc
FILE: ../../../flutter/impeller/entity/entity.h
FILE: ../../../flutter/impeller/entity/entity_pass.cc
Expand Down
3 changes: 3 additions & 0 deletions impeller/entity/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ impeller_component("entity") {
"contents/tiled_texture_contents.h",
"contents/vertices_contents.cc",
"contents/vertices_contents.h",
"draw_order_resolver.cc",
"draw_order_resolver.h",
"entity.cc",
"entity.h",
"entity_pass.cc",
Expand Down Expand Up @@ -248,6 +250,7 @@ impeller_component("entity_unittests") {
"contents/filters/matrix_filter_contents_unittests.cc",
"contents/host_buffer_unittests.cc",
"contents/tiled_texture_contents_unittests.cc",
"draw_order_resolver_unittests.cc",
"entity_pass_target_unittests.cc",
"entity_pass_unittests.cc",
"entity_playground.cc",
Expand Down
5 changes: 5 additions & 0 deletions impeller/entity/contents/color_source_contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ class ColorSourceContents : public Contents {
pass.SetVertexBuffer(std::move(geometry_result.vertex_buffer));
options.primitive_type = geometry_result.type;

// Enable depth writing for all opaque entities in order to allow
// reordering. Opaque entities are coerced to source blending by
// `EntityPass::AddEntity`.
options.depth_write_enabled = options.blend_mode == BlendMode::kSource;

// Take the pre-populated vertex shader uniform struct and set managed
// values.
frame_info.mvp = geometry_result.transform;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -575,16 +575,16 @@ Entity ApplyBlurStyle(FilterContents::BlurStyle blur_style,
const ContentContext& renderer, const Entity& entity,
RenderPass& pass) mutable {
bool result = true;
blur_entity.SetClipDepth(entity.GetClipDepth());
blur_entity.SetTransform(entity.GetTransform() *
blurred_transform);
result = result && blur_entity.Render(renderer, pass);
snapshot_entity.SetTransform(
entity.GetTransform() *
Matrix::MakeScale(1.f / source_space_scalar) *
snapshot_transform);
snapshot_entity.SetClipDepth(entity.GetClipDepth());
result = result && snapshot_entity.Render(renderer, pass);
blur_entity.SetClipDepth(entity.GetClipDepth());
blur_entity.SetTransform(entity.GetTransform() *
blurred_transform);
result = result && blur_entity.Render(renderer, pass);
return result;
}),
fml::MakeCopyable([blur_entity = blur_entity.Clone(),
Expand Down
3 changes: 3 additions & 0 deletions impeller/entity/contents/texture_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ bool TextureContents::Render(const ContentContext& renderer,
}
pipeline_options.primitive_type = PrimitiveType::kTriangleStrip;

pipeline_options.depth_write_enabled =
stencil_enabled_ && pipeline_options.blend_mode == BlendMode::kSource;

pass.SetPipeline(strict_source_rect_enabled_
? renderer.GetTextureStrictSrcPipeline(pipeline_options)
: renderer.GetTexturePipeline(pipeline_options));
Expand Down
124 changes: 124 additions & 0 deletions impeller/entity/draw_order_resolver.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "impeller/entity/draw_order_resolver.h"

#include "flutter/fml/logging.h"
#include "impeller/base/validation.h"

namespace impeller {

DrawOrderResolver::DrawOrderResolver() : draw_order_layers_({{}}){};

void DrawOrderResolver::AddElement(size_t element_index, bool is_opaque) {
DrawOrderLayer& layer = draw_order_layers_.back();
if (is_opaque) {
layer.opaque_elements.push_back(element_index);
} else {
layer.dependent_elements.push_back(element_index);
}
}
void DrawOrderResolver::PushClip(size_t element_index) {
draw_order_layers_.back().dependent_elements.push_back(element_index);
draw_order_layers_.push_back({});
};

void DrawOrderResolver::PopClip() {
if (draw_order_layers_.size() == 1u) {
// This is likely recoverable, so don't assert.
VALIDATION_LOG
<< "Attemped to pop the first draw order clip layer. This is a bug in "
"`EntityPass`.";
return;
}

DrawOrderLayer& layer = draw_order_layers_.back();
DrawOrderLayer& parent_layer =
draw_order_layers_[draw_order_layers_.size() - 2];

layer.WriteCombinedDraws(parent_layer.dependent_elements, 0, 0);

draw_order_layers_.pop_back();
}

void DrawOrderResolver::Flush() {
FML_DCHECK(draw_order_layers_.size() >= 1u);

size_t layer_count = draw_order_layers_.size();

// Pop all clip layers.
while (draw_order_layers_.size() > 1u) {
PopClip();
}

// Move the root layer items into the sorted list.
DrawOrderLayer& layer = draw_order_layers_.back();
if (!first_root_flush_.has_value()) {
// Record the first flush.
first_root_flush_ = std::move(layer);
layer = {};
} else {
// Write subsequent flushes into the sorted root list.
layer.WriteCombinedDraws(sorted_elements_, 0, 0);
layer.opaque_elements.clear();
layer.dependent_elements.clear();
}

// Refill with empty layers.
draw_order_layers_.resize(layer_count);
}

DrawOrderResolver::ElementRefs DrawOrderResolver::GetSortedDraws(
size_t opaque_skip_count,
size_t translucent_skip_count) const {
FML_DCHECK(draw_order_layers_.size() == 1u)
<< "Attempted to get sorted draws before all clips were popped.";

ElementRefs sorted_elements;
sorted_elements.reserve(
(first_root_flush_.has_value()
? first_root_flush_->opaque_elements.size() +
first_root_flush_->dependent_elements.size()
: 0u) +
sorted_elements_.size() +
draw_order_layers_.back().opaque_elements.size() +
draw_order_layers_.back().dependent_elements.size());

// Write all flushed items.
if (first_root_flush_.has_value()) {
first_root_flush_->WriteCombinedDraws(sorted_elements, opaque_skip_count,
translucent_skip_count);
}
sorted_elements.insert(sorted_elements.end(), sorted_elements_.begin(),
sorted_elements_.end());

// Write any remaining non-flushed items.
draw_order_layers_.back().WriteCombinedDraws(
sorted_elements, first_root_flush_.has_value() ? 0 : opaque_skip_count,
first_root_flush_.has_value() ? 0 : translucent_skip_count);

return sorted_elements;
}

void DrawOrderResolver::DrawOrderLayer::WriteCombinedDraws(
ElementRefs& destination,
size_t opaque_skip_count,
size_t translucent_skip_count) const {
FML_DCHECK(opaque_skip_count <= opaque_elements.size());
FML_DCHECK(translucent_skip_count <= dependent_elements.size());

destination.reserve(destination.size() + //
opaque_elements.size() - opaque_skip_count + //
dependent_elements.size() - translucent_skip_count);

// Draw backdrop-independent elements in reverse order first.
destination.insert(destination.end(), opaque_elements.rbegin(),
opaque_elements.rend() - opaque_skip_count);
// Then, draw backdrop-dependent elements in their original order.
destination.insert(destination.end(),
dependent_elements.begin() + translucent_skip_count,
dependent_elements.end());
}

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

#ifndef FLUTTER_IMPELLER_ENTITY_DRAW_ORDER_RESOLVER_H_
#define FLUTTER_IMPELLER_ENTITY_DRAW_ORDER_RESOLVER_H_

#include <optional>
#include <vector>

namespace impeller {

/// Helper that records draw indices in painter's order and sorts the draws into
/// an optimized order based on translucency and clips.
class DrawOrderResolver {
public:
using ElementRefs = std::vector<size_t>;

DrawOrderResolver();

void AddElement(size_t element_index, bool is_opaque);

void PushClip(size_t element_index);

void PopClip();

void Flush();

//-------------------------------------------------------------------------
/// @brief Returns the sorted draws for the current draw order layer.
/// This should only be called after all recording has finished.
///
/// @param[in] opaque_skip_count The number of opaque elements to skip
/// when appending the combined elements.
/// This is used for the "clear color"
/// optimization.
/// @param[in] translucent_skip_count The number of translucent elements to
/// skip when appending the combined
/// elements. This is used for the
/// "clear color" optimization.
///
ElementRefs GetSortedDraws(size_t opaque_skip_count,
size_t translucent_skip_count) const;

private:
/// A data structure for collecting sorted draws for a given "draw order
/// layer". Currently these layers just correspond to the local clip stack.
struct DrawOrderLayer {
/// The list of backdrop-independent elements (always just opaque). These
/// are order independent, and so we render these elements in reverse
/// painter's order so that they cull one another.
ElementRefs opaque_elements;

/// The list of backdrop-dependent elements with respect to this draw
/// order layer. These elements are drawn after all of the independent
/// elements.
ElementRefs dependent_elements;

//-----------------------------------------------------------------------
/// @brief Appends the combined opaque and transparent elements into
/// a final destination buffer.
///
/// @param[in] destination The buffer to append the combined
/// elements to.
/// @param[in] opaque_skip_count The number of opaque elements to
/// skip when appending the combined
/// elements. This is used for the
/// "clear color" optimization.
/// @param[in] translucent_skip_count The number of translucent elements
/// to skip when appending the combined
/// elements. This is used for the
/// "clear color" optimization.
///
void WriteCombinedDraws(ElementRefs& destination,
size_t opaque_skip_count,
size_t translucent_skip_count) const;
};
std::vector<DrawOrderLayer> draw_order_layers_;

// The first time the root layer is flushed, the layer contents are stored
// here. This is done to enable element skipping for the clear color
// optimization.
std::optional<DrawOrderLayer> first_root_flush_;
// All subsequent root flushes are stored here.
ElementRefs sorted_elements_;

DrawOrderResolver(const DrawOrderResolver&) = delete;

DrawOrderResolver& operator=(const DrawOrderResolver&) = delete;
};

} // namespace impeller

#endif // FLUTTER_IMPELLER_ENTITY_DRAW_ORDER_RESOLVER_H_
Loading

0 comments on commit 4dc94d6

Please sign in to comment.