Blender V4.5
vk_shader_interface.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
10#include "vk_backend.hh"
11#include "vk_context.hh"
12#include "vk_state_manager.hh"
13
14namespace blender::gpu {
15
31
33{
34 static char PUSH_CONSTANTS_FALLBACK_NAME[] = "push_constants_fallback";
35 static size_t PUSH_CONSTANTS_FALLBACK_NAME_LEN = strlen(PUSH_CONSTANTS_FALLBACK_NAME);
36 static char SUBPASS_FALLBACK_NAME[] = "gpu_subpass_img_0";
37 static size_t SUBPASS_FALLBACK_NAME_LEN = strlen(SUBPASS_FALLBACK_NAME);
38
39 using namespace blender::gpu::shader;
40 shader_builtins_ = info.builtins_;
41
42 attr_len_ = info.vertex_inputs_.size();
43 uniform_len_ = info.push_constants_.size();
45 ssbo_len_ = 0;
46 ubo_len_ = 0;
48 all_resources.extend(info.pass_resources_);
49 all_resources.extend(info.batch_resources_);
50 all_resources.extend(info.geometry_resources_);
51
52 for (ShaderCreateInfo::Resource &res : all_resources) {
53 switch (res.bind_type) {
57 break;
59 ubo_len_++;
60 break;
62 ssbo_len_++;
63 break;
64 }
65 }
66 const VKDevice &device = VKBackend::get().device;
67 const bool supports_local_read = device.extensions_get().dynamic_rendering_local_read;
68 uniform_len_ += info.subpass_inputs_.size();
69
70 /* Reserve 1 uniform buffer for push constants fallback. */
71 size_t names_size = info.interface_names_size_;
72 const VKPushConstants::StorageType push_constants_storage_type =
74 if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
75 ubo_len_++;
76 names_size += PUSH_CONSTANTS_FALLBACK_NAME_LEN + 1;
77 }
78 names_size += info.subpass_inputs_.size() * SUBPASS_FALLBACK_NAME_LEN;
79
81 inputs_ = MEM_calloc_arrayN<ShaderInput>(input_tot_len, __func__);
83
84 name_buffer_ = (char *)MEM_mallocN(names_size, "name_buffer");
85 uint32_t name_buffer_offset = 0;
86
87 /* Attributes */
88 for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
89 copy_input_name(input, attr.name, name_buffer_, name_buffer_offset);
90 input->location = input->binding = attr.index;
91 if (input->location != -1) {
92 enabled_attr_mask_ |= (1 << input->location);
93
94 /* Used in `GPU_shader_get_attribute_info`. */
95 attr_types_[input->location] = uint8_t(attr.type);
96 }
97
98 input++;
99 }
100
101 /* Uniform blocks */
102 for (const ShaderCreateInfo::Resource &res : all_resources) {
104 copy_input_name(input, res.uniformbuf.name, name_buffer_, name_buffer_offset);
105 input->location = input->binding = res.slot;
106 input++;
107 }
108 }
109 /* Add push constant when using uniform buffer as a fallback. */
110 int32_t push_constants_fallback_location = -1;
111 if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
112 copy_input_name(input, PUSH_CONSTANTS_FALLBACK_NAME, name_buffer_, name_buffer_offset);
113 input->location = input->binding = -1;
114 input++;
115 }
116
117 /* Images, Samplers and buffers. */
118 for (const ShaderCreateInfo::SubpassIn &subpass_in : info.subpass_inputs_) {
119 copy_input_name(input, SUBPASS_FALLBACK_NAME, name_buffer_, name_buffer_offset);
120 input->location = input->binding = subpass_in.index;
121 input++;
122 }
123 for (const ShaderCreateInfo::Resource &res : all_resources) {
125 copy_input_name(input, res.sampler.name, name_buffer_, name_buffer_offset);
126 input->location = input->binding = res.slot;
127 input++;
128 }
129 else if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
130 copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset);
131 input->location = input->binding = res.slot + BIND_SPACE_IMAGE_OFFSET;
132 input++;
133 }
134 }
135
136 /* Push constants. */
137 int32_t push_constant_location = 1024;
138 for (const ShaderCreateInfo::PushConst &push_constant : info.push_constants_) {
139 copy_input_name(input, push_constant.name, name_buffer_, name_buffer_offset);
140 input->location = push_constant_location++;
141 input->binding = -1;
142 input++;
143 }
144
145 /* Storage buffers */
146 for (const ShaderCreateInfo::Resource &res : all_resources) {
148 copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset);
149 input->location = input->binding = res.slot;
150 input++;
151 }
152 }
153
154 for (const ShaderCreateInfo::Resource &res : info.geometry_resources_) {
156 ssbo_attr_mask_ |= (1 << res.slot);
157 }
158 else {
159 BLI_assert_msg(0, "Resource type is not supported for Geometry frequency");
160 }
161 }
162
163 /* Constants */
164 int constant_id = 0;
165 for (const SpecializationConstant &constant : info.specialization_constants_) {
166 copy_input_name(input, constant.name, name_buffer_, name_buffer_offset);
167 input->location = constant_id++;
168 input++;
169 }
170
171 sort_inputs();
172
173 /* Builtin Uniforms */
174 for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
175 GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
176 const ShaderInput *uni = this->uniform_get(builtin_uniform_name(u));
177 builtins_[u] = (uni != nullptr) ? uni->location : -1;
178 }
179
180 /* Builtin Uniforms Blocks */
181 for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) {
182 GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int);
183 const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u));
184 builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
185 }
186
187 /* Determine the descriptor set locations after the inputs have been sorted. */
188 /* NOTE: input_tot_len is sometimes more than we need. */
189 const uint32_t resources_len = input_tot_len;
190
191 /* Initialize the descriptor set layout. */
192 init_descriptor_set_layout_info(info, resources_len, all_resources, push_constants_storage_type);
193
194 /* Update the descriptor set locations, bind types and access masks. */
195 resource_bindings_ = Array<VKResourceBinding>(resources_len);
196 resource_bindings_.fill({});
197
198 uint32_t descriptor_set_location = 0;
199 for (const ShaderCreateInfo::SubpassIn &subpass_in : info.subpass_inputs_) {
200 const ShaderInput *input = supports_local_read ?
201 texture_get(subpass_in.index) :
202 shader_input_get(
204 subpass_in.index);
206 BLI_assert(STREQ(input_name_get(input), SUBPASS_FALLBACK_NAME));
207 descriptor_set_location_update(input,
210 std::nullopt,
212 }
213 for (ShaderCreateInfo::Resource &res : all_resources) {
214 const ShaderInput *input = shader_input_get(res);
218 arrayed = ELEM(res.image.type,
219 shader::ImageType::Float1DArray,
220 shader::ImageType::Float2DArray,
221 shader::ImageType::FloatCubeArray,
222 shader::ImageType::Int1DArray,
223 shader::ImageType::Int2DArray,
224 shader::ImageType::IntCubeArray,
225 shader::ImageType::Uint1DArray,
226 shader::ImageType::Uint2DArray,
227 shader::ImageType::UintCubeArray,
228 shader::ImageType::AtomicUint2DArray,
229 shader::ImageType::AtomicInt2DArray) ?
232 }
234 arrayed = ELEM(res.sampler.type,
235 shader::ImageType::Float1DArray,
236 shader::ImageType::Float2DArray,
237 shader::ImageType::FloatCubeArray,
238 shader::ImageType::Int1DArray,
239 shader::ImageType::Int2DArray,
240 shader::ImageType::IntCubeArray,
241 shader::ImageType::Uint1DArray,
242 shader::ImageType::Uint2DArray,
243 shader::ImageType::UintCubeArray,
244 shader::ImageType::Shadow2DArray,
245 shader::ImageType::ShadowCubeArray,
246 shader::ImageType::Depth2DArray,
247 shader::ImageType::DepthCubeArray,
248 shader::ImageType::AtomicUint2DArray,
249 shader::ImageType::AtomicInt2DArray) ?
252 }
253
254 const VKBindType bind_type = to_bind_type(res.bind_type);
255 descriptor_set_location_update(input, descriptor_set_location++, bind_type, res, arrayed);
256 }
257
258 /* Post initializing push constants. */
259 /* Determine the binding location of push constants fallback buffer. */
260 int32_t push_constant_descriptor_set_location = -1;
261 if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
262 push_constant_descriptor_set_location = descriptor_set_location++;
263 const ShaderInput *push_constant_input = ubo_get(PUSH_CONSTANTS_FALLBACK_NAME);
264 descriptor_set_location_update(push_constant_input,
265 push_constants_fallback_location,
267 std::nullopt,
269 }
270 push_constants_layout_.init(
271 info, *this, push_constants_storage_type, push_constant_descriptor_set_location);
272}
273
274static int32_t shader_input_index(const ShaderInput *shader_inputs,
275 const ShaderInput *shader_input)
276{
277 int32_t index = (shader_input - shader_inputs);
278 return index;
279}
280
281void VKShaderInterface::descriptor_set_location_update(
282 const ShaderInput *shader_input,
283 const VKDescriptorSet::Location location,
284 const VKBindType bind_type,
285 std::optional<const shader::ShaderCreateInfo::Resource> resource,
286 VKImageViewArrayed arrayed)
287{
288 BLI_assert_msg(resource.has_value() ||
290 "Incorrect parameters, when no resource is given, it must be the uniform buffer "
291 "for storing push constants or input attachment for subpass inputs.");
292 BLI_assert_msg(!resource.has_value() || to_bind_type(resource->bind_type) == bind_type,
293 "Incorrect parameter, bind types do not match.");
294
295 const VKDevice &device = VKBackend::get().device;
296 const bool supports_local_read = device.extensions_get().dynamic_rendering_local_read;
297
298 int32_t index = shader_input_index(inputs_, shader_input);
299 BLI_assert(resource_bindings_[index].binding == -1);
300
301 VkAccessFlags vk_access_flags = VK_ACCESS_NONE;
302 if (resource.has_value()) {
303 switch (resource->bind_type) {
305 vk_access_flags |= VK_ACCESS_UNIFORM_READ_BIT;
306 break;
307
309 if (bool(resource->storagebuf.qualifiers & shader::Qualifier::read) == true) {
310 vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
311 }
312 if (bool(resource->storagebuf.qualifiers & shader::Qualifier::write) == true) {
313 vk_access_flags |= VK_ACCESS_SHADER_WRITE_BIT;
314 }
315 break;
316
318 if (bool(resource->image.qualifiers & shader::Qualifier::read) == true) {
319 vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
320 }
321 if (bool(resource->image.qualifiers & shader::Qualifier::write) == true) {
322 vk_access_flags |= VK_ACCESS_SHADER_WRITE_BIT;
323 }
324 break;
325
327 vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
328 break;
329 };
330 }
331 else if (bind_type == VKBindType::UNIFORM_BUFFER) {
332 vk_access_flags |= VK_ACCESS_UNIFORM_READ_BIT;
333 }
334 else if (bind_type == VKBindType::INPUT_ATTACHMENT) {
335 vk_access_flags |= supports_local_read ? VK_ACCESS_INPUT_ATTACHMENT_READ_BIT |
336 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT :
337 VK_ACCESS_SHADER_READ_BIT;
338 }
339
340 VKResourceBinding &resource_binding = resource_bindings_[index];
341 resource_binding.bind_type = bind_type;
342 resource_binding.binding = shader_input->binding;
343 resource_binding.location = location;
344 resource_binding.arrayed = arrayed;
345 resource_binding.access_mask = vk_access_flags;
346}
347
348const VKResourceBinding &VKShaderInterface::resource_binding_info(
349 const ShaderInput *shader_input) const
350{
351 int32_t index = shader_input_index(inputs_, shader_input);
352 return resource_bindings_[index];
353}
354
357{
358 const ShaderInput *shader_input = shader_input_get(resource);
359 BLI_assert(shader_input);
360 return resource_binding_info(shader_input).location;
361}
362
363const std::optional<VKDescriptorSet::Location> VKShaderInterface::descriptor_set_location(
364 const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
365{
366 const ShaderInput *shader_input = shader_input_get(bind_type, binding);
367 if (shader_input == nullptr) {
368 return std::nullopt;
369 }
370 const VKResourceBinding &resource_binding = resource_binding_info(shader_input);
371 if (resource_binding.bind_type != to_bind_type(bind_type)) {
372 return std::nullopt;
373 }
374 return resource_binding.location;
375}
376
377const ShaderInput *VKShaderInterface::shader_input_get(
379{
380 return shader_input_get(resource.bind_type, resource.slot);
381}
382
383const ShaderInput *VKShaderInterface::shader_input_get(
384 const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
385{
386 switch (bind_type) {
388 /* Not really nice, but the binding namespace between OpenGL and Vulkan don't match. To fix
389 * this we need to check if one of both cases return a binding.
390 */
391 return texture_get((binding >= BIND_SPACE_IMAGE_OFFSET) ? binding :
392 binding + BIND_SPACE_IMAGE_OFFSET);
394 return texture_get(binding);
396 return ssbo_get(binding);
398 return ubo_get(binding);
399 }
400 return nullptr;
401}
402
403void VKShaderInterface::init_descriptor_set_layout_info(
404 const shader::ShaderCreateInfo &info,
405 int64_t resources_len,
406 Span<shader::ShaderCreateInfo::Resource> all_resources,
407 VKPushConstants::StorageType push_constants_storage)
408{
409 BLI_assert(descriptor_set_layout_info_.bindings.is_empty());
410 const VKExtensions &extensions = VKBackend::get().device.extensions_get();
411 const bool supports_local_read = extensions.dynamic_rendering_local_read;
412
413 descriptor_set_layout_info_.bindings.reserve(resources_len);
414 if (!(info.compute_source_.is_empty() && info.compute_source_generated.empty())) {
415 descriptor_set_layout_info_.vk_shader_stage_flags = VK_SHADER_STAGE_COMPUTE_BIT;
416 }
417 else if (supports_local_read && !info.subpass_inputs_.is_empty()) {
418 descriptor_set_layout_info_.vk_shader_stage_flags = VK_SHADER_STAGE_FRAGMENT_BIT;
419 }
420 else {
421 descriptor_set_layout_info_.vk_shader_stage_flags = VK_SHADER_STAGE_ALL_GRAPHICS;
422 }
423 for (int index : IndexRange(info.subpass_inputs_.size())) {
424 UNUSED_VARS(index);
425 // TODO: clean up remove negation.
426 descriptor_set_layout_info_.bindings.append_n_times(
427 !extensions.dynamic_rendering ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT :
428 !extensions.dynamic_rendering_local_read ? VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER :
429 VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
430 info.subpass_inputs_.size());
431 }
432 for (const shader::ShaderCreateInfo::Resource &res : all_resources) {
433 descriptor_set_layout_info_.bindings.append(to_vk_descriptor_type(res));
434 }
435 if (push_constants_storage == VKPushConstants::StorageType::UNIFORM_BUFFER) {
436 descriptor_set_layout_info_.bindings.append(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
437 }
438}
439
440} // namespace blender::gpu
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define UNUSED_VARS(...)
#define ELEM(...)
#define STREQ(a, b)
#define GPU_NUM_UNIFORMS
GPUUniformBuiltin
GPUUniformBlockBuiltin
@ GPU_NUM_UNIFORM_BLOCKS
long long int int64_t
constexpr bool is_empty() const
void extend(Span< T > array)
void copy_input_name(ShaderInput *input, const StringRefNull &name, char *name_buffer, uint32_t &name_buffer_offset) const
const ShaderInput * uniform_get(const StringRefNull name) const
const ShaderInput * texture_get(const int binding) const
const char * input_name_get(const ShaderInput *input) const
static const char * builtin_uniform_block_name(GPUUniformBlockBuiltin u)
const ShaderInput * ubo_get(const StringRefNull name) const
const ShaderInput * ssbo_get(const StringRefNull name) const
int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]
int32_t builtins_[GPU_NUM_UNIFORMS]
uint8_t attr_types_[GPU_VERT_ATTR_MAX_LEN]
static const char * builtin_uniform_name(GPUUniformBuiltin u)
static VKBackend & get()
Definition vk_backend.hh:91
const VKExtensions & extensions_get() const
Definition vk_device.hh:396
const VKDescriptorSet::Location descriptor_set_location(const shader::ShaderCreateInfo::Resource &resource) const
void init(const shader::ShaderCreateInfo &info)
#define resource
#define input
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
VkDescriptorType to_vk_descriptor_type(const shader::ShaderCreateInfo::Resource &resource)
Definition vk_common.cc:930
static constexpr int BIND_SPACE_IMAGE_OFFSET
static int32_t shader_input_index(const ShaderInput *shader_inputs, const ShaderInput *shader_input)
static VKBindType to_bind_type(shader::ShaderCreateInfo::Resource::BindType bind_type)
static StorageType determine_storage_type(const shader::ShaderCreateInfo &info, const VKDevice &device)
VKDescriptorSet::Location location
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Vector< SpecializationConstant > specialization_constants_