| // 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/renderer/backend/vulkan/render_pass_vk.h" |
| |
| #include <array> |
| #include <cstdint> |
| #include <vector> |
| |
| #include "fml/status.h" |
| #include "impeller/base/validation.h" |
| #include "impeller/core/device_buffer.h" |
| #include "impeller/core/formats.h" |
| #include "impeller/core/texture.h" |
| #include "impeller/renderer/backend/vulkan/barrier_vk.h" |
| #include "impeller/renderer/backend/vulkan/command_buffer_vk.h" |
| #include "impeller/renderer/backend/vulkan/command_encoder_vk.h" |
| #include "impeller/renderer/backend/vulkan/context_vk.h" |
| #include "impeller/renderer/backend/vulkan/device_buffer_vk.h" |
| #include "impeller/renderer/backend/vulkan/formats_vk.h" |
| #include "impeller/renderer/backend/vulkan/pipeline_vk.h" |
| #include "impeller/renderer/backend/vulkan/render_pass_builder_vk.h" |
| #include "impeller/renderer/backend/vulkan/sampler_vk.h" |
| #include "impeller/renderer/backend/vulkan/shared_object_vk.h" |
| #include "impeller/renderer/backend/vulkan/texture_vk.h" |
| #include "vulkan/vulkan_handles.hpp" |
| |
| namespace impeller { |
| |
| // Warning: if any of the constant values or layouts are changed in the |
| // framebuffer fetch shader, then this input binding may need to be |
| // manually changed. |
| // |
| // See: impeller/entity/shaders/blending/framebuffer_blend.frag |
| static constexpr size_t kMagicSubpassInputBinding = 64u; |
| |
| static vk::ClearColorValue VKClearValueFromColor(Color color) { |
| vk::ClearColorValue value; |
| value.setFloat32( |
| std::array<float, 4>{color.red, color.green, color.blue, color.alpha}); |
| return value; |
| } |
| |
| static vk::ClearDepthStencilValue VKClearValueFromDepthStencil(uint32_t stencil, |
| Scalar depth) { |
| vk::ClearDepthStencilValue value; |
| value.depth = depth; |
| value.stencil = stencil; |
| return value; |
| } |
| |
| static std::vector<vk::ClearValue> GetVKClearValues( |
| const RenderTarget& target) { |
| std::vector<vk::ClearValue> clears; |
| |
| for (const auto& [_, color] : target.GetColorAttachments()) { |
| clears.emplace_back(VKClearValueFromColor(color.clear_color)); |
| if (color.resolve_texture) { |
| clears.emplace_back(VKClearValueFromColor(color.clear_color)); |
| } |
| } |
| |
| const auto& depth = target.GetDepthAttachment(); |
| const auto& stencil = target.GetStencilAttachment(); |
| |
| if (depth.has_value()) { |
| clears.emplace_back(VKClearValueFromDepthStencil( |
| stencil ? stencil->clear_stencil : 0u, depth->clear_depth)); |
| } else if (stencil.has_value()) { |
| clears.emplace_back(VKClearValueFromDepthStencil( |
| stencil->clear_stencil, depth ? depth->clear_depth : 0.0f)); |
| } |
| |
| return clears; |
| } |
| |
| SharedHandleVK<vk::RenderPass> RenderPassVK::CreateVKRenderPass( |
| const ContextVK& context, |
| const SharedHandleVK<vk::RenderPass>& recycled_renderpass, |
| const std::shared_ptr<CommandBufferVK>& command_buffer) const { |
| BarrierVK barrier; |
| barrier.new_layout = vk::ImageLayout::eGeneral; |
| barrier.cmd_buffer = command_buffer->GetEncoder()->GetCommandBuffer(); |
| barrier.src_access = vk::AccessFlagBits::eShaderRead; |
| barrier.src_stage = vk::PipelineStageFlagBits::eFragmentShader; |
| barrier.dst_access = vk::AccessFlagBits::eColorAttachmentWrite | |
| vk::AccessFlagBits::eTransferWrite; |
| barrier.dst_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput | |
| vk::PipelineStageFlagBits::eTransfer; |
| |
| RenderPassBuilderVK builder; |
| |
| for (const auto& [bind_point, color] : render_target_.GetColorAttachments()) { |
| builder.SetColorAttachment( |
| bind_point, // |
| color.texture->GetTextureDescriptor().format, // |
| color.texture->GetTextureDescriptor().sample_count, // |
| color.load_action, // |
| color.store_action // |
| ); |
| TextureVK::Cast(*color.texture).SetLayout(barrier); |
| if (color.resolve_texture) { |
| TextureVK::Cast(*color.resolve_texture).SetLayout(barrier); |
| } |
| } |
| |
| if (auto depth = render_target_.GetDepthAttachment(); depth.has_value()) { |
| builder.SetDepthStencilAttachment( |
| depth->texture->GetTextureDescriptor().format, // |
| depth->texture->GetTextureDescriptor().sample_count, // |
| depth->load_action, // |
| depth->store_action // |
| ); |
| } else if (auto stencil = render_target_.GetStencilAttachment(); |
| stencil.has_value()) { |
| builder.SetStencilAttachment( |
| stencil->texture->GetTextureDescriptor().format, // |
| stencil->texture->GetTextureDescriptor().sample_count, // |
| stencil->load_action, // |
| stencil->store_action // |
| ); |
| } |
| |
| if (recycled_renderpass != nullptr) { |
| return recycled_renderpass; |
| } |
| |
| auto pass = builder.Build(context.GetDevice()); |
| |
| if (!pass) { |
| VALIDATION_LOG << "Failed to create render pass for framebuffer."; |
| return {}; |
| } |
| |
| context.SetDebugName(pass.get(), debug_label_.c_str()); |
| |
| return MakeSharedVK(std::move(pass)); |
| } |
| |
| RenderPassVK::RenderPassVK(const std::shared_ptr<const Context>& context, |
| const RenderTarget& target, |
| std::shared_ptr<CommandBufferVK> command_buffer) |
| : RenderPass(context, target), command_buffer_(std::move(command_buffer)) { |
| color_image_vk_ = |
| render_target_.GetColorAttachments().find(0u)->second.texture; |
| resolve_image_vk_ = |
| render_target_.GetColorAttachments().find(0u)->second.resolve_texture; |
| |
| const auto& vk_context = ContextVK::Cast(*context); |
| const std::shared_ptr<CommandEncoderVK>& encoder = |
| command_buffer_->GetEncoder(); |
| command_buffer_vk_ = encoder->GetCommandBuffer(); |
| render_target_.IterateAllAttachments( |
| [&encoder](const auto& attachment) -> bool { |
| encoder->Track(attachment.texture); |
| encoder->Track(attachment.resolve_texture); |
| return true; |
| }); |
| |
| SharedHandleVK<vk::RenderPass> recycled_render_pass; |
| SharedHandleVK<vk::Framebuffer> recycled_framebuffer; |
| if (resolve_image_vk_) { |
| recycled_render_pass = |
| TextureVK::Cast(*resolve_image_vk_).GetCachedRenderPass(); |
| recycled_framebuffer = |
| TextureVK::Cast(*resolve_image_vk_).GetCachedFramebuffer(); |
| } |
| |
| const auto& target_size = render_target_.GetRenderTargetSize(); |
| |
| render_pass_ = |
| CreateVKRenderPass(vk_context, recycled_render_pass, command_buffer_); |
| if (!render_pass_) { |
| VALIDATION_LOG << "Could not create renderpass."; |
| is_valid_ = false; |
| return; |
| } |
| |
| auto framebuffer = (recycled_framebuffer == nullptr) |
| ? CreateVKFramebuffer(vk_context, *render_pass_) |
| : recycled_framebuffer; |
| if (!framebuffer) { |
| VALIDATION_LOG << "Could not create framebuffer."; |
| is_valid_ = false; |
| return; |
| } |
| |
| if (!encoder->Track(framebuffer) || !encoder->Track(render_pass_)) { |
| is_valid_ = false; |
| return; |
| } |
| if (resolve_image_vk_) { |
| TextureVK::Cast(*resolve_image_vk_).SetCachedFramebuffer(framebuffer); |
| TextureVK::Cast(*resolve_image_vk_).SetCachedRenderPass(render_pass_); |
| } |
| |
| auto clear_values = GetVKClearValues(render_target_); |
| |
| vk::RenderPassBeginInfo pass_info; |
| pass_info.renderPass = *render_pass_; |
| pass_info.framebuffer = *framebuffer; |
| pass_info.renderArea.extent.width = static_cast<uint32_t>(target_size.width); |
| pass_info.renderArea.extent.height = |
| static_cast<uint32_t>(target_size.height); |
| pass_info.setClearValues(clear_values); |
| |
| command_buffer_vk_.beginRenderPass(pass_info, vk::SubpassContents::eInline); |
| |
| // Set the initial viewport. |
| const auto vp = Viewport{.rect = Rect::MakeSize(target_size)}; |
| vk::Viewport viewport = vk::Viewport() |
| .setWidth(vp.rect.GetWidth()) |
| .setHeight(-vp.rect.GetHeight()) |
| .setY(vp.rect.GetHeight()) |
| .setMinDepth(0.0f) |
| .setMaxDepth(1.0f); |
| command_buffer_vk_.setViewport(0, 1, &viewport); |
| |
| // Set the initial scissor. |
| const auto sc = IRect::MakeSize(target_size); |
| vk::Rect2D scissor = |
| vk::Rect2D() |
| .setOffset(vk::Offset2D(sc.GetX(), sc.GetY())) |
| .setExtent(vk::Extent2D(sc.GetWidth(), sc.GetHeight())); |
| command_buffer_vk_.setScissor(0, 1, &scissor); |
| |
| // Set the initial stencil reference. |
| command_buffer_vk_.setStencilReference( |
| vk::StencilFaceFlagBits::eVkStencilFrontAndBack, 0u); |
| |
| is_valid_ = true; |
| } |
| |
| RenderPassVK::~RenderPassVK() = default; |
| |
| bool RenderPassVK::IsValid() const { |
| return is_valid_; |
| } |
| |
| void RenderPassVK::OnSetLabel(std::string label) { |
| #ifdef IMPELLER_DEBUG |
| ContextVK::Cast(*context_).SetDebugName(render_pass_->Get(), |
| std::string(label).c_str()); |
| #endif // IMPELLER_DEBUG |
| } |
| |
| SharedHandleVK<vk::Framebuffer> RenderPassVK::CreateVKFramebuffer( |
| const ContextVK& context, |
| const vk::RenderPass& pass) const { |
| vk::FramebufferCreateInfo fb_info; |
| |
| fb_info.renderPass = pass; |
| |
| const auto target_size = render_target_.GetRenderTargetSize(); |
| fb_info.width = target_size.width; |
| fb_info.height = target_size.height; |
| fb_info.layers = 1u; |
| |
| std::vector<vk::ImageView> attachments; |
| |
| // This bit must be consistent to ensure compatibility with the pass created |
| // earlier. Follow this order: Color attachments, then depth-stencil, then |
| // stencil. |
| for (const auto& [_, color] : render_target_.GetColorAttachments()) { |
| // The bind point doesn't matter here since that information is present in |
| // the render pass. |
| attachments.emplace_back( |
| TextureVK::Cast(*color.texture).GetRenderTargetView()); |
| if (color.resolve_texture) { |
| attachments.emplace_back( |
| TextureVK::Cast(*color.resolve_texture).GetRenderTargetView()); |
| } |
| } |
| if (auto depth = render_target_.GetDepthAttachment(); depth.has_value()) { |
| attachments.emplace_back( |
| TextureVK::Cast(*depth->texture).GetRenderTargetView()); |
| } else if (auto stencil = render_target_.GetStencilAttachment(); |
| stencil.has_value()) { |
| attachments.emplace_back( |
| TextureVK::Cast(*stencil->texture).GetRenderTargetView()); |
| } |
| |
| fb_info.setAttachments(attachments); |
| |
| auto [result, framebuffer] = |
| context.GetDevice().createFramebufferUnique(fb_info); |
| |
| if (result != vk::Result::eSuccess) { |
| VALIDATION_LOG << "Could not create framebuffer: " << vk::to_string(result); |
| return {}; |
| } |
| |
| return MakeSharedVK(std::move(framebuffer)); |
| } |
| |
| // |RenderPass| |
| void RenderPassVK::SetPipeline( |
| const std::shared_ptr<Pipeline<PipelineDescriptor>>& pipeline) { |
| pipeline_ = pipeline.get(); |
| if (!pipeline_) { |
| return; |
| } |
| |
| pipeline_uses_input_attachments_ = |
| pipeline_->GetDescriptor().GetVertexDescriptor()->UsesInputAttacments(); |
| |
| if (pipeline_uses_input_attachments_) { |
| if (bound_image_offset_ >= kMaxBindings) { |
| pipeline_ = nullptr; |
| return; |
| } |
| vk::DescriptorImageInfo image_info; |
| image_info.imageLayout = vk::ImageLayout::eGeneral; |
| image_info.sampler = VK_NULL_HANDLE; |
| image_info.imageView = TextureVK::Cast(*color_image_vk_).GetImageView(); |
| image_workspace_[bound_image_offset_++] = image_info; |
| |
| vk::WriteDescriptorSet write_set; |
| write_set.dstBinding = kMagicSubpassInputBinding; |
| write_set.descriptorCount = 1u; |
| write_set.descriptorType = vk::DescriptorType::eInputAttachment; |
| write_set.pImageInfo = &image_workspace_[bound_image_offset_ - 1]; |
| |
| write_workspace_[descriptor_write_offset_++] = write_set; |
| } |
| } |
| |
| // |RenderPass| |
| void RenderPassVK::SetCommandLabel(std::string_view label) { |
| #ifdef IMPELLER_DEBUG |
| command_buffer_->GetEncoder()->PushDebugGroup(label); |
| has_label_ = true; |
| #endif // IMPELLER_DEBUG |
| } |
| |
| // |RenderPass| |
| void RenderPassVK::SetStencilReference(uint32_t value) { |
| command_buffer_vk_.setStencilReference( |
| vk::StencilFaceFlagBits::eVkStencilFrontAndBack, value); |
| } |
| |
| // |RenderPass| |
| void RenderPassVK::SetBaseVertex(uint64_t value) { |
| base_vertex_ = value; |
| } |
| |
| // |RenderPass| |
| void RenderPassVK::SetViewport(Viewport viewport) { |
| vk::Viewport viewport_vk = vk::Viewport() |
| .setWidth(viewport.rect.GetWidth()) |
| .setHeight(-viewport.rect.GetHeight()) |
| .setY(viewport.rect.GetHeight()) |
| .setMinDepth(0.0f) |
| .setMaxDepth(1.0f); |
| command_buffer_vk_.setViewport(0, 1, &viewport_vk); |
| } |
| |
| // |RenderPass| |
| void RenderPassVK::SetScissor(IRect scissor) { |
| vk::Rect2D scissor_vk = |
| vk::Rect2D() |
| .setOffset(vk::Offset2D(scissor.GetX(), scissor.GetY())) |
| .setExtent(vk::Extent2D(scissor.GetWidth(), scissor.GetHeight())); |
| command_buffer_vk_.setScissor(0, 1, &scissor_vk); |
| } |
| |
| // |RenderPass| |
| void RenderPassVK::SetInstanceCount(size_t count) { |
| instance_count_ = count; |
| } |
| |
| // |RenderPass| |
| bool RenderPassVK::SetVertexBuffer(VertexBuffer buffer) { |
| vertex_count_ = buffer.vertex_count; |
| if (buffer.index_type == IndexType::kUnknown || !buffer.vertex_buffer) { |
| return false; |
| } |
| |
| if (!command_buffer_->GetEncoder()->Track(buffer.vertex_buffer.buffer)) { |
| return false; |
| } |
| |
| // Bind the vertex buffer. |
| vk::Buffer vertex_buffer_handle = |
| DeviceBufferVK::Cast(*buffer.vertex_buffer.buffer).GetBuffer(); |
| vk::Buffer vertex_buffers[] = {vertex_buffer_handle}; |
| vk::DeviceSize vertex_buffer_offsets[] = {buffer.vertex_buffer.range.offset}; |
| |
| command_buffer_vk_.bindVertexBuffers(0u, 1u, vertex_buffers, |
| vertex_buffer_offsets); |
| |
| // Bind the index buffer. |
| if (buffer.index_type != IndexType::kNone) { |
| has_index_buffer_ = true; |
| const BufferView& index_buffer_view = buffer.index_buffer; |
| if (!index_buffer_view) { |
| return false; |
| } |
| |
| const std::shared_ptr<const DeviceBuffer>& index_buffer = |
| index_buffer_view.buffer; |
| if (!index_buffer) { |
| VALIDATION_LOG << "Failed to acquire device buffer" |
| << " for index buffer view"; |
| return false; |
| } |
| |
| if (!command_buffer_->GetEncoder()->Track(index_buffer)) { |
| return false; |
| } |
| |
| vk::Buffer index_buffer_handle = |
| DeviceBufferVK::Cast(*index_buffer).GetBuffer(); |
| command_buffer_vk_.bindIndexBuffer(index_buffer_handle, |
| index_buffer_view.range.offset, |
| ToVKIndexType(buffer.index_type)); |
| } else { |
| has_index_buffer_ = false; |
| } |
| return true; |
| } |
| |
| // |RenderPass| |
| fml::Status RenderPassVK::Draw() { |
| if (!pipeline_) { |
| return fml::Status(fml::StatusCode::kCancelled, |
| "No valid pipeline is bound to the RenderPass."); |
| } |
| |
| //---------------------------------------------------------------------------- |
| /// If there are immutable samplers referenced in the render pass, the base |
| /// pipeline variant is no longer valid and needs to be re-constructed to |
| /// reference the samplers. |
| /// |
| /// This is an instance of JIT creation of PSOs that can cause jank. It is |
| /// unavoidable because it isn't possible to know all possible combinations of |
| /// target YUV conversions. Fortunately, this will only ever happen when |
| /// rendering to external textures. Like Android Hardware Buffers on Android. |
| /// |
| /// Even when JIT creation is unavoidable, pipelines will cache their variants |
| /// when able and all pipeline creation will happen via a base pipeline cache |
| /// anyway. So the jank can be mostly entirely ameliorated and it should only |
| /// ever happen when the first unknown YUV conversion is encountered. |
| /// |
| /// Jank can be completely eliminated by pre-populating known YUV conversion |
| /// pipelines. |
| if (immutable_sampler_) { |
| std::shared_ptr<PipelineVK> pipeline_variant = |
| PipelineVK::Cast(*pipeline_) |
| .CreateVariantForImmutableSamplers(immutable_sampler_); |
| if (!pipeline_variant) { |
| return fml::Status( |
| fml::StatusCode::kAborted, |
| "Could not create pipeline variant with immutable sampler."); |
| } |
| pipeline_ = pipeline_variant.get(); |
| } |
| |
| const auto& context_vk = ContextVK::Cast(*context_); |
| const auto& pipeline_vk = PipelineVK::Cast(*pipeline_); |
| |
| auto descriptor_result = |
| command_buffer_->GetEncoder()->AllocateDescriptorSets( |
| pipeline_vk.GetDescriptorSetLayout(), context_vk); |
| if (!descriptor_result.ok()) { |
| return fml::Status(fml::StatusCode::kAborted, |
| "Could not allocate descriptor sets."); |
| } |
| const auto descriptor_set = descriptor_result.value(); |
| const auto pipeline_layout = pipeline_vk.GetPipelineLayout(); |
| command_buffer_vk_.bindPipeline(vk::PipelineBindPoint::eGraphics, |
| pipeline_vk.GetPipeline()); |
| |
| for (auto i = 0u; i < descriptor_write_offset_; i++) { |
| write_workspace_[i].dstSet = descriptor_set; |
| } |
| |
| context_vk.GetDevice().updateDescriptorSets(descriptor_write_offset_, |
| write_workspace_.data(), 0u, {}); |
| |
| command_buffer_vk_.bindDescriptorSets( |
| vk::PipelineBindPoint::eGraphics, // bind point |
| pipeline_layout, // layout |
| 0, // first set |
| 1, // set count |
| &descriptor_set, // sets |
| 0, // offset count |
| nullptr // offsets |
| ); |
| |
| if (pipeline_uses_input_attachments_) { |
| InsertBarrierForInputAttachmentRead( |
| command_buffer_vk_, TextureVK::Cast(*color_image_vk_).GetImage()); |
| } |
| |
| if (has_index_buffer_) { |
| command_buffer_vk_.drawIndexed(vertex_count_, // index count |
| instance_count_, // instance count |
| 0u, // first index |
| base_vertex_, // vertex offset |
| 0u // first instance |
| ); |
| } else { |
| command_buffer_vk_.draw(vertex_count_, // vertex count |
| instance_count_, // instance count |
| base_vertex_, // vertex offset |
| 0u // first instance |
| ); |
| } |
| |
| #ifdef IMPELLER_DEBUG |
| if (has_label_) { |
| command_buffer_->GetEncoder()->PopDebugGroup(); |
| } |
| #endif // IMPELLER_DEBUG |
| has_label_ = false; |
| has_index_buffer_ = false; |
| bound_image_offset_ = 0u; |
| bound_buffer_offset_ = 0u; |
| descriptor_write_offset_ = 0u; |
| instance_count_ = 1u; |
| base_vertex_ = 0u; |
| vertex_count_ = 0u; |
| pipeline_ = nullptr; |
| pipeline_uses_input_attachments_ = false; |
| immutable_sampler_ = nullptr; |
| return fml::Status(); |
| } |
| |
| // The RenderPassVK binding methods only need the binding, set, and buffer type |
| // information. |
| bool RenderPassVK::BindResource(ShaderStage stage, |
| DescriptorType type, |
| const ShaderUniformSlot& slot, |
| const ShaderMetadata& metadata, |
| BufferView view) { |
| return BindResource(slot.binding, type, view); |
| } |
| |
| bool RenderPassVK::BindResource( |
| ShaderStage stage, |
| DescriptorType type, |
| const ShaderUniformSlot& slot, |
| const std::shared_ptr<const ShaderMetadata>& metadata, |
| BufferView view) { |
| return BindResource(slot.binding, type, view); |
| } |
| |
| bool RenderPassVK::BindResource(size_t binding, |
| DescriptorType type, |
| const BufferView& view) { |
| if (bound_buffer_offset_ >= kMaxBindings) { |
| return false; |
| } |
| |
| const std::shared_ptr<const DeviceBuffer>& device_buffer = view.buffer; |
| auto buffer = DeviceBufferVK::Cast(*device_buffer).GetBuffer(); |
| if (!buffer) { |
| return false; |
| } |
| |
| if (!command_buffer_->GetEncoder()->Track(device_buffer)) { |
| return false; |
| } |
| |
| uint32_t offset = view.range.offset; |
| |
| vk::DescriptorBufferInfo buffer_info; |
| buffer_info.buffer = buffer; |
| buffer_info.offset = offset; |
| buffer_info.range = view.range.length; |
| buffer_workspace_[bound_buffer_offset_++] = buffer_info; |
| |
| vk::WriteDescriptorSet write_set; |
| write_set.dstBinding = binding; |
| write_set.descriptorCount = 1u; |
| write_set.descriptorType = ToVKDescriptorType(type); |
| write_set.pBufferInfo = &buffer_workspace_[bound_buffer_offset_ - 1]; |
| |
| write_workspace_[descriptor_write_offset_++] = write_set; |
| return true; |
| } |
| |
| bool RenderPassVK::BindResource(ShaderStage stage, |
| DescriptorType type, |
| const SampledImageSlot& slot, |
| const ShaderMetadata& metadata, |
| std::shared_ptr<const Texture> texture, |
| const std::unique_ptr<const Sampler>& sampler) { |
| if (bound_buffer_offset_ >= kMaxBindings) { |
| return false; |
| } |
| if (!texture->IsValid() || !sampler) { |
| return false; |
| } |
| const TextureVK& texture_vk = TextureVK::Cast(*texture); |
| const SamplerVK& sampler_vk = SamplerVK::Cast(*sampler); |
| |
| if (!command_buffer_->GetEncoder()->Track(texture)) { |
| return false; |
| } |
| |
| if (!immutable_sampler_) { |
| immutable_sampler_ = texture_vk.GetImmutableSamplerVariant(sampler_vk); |
| } |
| |
| vk::DescriptorImageInfo image_info; |
| image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal; |
| image_info.sampler = sampler_vk.GetSampler(); |
| image_info.imageView = texture_vk.GetImageView(); |
| image_workspace_[bound_image_offset_++] = image_info; |
| |
| vk::WriteDescriptorSet write_set; |
| write_set.dstBinding = slot.binding; |
| write_set.descriptorCount = 1u; |
| write_set.descriptorType = vk::DescriptorType::eCombinedImageSampler; |
| write_set.pImageInfo = &image_workspace_[bound_image_offset_ - 1]; |
| |
| write_workspace_[descriptor_write_offset_++] = write_set; |
| return true; |
| } |
| |
| bool RenderPassVK::OnEncodeCommands(const Context& context) const { |
| command_buffer_->GetEncoder()->GetCommandBuffer().endRenderPass(); |
| |
| // If this render target will be consumed by a subsequent render pass, |
| // perform a layout transition to a shader read state. |
| const std::shared_ptr<Texture>& result_texture = |
| resolve_image_vk_ ? resolve_image_vk_ : color_image_vk_; |
| if (result_texture->GetTextureDescriptor().usage & |
| TextureUsage::kShaderRead) { |
| BarrierVK barrier; |
| barrier.cmd_buffer = command_buffer_vk_; |
| barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite | |
| vk::AccessFlagBits::eTransferWrite; |
| barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput | |
| vk::PipelineStageFlagBits::eTransfer; |
| barrier.dst_access = vk::AccessFlagBits::eShaderRead; |
| barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader; |
| |
| barrier.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal; |
| |
| if (!TextureVK::Cast(*result_texture).SetLayout(barrier)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace impeller |