Reverts "[Impeller] Reland: Implement draw order optimization. (#54215)" (flutter/engine#54261)

Reverts: flutter/engine#54215
Initiated by: bdero
Reason for reverting: Causing golden diffs in framework roll https://flutter-gold.skia.org/search?issue=152633&crs=github&patchsets=2&corpus=flutter
Original PR Author: bdero

Reviewed By: {jonahwilliams}

This change reverts the following previous change:
Original PR: #54136
Revert PR: #54067

Includes fixes for issue seen in [these goldens](https://flutter-gold.skia.org/search?issue=152354&crs=github&patchsets=2&corpus=flutter).

The problem was that the scissor was ending up wrong after clip replay for opaque draws that are supposed to occur outside the parent clip scope(s).

To fix it, I made clip replay draws as well as the subpass texture draw apply lazily.
For the clip replay, there's no need to apply a given clip until we come across an entity that has a depth less than or equal to the clip.
And for the subpass texture (which is often translucent), we can defer drawing it until we come across another translucent draw. Deferring the subpass texture is important because if we draw it immediately, then all of the replay clips need to be drawn immediately too.

## Description
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.

### Local new_gallery before/after (iPhone 12 mini):
```
cd ~/projects/flutter/flutter/dev/integration_tests/new_gallery
flutter drive --profile --local-engine-src-path ~/projects/flutter/engine/src --local-engine=ios_profile --local-engine-host=host_profile_arm64 --trace-startup -t test_driver/transitions_perf.dart -d 00008101-000A59A93C10001E
```
![image](https://github.com/user-attachments/assets/7372c128-ca71-44a6-8e6c-b0043025f751)
This commit is contained in:
auto-submit[bot] 2024-07-31 19:59:48 +00:00 committed by GitHub
parent a99fbaa228
commit fae441f0ef
15 changed files with 88 additions and 629 deletions

View File

@ -164,7 +164,6 @@
../../../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

View File

@ -42128,8 +42128,6 @@ 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
@ -45011,8 +45009,6 @@ 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

View File

@ -166,8 +166,6 @@ 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",
@ -250,7 +248,6 @@ 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",

View File

@ -213,11 +213,6 @@ 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;

View File

@ -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(),

View File

@ -153,9 +153,6 @@ 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));

View File

@ -1,124 +0,0 @@
// 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

View File

@ -1,94 +0,0 @@
// 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_

View File

@ -1,156 +0,0 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/testing/testing.h"
#include "impeller/entity/draw_order_resolver.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace impeller {
namespace testing {
TEST(DrawOrderResolverTest, GetSortedDrawsReturnsCorrectOrderWithNoClips) {
DrawOrderResolver resolver;
// Opaque items.
resolver.AddElement(0, true);
resolver.AddElement(1, true);
// Translucent items.
resolver.AddElement(2, false);
resolver.AddElement(3, false);
auto sorted_elements = resolver.GetSortedDraws(0, 0);
EXPECT_EQ(sorted_elements.size(), 4u);
// First, the opaque items are drawn in reverse order.
EXPECT_EQ(sorted_elements[0], 1u);
EXPECT_EQ(sorted_elements[1], 0u);
// Then the translucent items are drawn.
EXPECT_EQ(sorted_elements[2], 2u);
EXPECT_EQ(sorted_elements[3], 3u);
}
TEST(DrawOrderResolverTest, GetSortedDrawsReturnsCorrectOrderWithClips) {
DrawOrderResolver resolver;
// Items before clip.
resolver.AddElement(0, false);
resolver.AddElement(1, true);
resolver.AddElement(2, false);
resolver.AddElement(3, true);
// Clip.
resolver.PushClip(4);
{
// Clipped items.
resolver.AddElement(5, false);
resolver.AddElement(6, false);
// Clipped translucent items.
resolver.AddElement(7, true);
resolver.AddElement(8, true);
}
resolver.PopClip();
// Items after clip.
resolver.AddElement(9, true);
resolver.AddElement(10, false);
resolver.AddElement(11, true);
resolver.AddElement(12, false);
auto sorted_elements = resolver.GetSortedDraws(0, 0);
EXPECT_EQ(sorted_elements.size(), 13u);
// First, all the non-clipped opaque items are drawn in reverse order.
EXPECT_EQ(sorted_elements[0], 11u);
EXPECT_EQ(sorted_elements[1], 9u);
EXPECT_EQ(sorted_elements[2], 3u);
EXPECT_EQ(sorted_elements[3], 1u);
// Then, non-clipped translucent items that came before the clip are drawn in
// their original order.
EXPECT_EQ(sorted_elements[4], 0u);
EXPECT_EQ(sorted_elements[5], 2u);
// Then, the clip and its sorted child items are drawn.
EXPECT_EQ(sorted_elements[6], 4u);
{
// Opaque clipped items are drawn in reverse order.
EXPECT_EQ(sorted_elements[7], 8u);
EXPECT_EQ(sorted_elements[8], 7u);
// Translucent clipped items are drawn.
EXPECT_EQ(sorted_elements[9], 5u);
EXPECT_EQ(sorted_elements[10], 6u);
}
// Finally, the non-clipped translucent items which came after the clip are
// drawn in their original order.
EXPECT_EQ(sorted_elements[11], 10u);
EXPECT_EQ(sorted_elements[12], 12u);
}
TEST(DrawOrderResolverTest, GetSortedDrawsRespectsSkipCounts) {
DrawOrderResolver resolver;
// These items will be skipped.
resolver.AddElement(0, false);
resolver.AddElement(1, true);
resolver.AddElement(2, false);
// These ones will be included in the final draw list.
resolver.AddElement(3, false);
resolver.AddElement(4, true);
resolver.AddElement(5, true);
// Form the draw list, skipping elements 0, 1, and 2.
// This emulates what happens when entitypass applies the clear color
// optimization.
auto sorted_elements = resolver.GetSortedDraws(1, 2);
EXPECT_EQ(sorted_elements.size(), 3u);
// First, opaque items are drawn in reverse order.
EXPECT_EQ(sorted_elements[0], 5u);
EXPECT_EQ(sorted_elements[1], 4u);
// Then, translucent items are drawn.
EXPECT_EQ(sorted_elements[2], 3u);
}
TEST(DrawOrderResolverTest, GetSortedDrawsReturnsCorrectOrderWithFlush) {
DrawOrderResolver resolver;
resolver.AddElement(0, false);
resolver.AddElement(1, true);
resolver.AddElement(2, false);
resolver.AddElement(3, true);
resolver.Flush();
resolver.AddElement(4, false);
resolver.AddElement(5, true);
resolver.AddElement(6, false);
resolver.AddElement(7, true);
resolver.Flush();
resolver.AddElement(8, false);
resolver.AddElement(9, true);
resolver.AddElement(10, false);
resolver.AddElement(11, true);
auto sorted_elements = resolver.GetSortedDraws(1, 1);
EXPECT_EQ(sorted_elements.size(), 10u);
// Skipped draws apply to the first flush.
EXPECT_EQ(sorted_elements[0], 3u);
EXPECT_EQ(sorted_elements[1], 2u);
EXPECT_EQ(sorted_elements[2], 7u);
EXPECT_EQ(sorted_elements[3], 5u);
EXPECT_EQ(sorted_elements[4], 4u);
EXPECT_EQ(sorted_elements[5], 6u);
EXPECT_EQ(sorted_elements[6], 11u);
EXPECT_EQ(sorted_elements[7], 9u);
EXPECT_EQ(sorted_elements[8], 8u);
EXPECT_EQ(sorted_elements[9], 10u);
}
} // namespace testing
} // namespace impeller

