Skip to content

Commit

Permalink
[Impeller] stencil buffer record/replay instead of MSAA storage. (#47397
Browse files Browse the repository at this point in the history
)

When restoring from a backdrop filter, replay the clip affecting cmds into a new stencil buffer instead of storing 4x MSAA stencil buffer.

Fixes flutter/flutter#137561
Fixes flutter/flutter#137448
Fixes flutter/flutter#137302

Helps flutter/flutter#137108
  • Loading branch information
jonahwilliams authored Nov 1, 2023
1 parent 2a6f1a3 commit 04329c5
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 43 deletions.
85 changes: 54 additions & 31 deletions impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -253,18 +253,15 @@ void EntityPass::AddSubpassInline(std::unique_ptr<EntityPass> pass) {
pass->advanced_blend_reads_from_pass_texture_;
}

static RenderTarget::AttachmentConfig GetDefaultStencilConfig(bool readable) {
return RenderTarget::AttachmentConfig{
.storage_mode = readable ? StorageMode::kDevicePrivate
: StorageMode::kDeviceTransient,
.load_action = LoadAction::kDontCare,
.store_action = StoreAction::kDontCare,
};
}
static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig =
RenderTarget::AttachmentConfig{
.storage_mode = StorageMode::kDeviceTransient,
.load_action = LoadAction::kDontCare,
.store_action = StoreAction::kDontCare,
};

static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
ISize size,
bool readable,
const Color& clear_color) {
auto context = renderer.GetContext();

Expand All @@ -285,8 +282,8 @@ static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
.resolve_storage_mode = StorageMode::kDevicePrivate,
.load_action = LoadAction::kDontCare,
.store_action = StoreAction::kMultisampleResolve,
.clear_color = clear_color}, // color_attachment_config
GetDefaultStencilConfig(readable) // stencil_attachment_config
.clear_color = clear_color}, // color_attachment_config
kDefaultStencilConfig // stencil_attachment_config
);
} else {
target = RenderTarget::CreateOffscreen(
Expand All @@ -299,8 +296,8 @@ static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
.load_action = LoadAction::kDontCare,
.store_action = StoreAction::kDontCare,
.clear_color = clear_color,
}, // color_attachment_config
GetDefaultStencilConfig(readable) // stencil_attachment_config
}, // color_attachment_config
kDefaultStencilConfig // stencil_attachment_config
);
}

Expand Down Expand Up @@ -361,9 +358,9 @@ bool EntityPass::Render(ContentContext& renderer,
// and then blit the results onto the onscreen texture. If using this branch,
// there's no need to set up a stencil attachment on the root render target.
if (!supports_onscreen_backdrop_reads && reads_from_onscreen_backdrop) {
auto offscreen_target = CreateRenderTarget(
renderer, root_render_target.GetRenderTargetSize(), true,
GetClearColor(render_target.GetRenderTargetSize()));
auto offscreen_target =
CreateRenderTarget(renderer, root_render_target.GetRenderTargetSize(),
GetClearColor(render_target.GetRenderTargetSize()));

if (!OnRender(renderer, // renderer
capture, // capture
Expand Down Expand Up @@ -465,8 +462,7 @@ bool EntityPass::Render(ContentContext& renderer,
*renderer.GetContext(), *renderer.GetRenderTargetCache(),
color0.texture->GetSize(),
renderer.GetContext()->GetCapabilities()->SupportsOffscreenMSAA(),
"ImpellerOnscreen",
GetDefaultStencilConfig(reads_from_onscreen_backdrop));
"ImpellerOnscreen", kDefaultStencilConfig);
}

// Set up the clear color of the root pass.
Expand Down Expand Up @@ -503,10 +499,10 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
//--------------------------------------------------------------------------
/// Setup entity element.
///

if (const auto& entity = std::get_if<Entity>(&element)) {
element_entity = *entity;
element_entity.SetCapture(capture.CreateChild("Entity"));

if (!global_pass_position.IsZero()) {
// If the pass image is going to be rendered with a non-zero position,
// apply the negative translation to entity copies before rendering them
Expand All @@ -515,16 +511,15 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
Matrix::MakeTranslation(Vector3(-global_pass_position)) *
element_entity.GetTransformation());
}
return EntityPass::EntityResult::Success(element_entity);
}

//--------------------------------------------------------------------------
/// Setup subpass element.
///

else if (const auto& subpass_ptr =
std::get_if<std::unique_ptr<EntityPass>>(&element)) {
if (const auto& subpass_ptr =
std::get_if<std::unique_ptr<EntityPass>>(&element)) {
auto subpass = subpass_ptr->get();

if (subpass->delegate_->CanElide()) {
return EntityPass::EntityResult::Skip();
}
Expand Down Expand Up @@ -625,10 +620,9 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
}

auto subpass_target = CreateRenderTarget(
renderer, // renderer
subpass_size, // size
subpass->GetTotalPassReads(renderer) > 0, // readable
subpass->GetClearColor(subpass_size)); // clear_color
renderer, // renderer
subpass_size, // size
subpass->GetClearColor(subpass_size)); // clear_color

if (!subpass_target.IsValid()) {
VALIDATION_LOG << "Subpass render target is invalid.";
Expand Down Expand Up @@ -696,11 +690,10 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
element_entity.SetTransformation(subpass_texture_capture.AddMatrix(
"Transform", Matrix::MakeTranslation(Vector3(subpass_coverage->origin -
global_pass_position))));
} else {
FML_UNREACHABLE();
}

return EntityPass::EntityResult::Success(element_entity);
return EntityPass::EntityResult::Success(element_entity);
}
FML_UNREACHABLE();
}

