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

Commit 04329c5

Browse files
author
Jonah Williams
authored
[Impeller] stencil buffer record/replay instead of MSAA storage. (#47397)
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
1 parent 2a6f1a3 commit 04329c5

File tree

4 files changed

+80
-43
lines changed

4 files changed

+80
-43
lines changed

impeller/entity/entity_pass.cc

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -253,18 +253,15 @@ void EntityPass::AddSubpassInline(std::unique_ptr<EntityPass> pass) {
253253
pass->advanced_blend_reads_from_pass_texture_;
254254
}
255255

256-
static RenderTarget::AttachmentConfig GetDefaultStencilConfig(bool readable) {
257-
return RenderTarget::AttachmentConfig{
258-
.storage_mode = readable ? StorageMode::kDevicePrivate
259-
: StorageMode::kDeviceTransient,
260-
.load_action = LoadAction::kDontCare,
261-
.store_action = StoreAction::kDontCare,
262-
};
263-
}
256+
static const constexpr RenderTarget::AttachmentConfig kDefaultStencilConfig =
257+
RenderTarget::AttachmentConfig{
258+
.storage_mode = StorageMode::kDeviceTransient,
259+
.load_action = LoadAction::kDontCare,
260+
.store_action = StoreAction::kDontCare,
261+
};
264262

265263
static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
266264
ISize size,
267-
bool readable,
268265
const Color& clear_color) {
269266
auto context = renderer.GetContext();
270267

@@ -285,8 +282,8 @@ static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
285282
.resolve_storage_mode = StorageMode::kDevicePrivate,
286283
.load_action = LoadAction::kDontCare,
287284
.store_action = StoreAction::kMultisampleResolve,
288-
.clear_color = clear_color}, // color_attachment_config
289-
GetDefaultStencilConfig(readable) // stencil_attachment_config
285+
.clear_color = clear_color}, // color_attachment_config
286+
kDefaultStencilConfig // stencil_attachment_config
290287
);
291288
} else {
292289
target = RenderTarget::CreateOffscreen(
@@ -299,8 +296,8 @@ static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
299296
.load_action = LoadAction::kDontCare,
300297
.store_action = StoreAction::kDontCare,
301298
.clear_color = clear_color,
302-
}, // color_attachment_config
303-
GetDefaultStencilConfig(readable) // stencil_attachment_config
299+
}, // color_attachment_config
300+
kDefaultStencilConfig // stencil_attachment_config
304301
);
305302
}
306303

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

368365
if (!OnRender(renderer, // renderer
369366
capture, // capture
@@ -465,8 +462,7 @@ bool EntityPass::Render(ContentContext& renderer,
465462
*renderer.GetContext(), *renderer.GetRenderTargetCache(),
466463
color0.texture->GetSize(),
467464
renderer.GetContext()->GetCapabilities()->SupportsOffscreenMSAA(),
468-
"ImpellerOnscreen",
469-
GetDefaultStencilConfig(reads_from_onscreen_backdrop));
465+
"ImpellerOnscreen", kDefaultStencilConfig);
470466
}
471467

472468
// Set up the clear color of the root pass.
@@ -503,10 +499,10 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
503499
//--------------------------------------------------------------------------
504500
/// Setup entity element.
505501
///
506-
507502
if (const auto& entity = std::get_if<Entity>(&element)) {
508503
element_entity = *entity;
509504
element_entity.SetCapture(capture.CreateChild("Entity"));
505+
510506
if (!global_pass_position.IsZero()) {
511507
// If the pass image is going to be rendered with a non-zero position,
512508
// apply the negative translation to entity copies before rendering them
@@ -515,16 +511,15 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
515511
Matrix::MakeTranslation(Vector3(-global_pass_position)) *
516512
element_entity.GetTransformation());
517513
}
514+
return EntityPass::EntityResult::Success(element_entity);
518515
}
519516

520517
//--------------------------------------------------------------------------
521518
/// Setup subpass element.
522519
///
523-
524-
else if (const auto& subpass_ptr =
525-
std::get_if<std::unique_ptr<EntityPass>>(&element)) {
520+
if (const auto& subpass_ptr =
521+
std::get_if<std::unique_ptr<EntityPass>>(&element)) {
526522
auto subpass = subpass_ptr->get();
527-
528523
if (subpass->delegate_->CanElide()) {
529524
return EntityPass::EntityResult::Skip();
530525
}
@@ -625,10 +620,9 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
625620
}
626621

627622
auto subpass_target = CreateRenderTarget(
628-
renderer, // renderer
629-
subpass_size, // size
630-
subpass->GetTotalPassReads(renderer) > 0, // readable
631-
subpass->GetClearColor(subpass_size)); // clear_color
623+
renderer, // renderer
624+
subpass_size, // size
625+
subpass->GetClearColor(subpass_size)); // clear_color
632626

633627
if (!subpass_target.IsValid()) {
634628
VALIDATION_LOG << "Subpass render target is invalid.";
@@ -696,11 +690,10 @@ EntityPass::EntityResult EntityPass::GetEntityForElement(
696690
element_entity.SetTransformation(subpass_texture_capture.AddMatrix(
697691
"Transform", Matrix::MakeTranslation(Vector3(subpass_coverage->origin -
698692
global_pass_position))));
699-
} else {
700-
FML_UNREACHABLE();
701-
}
702693

703-
return EntityPass::EntityResult::Success(element_entity);
694+
return EntityPass::EntityResult::Success(element_entity);
695+
}
696+
FML_UNREACHABLE();
704697
}
705698