View File

@ -43,8 +43,6 @@ Entity::Entity(Entity&&) = default;
Entity::Entity(const Entity&) = default;
Entity& Entity::operator=(Entity&&) = default;
const Matrix& Entity::GetTransform() const {
return transform_;
}

View File

@ -73,8 +73,6 @@ class Entity {
Entity(Entity&&);
Entity& operator=(Entity&&);
/// @brief Get the global transform matrix for this Entity.
const Matrix& GetTransform() const;

View File

@ -20,7 +20,6 @@
#include "impeller/entity/contents/filters/inputs/filter_input.h"
#include "impeller/entity/contents/framebuffer_blend_contents.h"
#include "impeller/entity/contents/texture_contents.h"
#include "impeller/entity/draw_order_resolver.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/entity_pass_clip_stack.h"
#include "impeller/entity/inline_pass_context.h"
@ -45,10 +44,6 @@ std::tuple<std::optional<Color>, BlendMode> ElementAsBackgroundColor(
}
} // namespace
bool EntityPass::IsSubpass(const Element& element) {
return std::holds_alternative<std::unique_ptr<EntityPass>>(element);
}
EntityPass::EntityPass() = default;
EntityPass::~EntityPass() = default;
@ -111,14 +106,11 @@ void EntityPass::AddEntity(Entity entity) {
if (entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
advanced_blend_reads_from_pass_texture_ = true;
}
draw_order_resolver_.AddElement(elements_.size(),
entity.GetBlendMode() == BlendMode::kSource);
elements_.emplace_back(std::move(entity));
}
void EntityPass::PushClip(Entity entity) {
elements_.emplace_back(std::move(entity));
draw_order_resolver_.PushClip(elements_.size() - 1);
active_clips_.emplace_back(elements_.size() - 1);
}
@ -137,7 +129,6 @@ void EntityPass::PopClips(size_t num_clips, uint64_t depth) {
FML_DCHECK(element);
element->SetClipDepth(depth);
active_clips_.pop_back();
draw_order_resolver_.PopClip();
}
}
@ -280,14 +271,8 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
FML_DCHECK(pass->superpass_ == nullptr);
pass->superpass_ = this;
bool has_backdrop_filter = pass->backdrop_filter_proc_ != nullptr;
if (has_backdrop_filter) {
if (pass->backdrop_filter_proc_) {
backdrop_filter_reads_from_pass_texture_ = true;
// Since backdrop filters trigger the RenderPass to end and lose all depth
// information for opaque draws, this is a hard barrier for the draw order
// optimization. Flush all sorted draws accumulated up to this point.
draw_order_resolver_.Flush();
}
if (pass->blend_mode_ > Entity::kLastPipelineBlendMode) {
advanced_blend_reads_from_pass_texture_ = true;
@ -295,12 +280,6 @@ EntityPass* EntityPass::AddSubpass(std::unique_ptr<EntityPass> pass) {
auto subpass_pointer = pass.get();
elements_.emplace_back(std::move(pass));
draw_order_resolver_.AddElement(elements_.size() - 1, false);
if (has_backdrop_filter) {
draw_order_resolver_.Flush();
}
return subpass_pointer;
}
@ -765,55 +744,6 @@ bool EntityPass::RenderElement(Entity& element_entity,
ContentContext& renderer,
EntityPassClipStack& clip_coverage_stack,
Point global_pass_position) const {
// Setup advanced blends.
if (element_entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) {
auto src_contents = element_entity.GetContents();
auto contents = std::make_shared<FramebufferBlendContents>();
contents->SetChildContents(src_contents);
contents->SetBlendMode(element_entity.GetBlendMode());
element_entity.SetContents(std::move(contents));
element_entity.SetBlendMode(BlendMode::kSource);
} else {
// End the active pass and flush the buffer before rendering
// "advanced" blends. Advanced blends work by binding the current
// render target texture as an input ("destination"), blending with a
// second texture input ("source"), writing the result to an
// intermediate texture, and finally copying the data from the
// intermediate texture back to the render target texture. And so all
// of the commands that have written to the render target texture so
// far need to execute before it's bound for blending (otherwise the
// blend pass will end up executing before all the previous commands
// in the active pass).
if (!pass_context.EndPass()) {
VALIDATION_LOG
<< "Failed to end the current render pass in order to read "
"from "
"the backdrop texture and apply an advanced blend.";
return false;
}
// Amend an advanced blend filter to the contents, attaching the pass
// texture.
auto texture = pass_context.GetTexture();
if (!texture) {
VALIDATION_LOG << "Failed to fetch the color texture in order to "
"apply an advanced blend.";
return false;
}
FilterInput::Vector inputs = {
FilterInput::Make(texture, element_entity.GetTransform().Invert()),
FilterInput::Make(element_entity.GetContents())};
auto contents =
ColorFilterContents::MakeBlend(element_entity.GetBlendMode(), inputs);
contents->SetCoverageHint(element_entity.GetCoverage());
element_entity.SetContents(std::move(contents));
element_entity.SetBlendMode(BlendMode::kSource);
}
}
auto result = pass_context.GetRenderPass(pass_depth);
if (!result.pass) {
// Failure to produce a render pass should be explained by specific errors
@ -846,19 +776,15 @@ bool EntityPass::RenderElement(Entity& element_entity,
}
if (result.just_created) {
clip_coverage_stack.ActivateClipReplay();
}
// If there are any pending clips to replay, render any that may affect
// the entity we're about to render.
while (const EntityPassClipStack::ReplayResult* next_replay_clip =
clip_coverage_stack.GetNextReplayResult(element_entity)) {
auto& replay_entity = next_replay_clip->entity;
SetClipScissor(clip_coverage_stack.CurrentClipCoverage(), *result.pass,
global_pass_position);
if (!replay_entity.Render(renderer, *result.pass)) {
VALIDATION_LOG << "Failed to render entity for clip replay.";
return false;
// Restore any clips that were recorded before the backdrop filter was
// applied.
auto& replay_entities = clip_coverage_stack.GetReplayEntities();
for (const auto& replay : replay_entities) {
SetClipScissor(clip_coverage_stack.CurrentClipCoverage(), *result.pass,
global_pass_position);
if (!replay.entity.Render(renderer, *result.pass)) {
VALIDATION_LOG << "Failed to render entity for clip restore.";
}
}
}
@ -965,81 +891,21 @@ bool EntityPass::OnRender(
renderer, clip_coverage_stack, global_pass_position);
}
bool should_collapse_clear_colors =
!collapsed_parent_pass &&
// Backdrop filters act as a entity before
// everything and disrupt the optimization.
!backdrop_filter_proc_;
// Count the number of elements eaten by the clear color optimization.
size_t opaque_clear_entity_count = 0;
size_t translucent_clear_entity_count = 0;
if (should_collapse_clear_colors) {
for (const auto& element : elements_) {
if (const Entity* entity = std::get_if<Entity>(&element)) {
std::optional<Color> entity_color =
entity->AsBackgroundColor(clear_color_size);
if (entity_color.has_value()) {
if (entity->GetBlendMode() == BlendMode::kSource) {
opaque_clear_entity_count++;
} else {
translucent_clear_entity_count++;
}
continue;
}
break;
bool is_collapsing_clear_colors = !collapsed_parent_pass &&
// Backdrop filters act as a entity before
// everything and disrupt the optimization.
!backdrop_filter_proc_;
for (const auto& element : elements_) {
// Skip elements that are incorporated into the clear color.
if (is_collapsing_clear_colors) {
auto [entity_color, _] =
ElementAsBackgroundColor(element, clear_color_size);
if (entity_color.has_value()) {
continue;
}
is_collapsing_clear_colors = false;
}
}
using ElementCallback = std::function<bool(const Element&)>;
using ElementIterator = std::function<bool(const ElementCallback&)>;
ElementIterator element_iterator;
if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) {
element_iterator =
[this, &opaque_clear_entity_count,
&translucent_clear_entity_count](const ElementCallback& callback) {
const auto& sorted_elements = draw_order_resolver_.GetSortedDraws(
opaque_clear_entity_count, translucent_clear_entity_count);
for (const auto& element_ref : sorted_elements) {
const Element& element = elements_[element_ref];
if (!callback(element)) {
return false;
}
}
return true;
};
} else {
// If framebuffer fetch isn't supported, just disable the draw order
// optimization. We could technically make it work by flushing each time
// we encounter an advanced blend at recording time down the road.
element_iterator = [this, &opaque_clear_entity_count,
&translucent_clear_entity_count](
const ElementCallback& callback) {
size_t skips = opaque_clear_entity_count + translucent_clear_entity_count;
for (const auto& element : elements_) {
if (skips > 0) {
skips--;
continue;
}
if (!callback(element)) {
return false;
}
}
return true;
};
}
const auto& render_element = [&, this](Entity& entity) {
return RenderElement(entity, clip_height_floor, pass_context, pass_depth,
renderer, clip_coverage_stack, global_pass_position);
};
std::optional<Entity> deferred_entity;
bool result = element_iterator([&](const Element& element) {
EntityResult result =
GetEntityForElement(element, // element
renderer, // renderer
@ -1058,36 +924,70 @@ bool EntityPass::OnRender(
// in `GetEntityForElement()`.
return false;
case EntityResult::kSkip:
return true;
continue;
};
if (deferred_entity.has_value() &&
result.entity.GetBlendMode() != BlendMode::kSource) {
if (!render_element(*deferred_entity)) {
return false;
}
deferred_entity.reset();
}
//--------------------------------------------------------------------------
/// Setup advanced blends.
///
if (IsSubpass(element)) {
if (deferred_entity.has_value()) {
if (!render_element(*deferred_entity)) {
if (result.entity.GetBlendMode() > Entity::kLastPipelineBlendMode) {
if (renderer.GetDeviceCapabilities().SupportsFramebufferFetch()) {
auto src_contents = result.entity.GetContents();
auto contents = std::make_shared<FramebufferBlendContents>();
contents->SetChildContents(src_contents);
contents->SetBlendMode(result.entity.GetBlendMode());
result.entity.SetContents(std::move(contents));
result.entity.SetBlendMode(BlendMode::kSource);
} else {
// End the active pass and flush the buffer before rendering "advanced"
// blends. Advanced blends work by binding the current render target
// texture as an input ("destination"), blending with a second texture
// input ("source"), writing the result to an intermediate texture, and
// finally copying the data from the intermediate texture back to the
// render target texture. And so all of the commands that have written
// to the render target texture so far need to execute before it's bound
// for blending (otherwise the blend pass will end up executing before
// all the previous commands in the active pass).
if (!pass_context.EndPass()) {
VALIDATION_LOG
<< "Failed to end the current render pass in order to read from "
"the backdrop texture and apply an advanced blend.";
return false;
}
// Amend an advanced blend filter to the contents, attaching the pass
// texture.
auto texture = pass_context.GetTexture();
if (!texture) {
VALIDATION_LOG << "Failed to fetch the color texture in order to "
"apply an advanced blend.";
return false;
}
FilterInput::Vector inputs = {
FilterInput::Make(texture, result.entity.GetTransform().Invert()),
FilterInput::Make(result.entity.GetContents())};
auto contents = ColorFilterContents::MakeBlend(
result.entity.GetBlendMode(), inputs);
contents->SetCoverageHint(result.entity.GetCoverage());
result.entity.SetContents(std::move(contents));
result.entity.SetBlendMode(BlendMode::kSource);
}
deferred_entity = std::move(result.entity);
return true;
}
return render_element(result.entity);
});
if (!result) {
return false;
//--------------------------------------------------------------------------
/// Render the Element.
///
if (!RenderElement(result.entity, clip_height_floor, pass_context,
pass_depth, renderer, clip_coverage_stack,
global_pass_position)) {
// Specific validation logs are handled in `render_element()`.
return false;
}
}
if (deferred_entity.has_value() && !render_element(*deferred_entity)) {
return false;
}
return true;
}

View File

@ -13,7 +13,6 @@
#include "impeller/entity/contents/contents.h"
#include "impeller/entity/contents/filters/filter_contents.h"
#include "impeller/entity/draw_order_resolver.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/entity_pass_clip_stack.h"
#include "impeller/entity/entity_pass_delegate.h"
@ -54,8 +53,6 @@ class EntityPass {
/// `GetEntityForElement()`.
using Element = std::variant<Entity, std::unique_ptr<EntityPass>>;
static bool IsSubpass(const Element& element);
using BackdropFilterProc = std::function<std::shared_ptr<FilterContents>(
FilterInput::Ref,
const Matrix& effect_transform,
@ -217,7 +214,8 @@ class EntityPass {
kSkip,
};
/// @brief The resulting entity that should be rendered.
/// @brief The resulting entity that should be rendered. If `std::nullopt`,
/// there is nothing to render.
Entity entity;
/// @brief This is set to `false` if there was an unexpected rendering
/// error while resolving the Entity.
@ -318,12 +316,10 @@ class EntityPass {
/// evaluated and recorded to an `EntityPassTarget` by the `OnRender` method.
std::vector<Element> elements_;
DrawOrderResolver draw_order_resolver_;
/// The stack of currently active clips (during Aiks recording time). Each
/// entry is an index into the `elements_` list. The depth value of a clip
/// is the max of all the entities it affects, so assignment of the depth
/// value is deferred until clip restore or end of the EntityPass.
/// entry is an index into the `elements_` list. The depth value of a clip is
/// the max of all the entities it affects, so assignment of the depth value
/// is deferred until clip restore or end of the EntityPass.
std::vector<size_t> active_clips_;
EntityPass* superpass_ = nullptr;
@ -342,8 +338,8 @@ class EntityPass {
/// 1. An entity with an "advanced blend" is added to the pass.
/// 2. A subpass with a backdrop filter is added to the pass.
/// These are tracked as separate values because we may ignore
/// `blend_reads_from_pass_texture_` if the device supports framebuffer
/// based advanced blends.
/// `blend_reads_from_pass_texture_` if the device supports framebuffer based
/// advanced blends.
bool advanced_blend_reads_from_pass_texture_ = false;
bool backdrop_filter_reads_from_pass_texture_ = false;

View File

@ -3,8 +3,6 @@
// found in the LICENSE file.
#include "impeller/entity/entity_pass_clip_stack.h"
#include "flutter/fml/logging.h"
#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/entity.h"
@ -39,12 +37,10 @@ void EntityPassClipStack::PushSubpass(std::optional<Rect> subpass_coverage,
.clip_height = clip_height},
},
});
next_replay_index_ = 0;
}
void EntityPassClipStack::PopSubpass() {
subpass_state_.pop_back();
next_replay_index_ = subpass_state_.back().rendered_clip_entities.size();
}
const std::vector<ClipCoverageLayer>
@ -142,22 +138,12 @@ void EntityPassClipStack::RecordEntity(const Entity& entity,
case Contents::ClipCoverage::Type::kNoChange:
return;
case Contents::ClipCoverage::Type::kAppend:
FML_DCHECK(next_replay_index_ ==
subpass_state.rendered_clip_entities.size())
<< "Not all clips have been replayed before appending new clip.";
subpass_state.rendered_clip_entities.push_back(
{.entity = entity.Clone(), .clip_coverage = clip_coverage});
next_replay_index_++;
break;
case Contents::ClipCoverage::Type::kRestore:
FML_DCHECK(next_replay_index_ <=
subpass_state.rendered_clip_entities.size());
if (!subpass_state.rendered_clip_entities.empty()) {
subpass_state.rendered_clip_entities.pop_back();
if (next_replay_index_ > subpass_state.rendered_clip_entities.size()) {
next_replay_index_ = subpass_state.rendered_clip_entities.size();
}
}
break;
}
@ -173,27 +159,4 @@ EntityPassClipStack::GetReplayEntities() const {
return subpass_state_.back().rendered_clip_entities;
}
void EntityPassClipStack::ActivateClipReplay() {
next_replay_index_ = 0;
}
const EntityPassClipStack::ReplayResult*
EntityPassClipStack::GetNextReplayResult(const Entity& entity) {
if (next_replay_index_ >=
subpass_state_.back().rendered_clip_entities.size()) {
// No clips need to be replayed.
return nullptr;
}
ReplayResult* next_replay =
&subpass_state_.back().rendered_clip_entities[next_replay_index_];
if (next_replay->entity.GetClipDepth() < entity.GetClipDepth()) {
// The next replay clip doesn't affect the current entity, so don't replay
// it yet.
return nullptr;
}
next_replay_index_++;
return next_replay;
}
} // namespace impeller

View File

@ -62,14 +62,9 @@ class EntityPassClipStack {
Contents::ClipCoverage::Type type,
std::optional<Rect> clip_coverage);
// Visible for testing.
const std::vector<ReplayResult>& GetReplayEntities() const;
void ActivateClipReplay();
/// @brief Returns the next Entity that should be replayed. If there are no
/// enities to replay, then nullptr is returned.
const ReplayResult* GetNextReplayResult(const Entity& entity);
// Visible for testing.
const std::vector<ClipCoverageLayer> GetClipCoverageLayers() const;
@ -82,7 +77,6 @@ class EntityPassClipStack {
SubpassState& GetCurrentSubpassState();
std::vector<SubpassState> subpass_state_;
size_t next_replay_index_ = 0;
};
} // namespace impeller