| // 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/inline_pass_context.h" |
| |
| #include <utility> |
| |
| #include "impeller/base/validation.h" |
| #include "impeller/core/formats.h" |
| #include "impeller/core/texture_descriptor.h" |
| #include "impeller/entity/entity_pass_target.h" |
| #include "impeller/renderer/command_buffer.h" |
| |
| namespace impeller { |
| |
| InlinePassContext::InlinePassContext( |
| std::shared_ptr<Context> context, |
| EntityPassTarget& pass_target, |
| uint32_t pass_texture_reads, |
| 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; |
| } |
| } |
| |
| InlinePassContext::~InlinePassContext() { |
| if (!is_collapsed_) { |
| EndPass(); |
| } |
| } |
| |
| bool InlinePassContext::IsValid() const { |
| return pass_target_.IsValid(); |
| } |
| |
| bool InlinePassContext::IsActive() const { |
| return pass_ != nullptr; |
| } |
| |
| std::shared_ptr<Texture> InlinePassContext::GetTexture() { |
| if (!IsValid()) { |
| return nullptr; |
| } |
| return pass_target_.GetRenderTarget().GetRenderTargetTexture(); |
| } |
| |
| bool InlinePassContext::EndPass() { |
| if (!IsActive()) { |
| return true; |
| } |
| |
| if (command_buffer_) { |
| if (!command_buffer_->SubmitCommandsAsync(std::move(pass_))) { |
| VALIDATION_LOG |
| << "Failed to encode and submit command buffer while ending " |
| "render pass."; |
| return false; |
| } |
| } |
| |
| pass_ = nullptr; |
| command_buffer_ = nullptr; |
| |
| return true; |
| } |
| |
| EntityPassTarget& InlinePassContext::GetPassTarget() const { |
| return pass_target_; |
| } |
| |
| InlinePassContext::RenderPassResult InlinePassContext::GetRenderPass( |
| uint32_t pass_depth) { |
| if (IsActive()) { |
| return {.pass = pass_}; |
| } |
| |
| /// Create a new render pass if one isn't active. This path will run the first |
| /// time this method is called, but it'll also run if the pass has been |
| /// previously ended via `EndPass`. |
| |
| command_buffer_ = context_->CreateCommandBuffer(); |
| if (!command_buffer_) { |
| VALIDATION_LOG << "Could not create command buffer."; |
| return {}; |
| } |
| |
| if (pass_target_.GetRenderTarget().GetColorAttachments().empty()) { |
| VALIDATION_LOG << "Color attachment unexpectedly missing from the " |
| "EntityPass render target."; |
| return {}; |
| } |
| |
| command_buffer_->SetLabel( |
| "EntityPass Command Buffer: Depth=" + std::to_string(pass_depth) + |
| " Count=" + std::to_string(pass_count_)); |
| |
| RenderPassResult result; |
| { |
| // If the pass target has a resolve texture, then we're using MSAA. |
| bool is_msaa = pass_target_.GetRenderTarget() |
| .GetColorAttachments() |
| .find(0) |
| ->second.resolve_texture != nullptr; |
| if (pass_count_ > 0 && is_msaa) { |
| result.backdrop_texture = |
| pass_target_.Flip(*context_->GetResourceAllocator()); |
| if (!result.backdrop_texture) { |
| VALIDATION_LOG << "Could not flip the EntityPass render target."; |
| } |
| } |
| } |
| |
| // Find the color attachment a second time, since the target may have just |
| // flipped. |
| auto color0 = |
| pass_target_.GetRenderTarget().GetColorAttachments().find(0)->second; |
| bool is_msaa = color0.resolve_texture != nullptr; |
| |
| if (pass_count_ > 0) { |
| // When MSAA is being used, we end up overriding the entire backdrop by |
| // drawing the previous pass texture, and so we don't have to clear it and |
| // can use kDontCare. |
| color0.load_action = is_msaa ? LoadAction::kDontCare : LoadAction::kLoad; |
| } else { |
| color0.load_action = LoadAction::kClear; |
| } |
| |
| color0.store_action = |
| is_msaa ? StoreAction::kMultisampleResolve : StoreAction::kStore; |
| |
| auto stencil = pass_target_.GetRenderTarget().GetStencilAttachment(); |
| if (!stencil.has_value()) { |
| VALIDATION_LOG << "Stencil attachment unexpectedly missing from the " |
| "EntityPass render target."; |
| 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; |
| pass_target_.target_.SetStencilAttachment(stencil.value()); |
| |
| pass_target_.target_.SetColorAttachment(color0, 0); |
| |
| pass_ = command_buffer_->CreateRenderPass(pass_target_.GetRenderTarget()); |
| if (!pass_) { |
| VALIDATION_LOG << "Could not create render pass."; |
| return {}; |
| } |
| |
| pass_->SetLabel( |
| "EntityPass Render Pass: Depth=" + std::to_string(pass_depth) + |
| " Count=" + std::to_string(pass_count_)); |
| |
| result.pass = pass_; |
| |
| if (!context_->GetCapabilities()->SupportsReadFromResolve() && |
| result.backdrop_texture == |
| result.pass->GetRenderTarget().GetRenderTargetTexture()) { |
| VALIDATION_LOG << "EntityPass backdrop restore configuration is not valid " |
| "for the current graphics backend."; |
| } |
| |
| ++pass_count_; |
| return result; |
| } |
| |
| uint32_t InlinePassContext::GetPassCount() const { |
| return pass_count_; |
| } |
| |
| } // namespace impeller |