bool EntityPass::RenderElement(Entity& element_entity,
Expand All @@ -724,6 +717,15 @@ bool EntityPass::RenderElement(Entity& element_entity,
// blit the non-MSAA resolve texture of the previous pass to MSAA textures
// (let alone a transient one).
if (result.backdrop_texture) {
// Restore any clips that were recorded before the backdrop filter was
// applied.
auto& replay_entities = clip_replay_->GetReplayEntities();
for (const auto& entity : replay_entities) {
if (!entity.Render(renderer, *result.pass)) {
VALIDATION_LOG << "Failed to render entity for clip restore.";
}
}

auto size_rect = Rect::MakeSize(result.pass->GetRenderTargetSize());
auto msaa_backdrop_contents = TextureContents::MakeRect(size_rect);
msaa_backdrop_contents->SetStencilEnabled(false);
Expand Down Expand Up @@ -836,6 +838,7 @@ bool EntityPass::RenderElement(Entity& element_entity,
#endif

element_entity.SetClipDepth(element_entity.GetClipDepth() - clip_depth_floor);
clip_replay_->RecordEntity(element_entity, clip_coverage.type);
if (!element_entity.Render(renderer, *result.pass)) {
VALIDATION_LOG << "Failed to render entity.";
return false;
Expand Down Expand Up @@ -1180,4 +1183,24 @@ void EntityPass::SetEnableOffscreenCheckerboard(bool enabled) {
enable_offscreen_debug_checkerboard_ = enabled;
}

EntityPassClipRecorder::EntityPassClipRecorder() {}

void EntityPassClipRecorder::RecordEntity(const Entity& entity,
Contents::ClipCoverage::Type type) {
switch (type) {
case Contents::ClipCoverage::Type::kNoChange:
return;
case Contents::ClipCoverage::Type::kAppend:
rendered_clip_entities_.push_back(entity);
break;
case Contents::ClipCoverage::Type::kRestore:
rendered_clip_entities_.pop_back();
break;
}
}

const std::vector<Entity>& EntityPassClipRecorder::GetReplayEntities() const {
return rendered_clip_entities_;
}

} // namespace impeller
23 changes: 23 additions & 0 deletions impeller/entity/entity_pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
namespace impeller {

class ContentContext;
class EntityPassClipRecorder;

class EntityPass {
public:
Expand Down Expand Up @@ -285,6 +286,8 @@ class EntityPass {
bool flood_clip_ = false;
bool enable_offscreen_debug_checkerboard_ = false;
std::optional<Rect> bounds_limit_;
std::unique_ptr<EntityPassClipRecorder> clip_replay_ =
std::make_unique<EntityPassClipRecorder>();

/// These values are incremented whenever something is added to the pass that
/// requires reading from the backdrop texture. Currently, this can happen in
Expand All @@ -309,4 +312,24 @@ class EntityPass {
EntityPass& operator=(const EntityPass&) = delete;
};

/// @brief A class that tracks all clips that have been recorded in the current
/// entity pass stencil.
///
/// These clips are replayed when restoring the backdrop so that the
/// stencil buffer is left in an identical state.
class EntityPassClipRecorder {
public:
EntityPassClipRecorder();

~EntityPassClipRecorder() = default;

/// @brief Record the entity based on the provided coverage [type].
void RecordEntity(const Entity& entity, Contents::ClipCoverage::Type type);

const std::vector<Entity>& GetReplayEntities() const;

private:
std::vector<Entity> rendered_clip_entities_;
};

} // namespace impeller
13 changes: 2 additions & 11 deletions impeller/entity/inline_pass_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ InlinePassContext::InlinePassContext(
std::optional<RenderPassResult> collapsed_parent_pass)
: context_(std::move(context)),
pass_target_(pass_target),
total_pass_reads_(pass_texture_reads),
is_collapsed_(collapsed_parent_pass.has_value()) {
if (collapsed_parent_pass.has_value()) {
pass_ = collapsed_parent_pass.value().pass;
Expand Down Expand Up @@ -140,17 +139,9 @@ InlinePassContext::RenderPassResult InlinePassContext::GetRenderPass(
return {};
}

// Only clear the stencil if this is the very first pass of the
// layer.
stencil->load_action =
pass_count_ > 0 ? LoadAction::kLoad : LoadAction::kClear;
// If we're on the last pass of the layer, there's no need to store the
// stencil because nothing needs to read it.
stencil->store_action = pass_count_ == total_pass_reads_
? StoreAction::kDontCare
: StoreAction::kStore;
stencil->load_action = LoadAction::kClear;
stencil->store_action = StoreAction::kDontCare;
pass_target_.target_.SetStencilAttachment(stencil.value());

pass_target_.target_.SetColorAttachment(color0, 0);

pass_ = command_buffer_->CreateRenderPass(pass_target_.GetRenderTarget());
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/inline_pass_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class InlinePassContext {
std::shared_ptr<CommandBuffer> command_buffer_;
std::shared_ptr<RenderPass> pass_;
uint32_t pass_count_ = 0;
uint32_t total_pass_reads_ = 0;

// Whether this context is collapsed into a parent entity pass.
bool is_collapsed_ = false;

Expand Down

0 comments on commit 04329c5

Please sign in to comment.