| // 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), | |
| 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 {}; | |
| } | |
| 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()); | |
| 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 |