706699
bool EntityPass::RenderElement(Entity& element_entity,
@@ -724,6 +717,15 @@ bool EntityPass::RenderElement(Entity& element_entity,
724717
// blit the non-MSAA resolve texture of the previous pass to MSAA textures
725718
// (let alone a transient one).
726719
if (result.backdrop_texture) {
720+
// Restore any clips that were recorded before the backdrop filter was
721+
// applied.
722+
auto& replay_entities = clip_replay_->GetReplayEntities();
723+
for (const auto& entity : replay_entities) {
724+
if (!entity.Render(renderer, *result.pass)) {
725+
VALIDATION_LOG << "Failed to render entity for clip restore.";
726+
}
727+
}
728+
727729
auto size_rect = Rect::MakeSize(result.pass->GetRenderTargetSize());
728730
auto msaa_backdrop_contents = TextureContents::MakeRect(size_rect);
729731
msaa_backdrop_contents->SetStencilEnabled(false);
@@ -836,6 +838,7 @@ bool EntityPass::RenderElement(Entity& element_entity,
836838
#endif
837839

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

1186+
EntityPassClipRecorder::EntityPassClipRecorder() {}
1187+
1188+
void EntityPassClipRecorder::RecordEntity(const Entity& entity,
1189+
Contents::ClipCoverage::Type type) {
1190+
switch (type) {
1191+
case Contents::ClipCoverage::Type::kNoChange:
1192+
return;
1193+
case Contents::ClipCoverage::Type::kAppend:
1194+
rendered_clip_entities_.push_back(entity);
1195+
break;
1196+
case Contents::ClipCoverage::Type::kRestore:
1197+
rendered_clip_entities_.pop_back();
1198+
break;
1199+
}
1200+
}
1201+
1202+
const std::vector<Entity>& EntityPassClipRecorder::GetReplayEntities() const {
1203+
return rendered_clip_entities_;
1204+
}
1205+
11831206
} // namespace impeller

impeller/entity/entity_pass.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
namespace impeller {
2020

2121
class ContentContext;
22+
class EntityPassClipRecorder;
2223

2324
class EntityPass {
2425
public:
@@ -285,6 +286,8 @@ class EntityPass {
285286
bool flood_clip_ = false;
286287
bool enable_offscreen_debug_checkerboard_ = false;
287288
std::optional<Rect> bounds_limit_;
289+
std::unique_ptr<EntityPassClipRecorder> clip_replay_ =
290+
std::make_unique<EntityPassClipRecorder>();
288291

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

315+
/// @brief A class that tracks all clips that have been recorded in the current
316+
/// entity pass stencil.
317+
///
318+
/// These clips are replayed when restoring the backdrop so that the
319+
/// stencil buffer is left in an identical state.
320+
class EntityPassClipRecorder {
321+
public:
322+
EntityPassClipRecorder();
323+
324+
~EntityPassClipRecorder() = default;
325+
326+
/// @brief Record the entity based on the provided coverage [type].
327+
void RecordEntity(const Entity& entity, Contents::ClipCoverage::Type type);
328+
329+
const std::vector<Entity>& GetReplayEntities() const;
330+
331+
private:
332+
std::vector<Entity> rendered_clip_entities_;
333+
};
334+
312335
} // namespace impeller

impeller/entity/inline_pass_context.cc

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ InlinePassContext::InlinePassContext(
2121
std::optional<RenderPassResult> collapsed_parent_pass)
2222
: context_(std::move(context)),
2323
pass_target_(pass_target),
24-
total_pass_reads_(pass_texture_reads),
2524
is_collapsed_(collapsed_parent_pass.has_value()) {
2625
if (collapsed_parent_pass.has_value()) {
2726
pass_ = collapsed_parent_pass.value().pass;
@@ -140,17 +139,9 @@ InlinePassContext::RenderPassResult InlinePassContext::GetRenderPass(
140139
return {};
141140
}
142141

143-
// Only clear the stencil if this is the very first pass of the
144-
// layer.
145-
stencil->load_action =
146-
pass_count_ > 0 ? LoadAction::kLoad : LoadAction::kClear;
147-
// If we're on the last pass of the layer, there's no need to store the
148-
// stencil because nothing needs to read it.
149-
stencil->store_action = pass_count_ == total_pass_reads_
150-
? StoreAction::kDontCare
151-
: StoreAction::kStore;
142+
stencil->load_action = LoadAction::kClear;
143+
stencil->store_action = StoreAction::kDontCare;
152144
pass_target_.target_.SetStencilAttachment(stencil.value());
153-
154145
pass_target_.target_.SetColorAttachment(color0, 0);
155146

156147
pass_ = command_buffer_->CreateRenderPass(pass_target_.GetRenderTarget());

impeller/entity/inline_pass_context.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class InlinePassContext {
4040
std::shared_ptr<CommandBuffer> command_buffer_;
4141
std::shared_ptr<RenderPass> pass_;
4242
uint32_t pass_count_ = 0;
43-
uint32_t total_pass_reads_ = 0;
43+
4444
// Whether this context is collapsed into a parent entity pass.
4545
bool is_collapsed_ = false;
4646

0 commit comments

Comments
 (0)