| // 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/compute_pass_vk.h" |
| |
| #include "flutter/fml/trace_event.h" |
| #include "impeller/renderer/backend/vulkan/binding_helpers_vk.h" |
| #include "impeller/renderer/backend/vulkan/command_buffer_vk.h" |
| #include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h" |
| #include "impeller/renderer/backend/vulkan/texture_vk.h" |
| #include "impeller/renderer/command.h" |
| |
| namespace impeller { |
| |
| ComputePassVK::ComputePassVK(std::weak_ptr<const Context> context, |
| std::weak_ptr<CommandBufferVK> command_buffer) |
| : ComputePass(std::move(context)), |
| command_buffer_(std::move(command_buffer)) { |
| is_valid_ = true; |
| } |
| |
| ComputePassVK::~ComputePassVK() = default; |
| |
| bool ComputePassVK::IsValid() const { |
| return is_valid_; |
| } |
| |
| void ComputePassVK::OnSetLabel(const std::string& label) { |
| if (label.empty()) { |
| return; |
| } |
| label_ = label; |
| } |
| |
| static bool UpdateBindingLayouts(const Bindings& bindings, |
| const vk::CommandBuffer& buffer) { |
| BarrierVK barrier; |
| barrier.cmd_buffer = buffer; |
| barrier.src_access = vk::AccessFlagBits::eTransferWrite; |
| barrier.src_stage = vk::PipelineStageFlagBits::eTransfer; |
| barrier.dst_access = vk::AccessFlagBits::eShaderRead; |
| barrier.dst_stage = vk::PipelineStageFlagBits::eComputeShader; |
| |
| barrier.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal; |
| |
| for (const TextureAndSampler& data : bindings.sampled_images) { |
| if (!TextureVK::Cast(*data.texture.resource).SetLayout(barrier)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static bool UpdateBindingLayouts(const ComputeCommand& command, |
| const vk::CommandBuffer& buffer) { |
| return UpdateBindingLayouts(command.bindings, buffer); |
| } |
| |
| static bool UpdateBindingLayouts(const std::vector<ComputeCommand>& commands, |
| const vk::CommandBuffer& buffer) { |
| for (const ComputeCommand& command : commands) { |
| if (!UpdateBindingLayouts(command, buffer)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool ComputePassVK::OnEncodeCommands(const Context& context, |
| const ISize& grid_size, |
| const ISize& thread_group_size) const { |
| TRACE_EVENT0("impeller", "ComputePassVK::EncodeCommands"); |
| if (!IsValid()) { |
| return false; |
| } |
| |
| FML_DCHECK(!grid_size.IsEmpty() && !thread_group_size.IsEmpty()); |
| |
| const auto& vk_context = ContextVK::Cast(context); |
| auto command_buffer = command_buffer_.lock(); |
| if (!command_buffer) { |
| VALIDATION_LOG << "Command buffer died before commands could be encoded."; |
| return false; |
| } |
| auto encoder = command_buffer->GetEncoder(); |
| if (!encoder) { |
| return false; |
| } |
| |
| fml::ScopedCleanupClosure pop_marker( |
| [&encoder]() { encoder->PopDebugGroup(); }); |
| if (!label_.empty()) { |
| encoder->PushDebugGroup(label_.c_str()); |
| } else { |
| pop_marker.Release(); |
| } |
| auto cmd_buffer = encoder->GetCommandBuffer(); |
| |
| if (!UpdateBindingLayouts(commands_, cmd_buffer)) { |
| VALIDATION_LOG << "Could not update binding layouts for compute pass."; |
| return false; |
| } |
| auto& allocator = *context.GetResourceAllocator(); |
| |
| TRACE_EVENT0("impeller", "EncodeComputePassCommands"); |
| for (const auto& command : commands_) { |
| auto desc_set_result = AllocateAndBindDescriptorSets( |
| vk_context, encoder, allocator, command, image_workspace_, |
| buffer_workspace_, write_workspace_); |
| if (!desc_set_result.ok()) { |
| return false; |
| } |
| auto desc_set = desc_set_result.value(); |
| |
| const auto& pipeline_vk = ComputePipelineVK::Cast(*command.pipeline); |
| |
| cmd_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, |
| pipeline_vk.GetPipeline()); |
| cmd_buffer.bindDescriptorSets( |
| vk::PipelineBindPoint::eCompute, // bind point |
| pipeline_vk.GetPipelineLayout(), // layout |
| 0, // first set |
| {vk::DescriptorSet{desc_set}}, // sets |
| nullptr // offsets |
| ); |
| |
| // TOOD(dnfield): This should be moved to caps. But for now keeping this |
| // in parallel with Metal. |
| auto device_properties = vk_context.GetPhysicalDevice().getProperties(); |
| |
| auto max_wg_size = device_properties.limits.maxComputeWorkGroupSize; |
| |
| int64_t width = grid_size.width; |
| int64_t height = grid_size.height; |
| |
| // Special case for linear processing. |
| if (height == 1) { |
| int64_t minimum = 1; |
| int64_t threadGroups = std::max( |
| static_cast<int64_t>(std::ceil(width * 1.0 / max_wg_size[0] * 1.0)), |
| minimum); |
| cmd_buffer.dispatch(threadGroups, 1, 1); |
| } else { |
| while (width > max_wg_size[0]) { |
| width = std::max(static_cast<int64_t>(1), width / 2); |
| } |
| while (height > max_wg_size[1]) { |
| height = std::max(static_cast<int64_t>(1), height / 2); |
| } |
| cmd_buffer.dispatch(width, height, 1); |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace impeller |