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