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  is_valid_ = true;
21 }
22 
23 ComputePassVK::~ComputePassVK() = default;
24 
25 bool ComputePassVK::IsValid() const {
26  return is_valid_;
27 }
28 
29 void ComputePassVK::OnSetLabel(const std::string& label) {
30  if (label.empty()) {
31  return;
32  }
33  label_ = label;
34 }
35 
36 // |RenderPass|
37 void ComputePassVK::SetCommandLabel(std::string_view label) {
38 #ifdef IMPELLER_DEBUG
39  command_buffer_->PushDebugGroup(label);
40  has_label_ = true;
41 #endif // IMPELLER_DEBUG
42 }
43 
44 // |ComputePass|
45 void ComputePassVK::SetPipeline(
46  const std::shared_ptr<Pipeline<ComputePipelineDescriptor>>& pipeline) {
47  const auto& pipeline_vk = ComputePipelineVK::Cast(*pipeline);
48  const vk::CommandBuffer& command_buffer_vk =
49  command_buffer_->GetCommandBuffer();
50  command_buffer_vk.bindPipeline(vk::PipelineBindPoint::eCompute,
51  pipeline_vk.GetPipeline());
52  pipeline_layout_ = pipeline_vk.GetPipelineLayout();
53 
54  auto descriptor_result = command_buffer_->AllocateDescriptorSets(
55  pipeline_vk.GetDescriptorSetLayout(), pipeline_vk.GetPipelineKey(),
56  ContextVK::Cast(*context_));
57  if (!descriptor_result.ok()) {
58  return;
59  }
60  descriptor_set_ = descriptor_result.value();
61  pipeline_valid_ = true;
62 }
63 
64 // |ComputePass|
65 fml::Status ComputePassVK::Compute(std::array<uint32_t, 3> workgroup_count) {
66  if (workgroup_count[0] == 0u || workgroup_count[1] == 0u ||
67  workgroup_count[2] == 0u || !pipeline_valid_) {
68  bound_image_offset_ = 0u;
69  bound_buffer_offset_ = 0u;
70  descriptor_write_offset_ = 0u;
71  has_label_ = false;
72  pipeline_valid_ = false;
73  return fml::Status(fml::StatusCode::kCancelled,
74  "Invalid pipeline or empty workgroup count.");
75  }
76 
77  const ContextVK& context_vk = ContextVK::Cast(*context_);
78  for (auto i = 0u; i < descriptor_write_offset_; i++) {
79  write_workspace_[i].dstSet = descriptor_set_;
80  }
81 
82  context_vk.GetDevice().updateDescriptorSets(descriptor_write_offset_,
83  write_workspace_.data(), 0u, {});
84  const vk::CommandBuffer& command_buffer_vk =
85  command_buffer_->GetCommandBuffer();
86 
87  command_buffer_vk.bindDescriptorSets(
88  vk::PipelineBindPoint::eCompute, // bind point
89  pipeline_layout_, // layout
90  0, // first set
91  1, // set count
92  &descriptor_set_, // sets
93  0, // offset count
94  nullptr // offsets
95  );
96 
97  // The arguments are workgroup counts. The per-workgroup invocation count (the
98  // local size) is baked into the shader module, so dispatch the counts
99  // directly.
100  command_buffer_vk.dispatch(workgroup_count[0], workgroup_count[1],
101  workgroup_count[2]);
102 
103 #ifdef IMPELLER_DEBUG
104  if (has_label_) {
105  command_buffer_->PopDebugGroup();
106  }
107  has_label_ = false;
108 #endif // IMPELLER_DEBUG
109 
110  bound_image_offset_ = 0u;
111  bound_buffer_offset_ = 0u;
112  descriptor_write_offset_ = 0u;
113  has_label_ = false;
114  pipeline_valid_ = false;
115 
116  return fml::Status();
117 }
118 
119 // |ResourceBinder|
120 bool ComputePassVK::BindResource(ShaderStage stage,
121  DescriptorType type,
122  const ShaderUniformSlot& slot,
123  const ShaderMetadata* metadata,
124  BufferView view) {
125  return BindResource(slot.binding, type, view);
126 }
127 
128 // |ResourceBinder|
129 bool ComputePassVK::BindResource(ShaderStage stage,
130  DescriptorType type,
131  const SampledImageSlot& slot,
132  const ShaderMetadata* metadata,
133  std::shared_ptr<const Texture> texture,
134  raw_ptr<const Sampler> sampler) {
135  if (bound_image_offset_ >= kMaxBindings) {
136  return false;
137  }
138  if (!texture->IsValid() || !sampler) {
139  return false;
140  }
141  const TextureVK& texture_vk = TextureVK::Cast(*texture);
142  const SamplerVK& sampler_vk = SamplerVK::Cast(*sampler);
143 
144  if (!command_buffer_->Track(texture)) {
145  return false;
146  }
147 
148  vk::DescriptorImageInfo image_info;
149  image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
150  image_info.sampler = sampler_vk.GetSampler();
151  image_info.imageView = texture_vk.GetImageView();
152  image_workspace_[bound_image_offset_++] = image_info;
153 
154  vk::WriteDescriptorSet write_set;
155  write_set.dstBinding = slot.binding;
156  write_set.descriptorCount = 1u;
157  write_set.descriptorType = ToVKDescriptorType(type);
158  write_set.pImageInfo = &image_workspace_[bound_image_offset_ - 1];
159 
160  write_workspace_[descriptor_write_offset_++] = write_set;
161  return true;
162 }
163 
164 bool ComputePassVK::BindResource(size_t binding,
165  DescriptorType type,
166  BufferView view) {
167  if (bound_buffer_offset_ >= kMaxBindings) {
168  return false;
169  }
170 
171  auto buffer = DeviceBufferVK::Cast(*view.GetBuffer()).GetBuffer();
172  if (!buffer) {
173  return false;
174  }
175 
176  std::shared_ptr<const DeviceBuffer> device_buffer = view.TakeBuffer();
177  if (device_buffer && !command_buffer_->Track(device_buffer)) {
178  return false;
179  }
180 
181  uint32_t offset = view.GetRange().offset;
182 
183  vk::DescriptorBufferInfo buffer_info;
184  buffer_info.buffer = buffer;
185  buffer_info.offset = offset;
186  buffer_info.range = view.GetRange().length;
187  buffer_workspace_[bound_buffer_offset_++] = buffer_info;
188 
189  vk::WriteDescriptorSet write_set;
190  write_set.dstBinding = binding;
191  write_set.descriptorCount = 1u;
192  write_set.descriptorType = ToVKDescriptorType(type);
193  write_set.pBufferInfo = &buffer_workspace_[bound_buffer_offset_ - 1];
194 
195  write_workspace_[descriptor_write_offset_++] = write_set;
196  return true;
197 }
198 
199 // Note:
200 // https://github.com/KhronosGroup/Vulkan-Docs/wiki/Synchronization-Examples
201 // Seems to suggest that anything more finely grained than a global memory
202 // barrier is likely to be weakened into a global barrier. Confirming this on
203 // mobile devices will require some experimentation.
204 
205 // |ComputePass|
206 void ComputePassVK::AddBufferMemoryBarrier() {
207  vk::MemoryBarrier barrier;
208  barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
209  barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
210 
211  command_buffer_->GetCommandBuffer().pipelineBarrier(
212  vk::PipelineStageFlagBits::eComputeShader,
213  vk::PipelineStageFlagBits::eComputeShader, {}, 1, &barrier, 0, {}, 0, {});
214 }
215 
216 // |ComputePass|
217 void ComputePassVK::AddTextureMemoryBarrier() {
218  vk::MemoryBarrier barrier;
219  barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
220  barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
221 
222  command_buffer_->GetCommandBuffer().pipelineBarrier(
223  vk::PipelineStageFlagBits::eComputeShader,
224  vk::PipelineStageFlagBits::eComputeShader, {}, 1, &barrier, 0, {}, 0, {});
225 }
226 
227 // |ComputePass|
228 bool ComputePassVK::EncodeCommands() const {
229  // Since we only use global memory barrier, we don't have to worry about
230  // compute to compute dependencies across cmd buffers. Instead, we pessimize
231  // here and assume that we wrote to a storage image or buffer and that a
232  // render pass will read from it. if there are ever scenarios where we end up
233  // with compute to compute dependencies this should be revisited.
234 
235  // This does not currently handle image barriers as we do not use them
236  // for anything.
237  vk::MemoryBarrier barrier;
238  barrier.srcAccessMask = vk::AccessFlagBits::eShaderWrite;
239  barrier.dstAccessMask =
240  vk::AccessFlagBits::eIndexRead | vk::AccessFlagBits::eVertexAttributeRead;
241 
242  command_buffer_->GetCommandBuffer().pipelineBarrier(
243  vk::PipelineStageFlagBits::eComputeShader,
244  vk::PipelineStageFlagBits::eVertexInput, {}, 1, &barrier, 0, {}, 0, {});
245 
246  return true;
247 }
248 
249 } // namespace impeller
constexpr vk::DescriptorType ToVKDescriptorType(DescriptorType type)
Definition: formats_vk.h:328
static constexpr size_t kMaxBindings
Definition: pipeline_vk.h:23
Definition: comparable.h:93
std::shared_ptr< ContextGLES > context
std::shared_ptr< PipelineGLES > pipeline
std::shared_ptr< CommandBuffer > command_buffer