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 
6 
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,
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(ShaderStage stage,
145  const SampledImageSlot& slot,
146  const ShaderMetadata* metadata,
147  std::shared_ptr<const Texture> texture,
148  raw_ptr<const Sampler> sampler) {
149  if (bound_image_offset_ >= kMaxBindings) {
150  return false;
151  }
152  if (!texture->IsValid() || !sampler) {
153  return false;
154  }
155  const TextureVK& texture_vk = TextureVK::Cast(*texture);
156  const SamplerVK& sampler_vk = SamplerVK::Cast(*sampler);
157 
158  if (!command_buffer_->Track(texture)) {
159  return false;
160  }
161 
162  vk::DescriptorImageInfo image_info;
163  image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
164  image_info.sampler = sampler_vk.GetSampler();
165  image_info.imageView = texture_vk.GetImageView();
166  image_workspace_[bound_image_offset_++] = image_info;
167 
168  vk::WriteDescriptorSet write_set;
169  write_set.dstBinding = slot.binding;
170  write_set.descriptorCount = 1u;
171  write_set.descriptorType = ToVKDescriptorType(type);
172  write_set.pImageInfo = &image_workspace_[bound_image_offset_ - 1];
173 
174  write_workspace_[descriptor_write_offset_++] = write_set;
175  return true;
176 }
177 
178 bool ComputePassVK::BindResource(size_t binding,
180  BufferView view) {
181  if (bound_buffer_offset_ >= kMaxBindings) {
182  return false;
183  }
184 
185  auto buffer = DeviceBufferVK::Cast(*view.GetBuffer()).GetBuffer();
186  if (!buffer) {
187  return false;
188  }
189 
190  std::shared_ptr<const DeviceBuffer> device_buffer = view.TakeBuffer();
191  if (device_buffer && !command_buffer_->Track(device_buffer)) {
192  return false;
193  }
194 
195  uint32_t offset = view.GetRange().offset;
196 
197  vk::DescriptorBufferInfo buffer_info;
198  buffer_info.buffer = buffer;
199  buffer_info.offset = offset;
200  buffer_info.range = view.GetRange().length;
201  buffer_workspace_[bound_buffer_offset_++] = buffer_info;
202 
203  vk::WriteDescriptorSet write_set;
204  write_set.dstBinding = binding;
205  write_set.descriptorCount = 1u;
206  write_set.descriptorType = ToVKDescriptorType(type);
207  write_set.pBufferInfo = &buffer_workspace_[bound_buffer_offset_ - 1];
208 
209  write_workspace_[descriptor_write_offset_++] = write_set;
210  return true;
211 }
212 
213 // Note:
214 // https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples
215 // Seems to suggest that anything more finely grained than a global memory
216 // barrier is likely to be weakened into a global barrier. Confirming this on
217 // mobile devices will require some experimentation.
218 
219 // |ComputePass|
220 void ComputePassVK::AddBufferMemoryBarrier() {
221  vk::MemoryBarrier barrier;
222  barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
223  barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
224 
225  command_buffer_->GetCommandBuffer().pipelineBarrier(
226  vk::PipelineStageFlagBits::eComputeShader,
227  vk::PipelineStageFlagBits::eComputeShader, {}, 1, &barrier, 0, {}, 0, {});
228 }
229 
230 // |ComputePass|
231 void ComputePassVK::AddTextureMemoryBarrier() {
232  vk::MemoryBarrier barrier;
233  barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
234  barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
235 
236  command_buffer_->GetCommandBuffer().pipelineBarrier(
237  vk::PipelineStageFlagBits::eComputeShader,
238  vk::PipelineStageFlagBits::eComputeShader, {}, 1, &barrier, 0, {}, 0, {});
239 }
240 
241 // |ComputePass|
242 bool ComputePassVK::EncodeCommands() const {
243  // Since we only use global memory barrier, we don't have to worry about
244  // compute to compute dependencies across cmd buffers. Instead, we pessimize
245  // here and assume that we wrote to a storage image or buffer and that a
246  // render pass will read from it. if there are ever scenarios where we end up
247  // with compute to compute dependencies this should be revisited.
248 
249  // This does not currently handle image barriers as we do not use them
250  // for anything.
251  vk::MemoryBarrier barrier;
252  barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
253  barrier.dstAccessMask =
254  vk::AccessFlagBits::eIndexRead | vk::AccessFlagBits::eVertexAttributeRead;
255 
256  command_buffer_->GetCommandBuffer().pipelineBarrier(
257  vk::PipelineStageFlagBits::eComputeShader,
258  vk::PipelineStageFlagBits::eVertexInput, {}, 1, &barrier, 0, {}, 0, {});
259 
260  return true;
261 }
262 
263 } // namespace impeller
GLenum type
constexpr vk::DescriptorType ToVKDescriptorType(DescriptorType type)
Definition: formats_vk.h:292
static constexpr size_t kMaxBindings
Definition: pipeline_vk.h:26
ISize64 ISize
Definition: size.h:174
Definition: comparable.h:95
SeparatedVector2 offset