| Flutter Impeller |

compute_pass_vk.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/renderer/backend/vulkan/compute_pass_vk.h"
6
7 #include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
8 #include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h"
9 #include "impeller/renderer/backend/vulkan/formats_vk.h"
10 #include "impeller/renderer/backend/vulkan/sampler_vk.h"
11 #include "impeller/renderer/backend/vulkan/texture_vk.h"
12 #include "vulkan/vulkan_structs.hpp"
13
14 namespace impeller {
15
16 ComputePassVK::ComputePassVK(std::shared_ptr<const Context> context,
17 std::shared_ptr<CommandBufferVK> command_buffer)
18 : ComputePass(std::move(context)),
19 command_buffer_(std::move(command_buffer)) {
20 // TOOD(dnfield): This should be moved to caps. But for now keeping this
21 // in parallel with Metal.
22 max_wg_size_ = ContextVK::Cast(*context_)
23 .GetPhysicalDevice()
24 .getProperties()
25 .limits.maxComputeWorkGroupSize;
26 is_valid_ = true;
27 }
28
29 ComputePassVK::~ComputePassVK() = default;
30
31 bool ComputePassVK::IsValid() const {
32 return is_valid_;
33 }
34
35 void ComputePassVK::OnSetLabel(const std::string& label) {
36 if (label.empty()) {
37 return;
38 }
39 label_ = label;
40 }
41
42 // |RenderPass|
43 void ComputePassVK::SetCommandLabel(std::string_view label) {
44 #ifdef IMPELLER_DEBUG
45 command_buffer_->PushDebugGroup(label);
46 has_label_ = true;
47 #endif // IMPELLER_DEBUG
48 }
49
50 // |ComputePass|
51 void ComputePassVK::SetPipeline(
52 const std::shared_ptr<Pipeline<ComputePipelineDescriptor>>& pipeline) {
53 const auto& pipeline_vk = ComputePipelineVK::Cast(*pipeline);
54 const vk::CommandBuffer& command_buffer_vk =
55 command_buffer_->GetCommandBuffer();
56 command_buffer_vk.bindPipeline(vk::PipelineBindPoint::eCompute,
57 pipeline_vk.GetPipeline());
58 pipeline_layout_ = pipeline_vk.GetPipelineLayout();
59
60 auto descriptor_result = command_buffer_->AllocateDescriptorSets(
61 pipeline_vk.GetDescriptorSetLayout(), ContextVK::Cast(*context_));
62 if (!descriptor_result.ok()) {
63 return;
64 }
65 descriptor_set_ = descriptor_result.value();
66 pipeline_valid_ = true;
67 }
68
69 // |ComputePass|
70 fml::Status ComputePassVK::Compute(const ISize& grid_size) {
71 if (grid_size.IsEmpty() || !pipeline_valid_) {
72 bound_image_offset_ = 0u;
73 bound_buffer_offset_ = 0u;
74 descriptor_write_offset_ = 0u;
75 has_label_ = false;
76 pipeline_valid_ = false;
77 return fml::Status(fml::StatusCode::kCancelled,
78 "Invalid pipeline or empty grid.");
79 }
80
81 const ContextVK& context_vk = ContextVK::Cast(*context_);
82 for (auto i = 0u; i < descriptor_write_offset_; i++) {
83 write_workspace_[i].dstSet = descriptor_set_;
84 }
85
86 context_vk.GetDevice().updateDescriptorSets(descriptor_write_offset_,
87 write_workspace_.data(), 0u, {});
88 const vk::CommandBuffer& command_buffer_vk =
89 command_buffer_->GetCommandBuffer();
90
91 command_buffer_vk.bindDescriptorSets(
92 vk::PipelineBindPoint::eCompute, // bind point
93 pipeline_layout_, // layout
94 0, // first set
95 1, // set count
96 &descriptor_set_, // sets
97 0, // offset count
98 nullptr // offsets
99 );
100
101 int64_t width = grid_size.width;
102 int64_t height = grid_size.height;
103
104 // Special case for linear processing.
105 if (height == 1) {
106 command_buffer_vk.dispatch(width, 1, 1);
107 } else {
108 while (width > max_wg_size_[0]) {
109 width = std::max(static_cast<int64_t>(1), width / 2);
110 }
111 while (height > max_wg_size_[1]) {
112 height = std::max(static_cast<int64_t>(1), height / 2);
113 }
114 command_buffer_vk.dispatch(width, height, 1);
115 }
116
117 #ifdef IMPELLER_DEBUG
118 if (has_label_) {
119 command_buffer_->PopDebugGroup();
120 }
121 has_label_ = false;
122 #endif // IMPELLER_DEBUG
123
124 bound_image_offset_ = 0u;
125 bound_buffer_offset_ = 0u;
126 descriptor_write_offset_ = 0u;
127 has_label_ = false;
128 pipeline_valid_ = false;
129
130 return fml::Status();
131 }
132
133 // |ResourceBinder|
134 bool ComputePassVK::BindResource(ShaderStage stage,
135 DescriptorType type,
136 const ShaderUniformSlot& slot,
137 const ShaderMetadata& metadata,
138 BufferView view) {
139 return BindResource(slot.binding, type, view);
140 }
141
142 // |ResourceBinder|
143 bool ComputePassVK::BindResource(
144 ShaderStage stage,
145 DescriptorType type,
146 const SampledImageSlot& slot,
147 const ShaderMetadata& metadata,
148 std::shared_ptr<const Texture> texture,
149 const std::unique_ptr<const Sampler>& sampler) {
150 if (bound_image_offset_ >= kMaxBindings) {
151 return false;
152 }
153 if (!texture->IsValid() || !sampler) {
154 return false;
155 }
156 const TextureVK& texture_vk = TextureVK::Cast(*texture);
157 const SamplerVK& sampler_vk = SamplerVK::Cast(*sampler);
158
159 if (!command_buffer_->Track(texture)) {
160 return false;
161 }
162
163 vk::DescriptorImageInfo image_info;
164 image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
165 image_info.sampler = sampler_vk.GetSampler();
166 image_info.imageView = texture_vk.GetImageView();
167 image_workspace_[bound_image_offset_++] = image_info;
168
169 vk::WriteDescriptorSet write_set;
170 write_set.dstBinding = slot.binding;
171 write_set.descriptorCount = 1u;
172 write_set.descriptorType = ToVKDescriptorType(type);
173 write_set.pImageInfo = &image_workspace_[bound_image_offset_ - 1];
174
175 write_workspace_[descriptor_write_offset_++] = write_set;
176 return true;
177 }
178
179 bool ComputePassVK::BindResource(size_t binding,
180 DescriptorType type,
181 const BufferView& view) {
182 if (bound_buffer_offset_ >= kMaxBindings) {
183 return false;
184 }
185
186 const std::shared_ptr<const DeviceBuffer>& device_buffer = view.buffer;
187 auto buffer = DeviceBufferVK::Cast(*device_buffer).GetBuffer();
188 if (!buffer) {
189 return false;
190 }
191
192 if (!command_buffer_->Track(device_buffer)) {
193 return false;
194 }
195
196 uint32_t offset = view.range.offset;
197
198 vk::DescriptorBufferInfo buffer_info;
199 buffer_info.buffer = buffer;
200 buffer_info.offset = offset;
201 buffer_info.range = view.range.length;
202 buffer_workspace_[bound_buffer_offset_++] = buffer_info;
203
204 vk::WriteDescriptorSet write_set;
205 write_set.dstBinding = binding;
206 write_set.descriptorCount = 1u;
207 write_set.descriptorType = ToVKDescriptorType(type);
208 write_set.pBufferInfo = &buffer_workspace_[bound_buffer_offset_ - 1];
209
210 write_workspace_[descriptor_write_offset_++] = write_set;
211 return true;
212 }
213
214 // Note:
215 // https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples
216 // Seems to suggest that anything more finely grained than a global memory
217 // barrier is likely to be weakened into a global barrier. Confirming this on
218 // mobile devices will require some experimentation.
219
220 // |ComputePass|
221 void ComputePassVK::AddBufferMemoryBarrier() {
222 vk::MemoryBarrier barrier;
223 barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
224 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
225
226 command_buffer_->GetCommandBuffer().pipelineBarrier(
227 vk::PipelineStageFlagBits::eComputeShader,
228 vk::PipelineStageFlagBits::eComputeShader, {}, 1, &barrier, 0, {}, 0, {});
229 }
230
231 // |ComputePass|
232 void ComputePassVK::AddTextureMemoryBarrier() {
233 vk::MemoryBarrier barrier;
234 barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
235 barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
236
237 command_buffer_->GetCommandBuffer().pipelineBarrier(
238 vk::PipelineStageFlagBits::eComputeShader,
239 vk::PipelineStageFlagBits::eComputeShader, {}, 1, &barrier, 0, {}, 0, {});
240 }
241
242 // |ComputePass|
243 bool ComputePassVK::EncodeCommands() const {
244 // Since we only use global memory barrier, we don't have to worry about
245 // compute to compute dependencies across cmd buffers. Instead, we pessimize
246 // here and assume that we wrote to a storage image or buffer and that a
247 // render pass will read from it. if there are ever scenarios where we end up
248 // with compute to compute dependencies this should be revisited.
249
250 // This does not currently handle image barriers as we do not use them
251 // for anything.
252 vk::MemoryBarrier barrier;
253 barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
254 barrier.dstAccessMask =
255 vk::AccessFlagBits::eIndexRead | vk::AccessFlagBits::eVertexAttributeRead;
256
257 command_buffer_->GetCommandBuffer().pipelineBarrier(
258 vk::PipelineStageFlagBits::eComputeShader,
259 vk::PipelineStageFlagBits::eVertexInput, {}, 1, &barrier, 0, {}, 0, {});
260
261 return true;
262 }
263
264 } // namespace impeller
impeller::ISize
ISize64 ISize
Definition: size.h:140
impeller::DeviceBufferVK::GetBuffer
vk::Buffer GetBuffer() const
Definition: device_buffer_vk.cc:86
compute_pass_vk.h
impeller::ToVKDescriptorType
constexpr vk::DescriptorType ToVKDescriptorType(DescriptorType type)
Definition: formats_vk.h:266
impeller::ContextVK::GetPhysicalDevice
vk::PhysicalDevice GetPhysicalDevice() const
Definition: context_vk.cc:563
impeller::ComputePassVK::~ComputePassVK
~ComputePassVK() override
impeller::ShaderStage
ShaderStage
Definition: shader_types.h:22
formats_vk.h
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:304
command_buffer_vk.h
impeller::kMaxBindings
static constexpr size_t kMaxBindings
Definition: pipeline_vk.h:26
impeller::ComputePass::context_
const std::shared_ptr< const Context > context_
Definition: compute_pass.h:65
compute_pipeline_vk.h
type
GLenum type
Definition: blit_command_gles.cc:127
texture_vk.h
std
Definition: comparable.h:95
impeller::BackendCast< ContextVK, Context >::Cast
static ContextVK & Cast(Context &base)
Definition: backend_cast.h:13
sampler_vk.h
impeller
Definition: allocation.cc:12
impeller::DescriptorType
DescriptorType
Definition: shader_types.h:156