Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Impeller] stencil buffer record/replay instead of MSAA storage. #47397

Merged
merged 10 commits into from
Nov 1, 2023
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to remove the readable param from CreateRenderTarget now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

);
} 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good riddance...


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

Expand Down