Blender V4.3
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
10#include "vk_backend.hh"
11#include "vk_context.hh"
12#include "vk_state_manager.hh"
13
14namespace blender::gpu {
15
17{
18 static char PUSH_CONSTANTS_FALLBACK_NAME[] = "push_constants_fallback";
19 static size_t PUSH_CONSTANTS_FALLBACK_NAME_LEN = strlen(PUSH_CONSTANTS_FALLBACK_NAME);
20 static char SUBPASS_FALLBACK_NAME[] = "gpu_subpass_img_0";
21 static size_t SUBPASS_FALLBACK_NAME_LEN = strlen(SUBPASS_FALLBACK_NAME);
22
23 using namespace blender::gpu::shader;
24 shader_builtins_ = info.builtins_;
25
26 attr_len_ = info.vertex_inputs_.size();
27 uniform_len_ = info.push_constants_.size();
29 ssbo_len_ = 0;
30 ubo_len_ = 0;
32 all_resources.extend(info.pass_resources_);
33 all_resources.extend(info.batch_resources_);
34 all_resources.extend(info.geometry_resources_);
35
36 for (ShaderCreateInfo::Resource &res : all_resources) {
37 switch (res.bind_type) {
38 case ShaderCreateInfo::Resource::BindType::IMAGE:
39 case ShaderCreateInfo::Resource::BindType::SAMPLER:
41 break;
42 case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
43 ubo_len_++;
44 break;
45 case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
46 ssbo_len_++;
47 break;
48 }
49 }
50
51 /* Sub-pass inputs are read as samplers.
52 * In future this can change depending on extensions that will be supported. */
53 uniform_len_ += info.subpass_inputs_.size();
54
55 /* Reserve 1 uniform buffer for push constants fallback. */
56 size_t names_size = info.interface_names_size_;
57 const VKDevice &device = VKBackend::get().device;
58 const VKPushConstants::StorageType push_constants_storage_type =
60 if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
61 ubo_len_++;
62 names_size += PUSH_CONSTANTS_FALLBACK_NAME_LEN + 1;
63 }
64 names_size += info.subpass_inputs_.size() * SUBPASS_FALLBACK_NAME_LEN;
65
67 inputs_ = static_cast<ShaderInput *>(
68 MEM_calloc_arrayN(input_tot_len, sizeof(ShaderInput), __func__));
69 ShaderInput *input = inputs_;
70
71 name_buffer_ = (char *)MEM_mallocN(names_size, "name_buffer");
72 uint32_t name_buffer_offset = 0;
73
74 /* Attributes */
75 for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
76 copy_input_name(input, attr.name, name_buffer_, name_buffer_offset);
77 input->location = input->binding = attr.index;
78 if (input->location != -1) {
79 enabled_attr_mask_ |= (1 << input->location);
80
81 /* Used in `GPU_shader_get_attribute_info`. */
82 attr_types_[input->location] = uint8_t(attr.type);
83 }
84
85 input++;
86 }
87
88 /* Uniform blocks */
89 for (const ShaderCreateInfo::Resource &res : all_resources) {
90 if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
91 copy_input_name(input, res.uniformbuf.name, name_buffer_, name_buffer_offset);
92 input->location = input->binding = res.slot;
93 input++;
94 }
95 }
96 /* Add push constant when using uniform buffer as fallback. */
97 int32_t push_constants_fallback_location = -1;
98 if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
99 copy_input_name(input, PUSH_CONSTANTS_FALLBACK_NAME, name_buffer_, name_buffer_offset);
100 input->location = input->binding = -1;
101 input++;
102 }
103
104 /* Images, Samplers and buffers. */
105 for (const ShaderCreateInfo::SubpassIn &subpass_in : info.subpass_inputs_) {
106 copy_input_name(input, SUBPASS_FALLBACK_NAME, name_buffer_, name_buffer_offset);
107 input->location = input->binding = subpass_in.index;
108 input++;
109 }
110 for (const ShaderCreateInfo::Resource &res : all_resources) {
111 if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
112 copy_input_name(input, res.sampler.name, name_buffer_, name_buffer_offset);
113 input->location = input->binding = res.slot;
114 input++;
115 }
116 else if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
117 copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset);
118 input->location = input->binding = res.slot + BIND_SPACE_IMAGE_OFFSET;
119 input++;
120 }
121 }
122
123 /* Push constants. */
124 int32_t push_constant_location = 1024;
126 copy_input_name(input, push_constant.name, name_buffer_, name_buffer_offset);
127 input->location = push_constant_location++;
128 input->binding = -1;
129 input++;
130 }
131
132 /* Storage buffers */
133 for (const ShaderCreateInfo::Resource &res : all_resources) {
134 if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
135 copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset);
136 input->location = input->binding = res.slot;
137 input++;
138 }
139 }
140
141 for (const ShaderCreateInfo::Resource &res : info.geometry_resources_) {
142 if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
143 ssbo_attr_mask_ |= (1 << res.slot);
144 }
145 else {
146 BLI_assert_msg(0, "Resource type is not supported for Geometry frequency");
147 }
148 }
149
150 /* Constants */
151 int constant_id = 0;
152 for (const SpecializationConstant &constant : info.specialization_constants_) {
153 copy_input_name(input, constant.name, name_buffer_, name_buffer_offset);
154 input->location = constant_id++;
155 input++;
156 }
157
158 sort_inputs();
159
160 /* Builtin Uniforms */
161 for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
162 GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
163 const ShaderInput *uni = this->uniform_get(builtin_uniform_name(u));
164 builtins_[u] = (uni != nullptr) ? uni->location : -1;
165 }
166
167 /* Builtin Uniforms Blocks */
168 for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) {
169 GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int);
170 const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u));
171 builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
172 }
173
174 /* Determine the descriptor set locations after the inputs have been sorted. */
175 /* NOTE: input_tot_len is sometimes more than we need. */
176 const uint32_t resources_len = input_tot_len;
177
178 /* Initialize the descriptor set layout. */
179 init_descriptor_set_layout_info(info, resources_len, all_resources, push_constants_storage_type);
180
181 /* Update the descriptor set locations, bind types and access masks. */
182 resource_bindings_ = Array<VKResourceBinding>(resources_len);
183 resource_bindings_.fill({});
184
186 for (const ShaderCreateInfo::SubpassIn &subpass_in : info.subpass_inputs_) {
187 const ShaderInput *input = shader_input_get(
189 BLI_assert(STREQ(input_name_get(input), SUBPASS_FALLBACK_NAME));
190 BLI_assert(input);
191 descriptor_set_location_update(input,
194 std::nullopt,
196 }
197 for (ShaderCreateInfo::Resource &res : all_resources) {
198 const ShaderInput *input = shader_input_get(res);
199 BLI_assert(input);
201 if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
202 arrayed = ELEM(res.image.type,
216 }
217 else if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
218 arrayed = ELEM(res.sampler.type,
236 }
237 descriptor_set_location_update(input, descriptor_set_location++, res.bind_type, res, arrayed);
238 }
239
240 /* Post initializing push constants. */
241 /* Determine the binding location of push constants fallback buffer. */
242 int32_t push_constant_descriptor_set_location = -1;
243 if (push_constants_storage_type == VKPushConstants::StorageType::UNIFORM_BUFFER) {
244 push_constant_descriptor_set_location = descriptor_set_location++;
245 const ShaderInput *push_constant_input = ubo_get(PUSH_CONSTANTS_FALLBACK_NAME);
246 descriptor_set_location_update(push_constant_input,
247 push_constants_fallback_location,
249 std::nullopt,
251 }
252 push_constants_layout_.init(
253 info, *this, push_constants_storage_type, push_constant_descriptor_set_location);
254}
255
256static int32_t shader_input_index(const ShaderInput *shader_inputs,
257 const ShaderInput *shader_input)
258{
259 int32_t index = (shader_input - shader_inputs);
260 return index;
261}
262
263void VKShaderInterface::descriptor_set_location_update(
264 const ShaderInput *shader_input,
265 const VKDescriptorSet::Location location,
267 std::optional<const shader::ShaderCreateInfo::Resource> resource,
268 VKImageViewArrayed arrayed)
269{
270 BLI_assert_msg(resource.has_value() ||
271 ELEM(bind_type,
274 "Incorrect parameters, when no resource is given, it must be the uniform buffer "
275 "for storing push constants or sampler for subpasses.");
276 BLI_assert_msg(!resource.has_value() || resource->bind_type == bind_type,
277 "Incorrect parameter, bind types do not match.");
278
279 int32_t index = shader_input_index(inputs_, shader_input);
280 BLI_assert(resource_bindings_[index].binding == -1);
281
282 VkAccessFlags vk_access_flags = VK_ACCESS_NONE;
283 if (resource.has_value()) {
284 switch (resource->bind_type) {
286 vk_access_flags |= VK_ACCESS_UNIFORM_READ_BIT;
287 break;
288
290 if (bool(resource->storagebuf.qualifiers & shader::Qualifier::READ) == true) {
291 vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
292 }
293 if (bool(resource->storagebuf.qualifiers & shader::Qualifier::WRITE) == true) {
294 vk_access_flags |= VK_ACCESS_SHADER_WRITE_BIT;
295 }
296 break;
297
299 if (bool(resource->image.qualifiers & shader::Qualifier::READ) == true) {
300 vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
301 }
302 if (bool(resource->image.qualifiers & shader::Qualifier::WRITE) == true) {
303 vk_access_flags |= VK_ACCESS_SHADER_WRITE_BIT;
304 }
305 break;
306
308 vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
309 break;
310 };
311 }
313 vk_access_flags |= VK_ACCESS_UNIFORM_READ_BIT;
314 }
316 vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
317 }
318
319 VKResourceBinding &resource_binding = resource_bindings_[index];
320 resource_binding.bind_type = bind_type;
321 resource_binding.binding = shader_input->binding;
322 resource_binding.location = location;
323 resource_binding.arrayed = arrayed;
324 resource_binding.access_mask = vk_access_flags;
325}
326
327const VKResourceBinding &VKShaderInterface::resource_binding_info(
328 const ShaderInput *shader_input) const
329{
330 int32_t index = shader_input_index(inputs_, shader_input);
331 return resource_bindings_[index];
332}
333
335 const shader::ShaderCreateInfo::Resource &resource) const
336{
337 const ShaderInput *shader_input = shader_input_get(resource);
338 BLI_assert(shader_input);
339 return resource_binding_info(shader_input).location;
340}
341
342const std::optional<VKDescriptorSet::Location> VKShaderInterface::descriptor_set_location(
343 const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
344{
345 const ShaderInput *shader_input = shader_input_get(bind_type, binding);
346 if (shader_input == nullptr) {
347 return std::nullopt;
348 }
349 const VKResourceBinding &resource_binding = resource_binding_info(shader_input);
350 if (resource_binding.bind_type != bind_type) {
351 return std::nullopt;
352 }
353 return resource_binding.location;
354}
355
357 const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
358{
359 const ShaderInput *shader_input = shader_input_get(bind_type, binding);
360 if (shader_input == nullptr) {
361 return VK_ACCESS_NONE;
362 }
363 const VKResourceBinding &resource_binding = resource_binding_info(shader_input);
364 if (resource_binding.bind_type != bind_type) {
365 return VK_ACCESS_NONE;
366 }
367 return resource_binding.access_mask;
368}
369
371 const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
372{
373 const ShaderInput *shader_input = shader_input_get(bind_type, binding);
374 if (shader_input == nullptr) {
376 }
377 return resource_binding_info(shader_input).arrayed;
378}
379
380const ShaderInput *VKShaderInterface::shader_input_get(
381 const shader::ShaderCreateInfo::Resource &resource) const
382{
383 return shader_input_get(resource.bind_type, resource.slot);
384}
385
386const ShaderInput *VKShaderInterface::shader_input_get(
387 const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
388{
389 switch (bind_type) {
391 /* Not really nice, but the binding namespace between OpenGL and Vulkan don't match. To fix
392 * this we need to check if one of both cases return a binding.
393 */
394 return texture_get((binding >= BIND_SPACE_IMAGE_OFFSET) ? binding :
395 binding + BIND_SPACE_IMAGE_OFFSET);
397 return texture_get(binding);
399 return ssbo_get(binding);
401 return ubo_get(binding);
402 }
403 return nullptr;
404}
405
406void VKShaderInterface::init_descriptor_set_layout_info(
407 const shader::ShaderCreateInfo &info,
408 int64_t resources_len,
410 VKPushConstants::StorageType push_constants_storage)
411{
412 BLI_assert(descriptor_set_layout_info_.bindings.is_empty());
413 descriptor_set_layout_info_.bindings.reserve(resources_len);
414 descriptor_set_layout_info_.vk_shader_stage_flags =
415 info.compute_source_.is_empty() && info.compute_source_generated.empty() ?
416 VK_SHADER_STAGE_ALL_GRAPHICS :
417 VK_SHADER_STAGE_COMPUTE_BIT;
418 for (int index : IndexRange(info.subpass_inputs_.size())) {
419 UNUSED_VARS(index);
420 descriptor_set_layout_info_.bindings.append(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
421 }
422 for (const shader::ShaderCreateInfo::Resource &res : all_resources) {
423 descriptor_set_layout_info_.bindings.append(to_vk_descriptor_type(res));
424 }
425 if (push_constants_storage == VKPushConstants::StorageType::UNIFORM_BUFFER) {
426 descriptor_set_layout_info_.bindings.append(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
427 }
428}
429
430} // namespace blender::gpu
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define UNUSED_VARS(...)
#define ELEM(...)
#define STREQ(a, b)
#define GPU_NUM_UNIFORMS
GPUUniformBuiltin
GPUUniformBlockBuiltin
@ GPU_NUM_UNIFORM_BLOCKS
constexpr bool is_empty() const
void append(const T &value)
bool is_empty() const
void reserve(const int64_t min_capacity)
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 * ubo_get(const char *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 * ssbo_get(const char *name) const
int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]
int32_t builtins_[GPU_NUM_UNIFORMS]
const ShaderInput * uniform_get(const char *name) const
uint8_t attr_types_[GPU_VERT_ATTR_MAX_LEN]
static const char * builtin_uniform_name(GPUUniformBuiltin u)
static VKBackend & get()
Definition vk_backend.hh:92
const VKImageViewArrayed arrayed(const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
const VKDescriptorSet::Location descriptor_set_location(const shader::ShaderCreateInfo::Resource &resource) const
void init(const shader::ShaderCreateInfo &info)
const VkAccessFlags access_mask(const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
output_img push_constant(Type::FLOAT, "subtrahend") .define("TYPE"
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:43
VkDescriptorType to_vk_descriptor_type(const shader::ShaderCreateInfo::Resource &resource)
static constexpr int BIND_SPACE_IMAGE_OFFSET
static int32_t shader_input_index(const ShaderInput *shader_inputs, const ShaderInput *shader_input)
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
signed int int32_t
Definition stdint.h:77
unsigned char uint8_t
Definition stdint.h:78
static StorageType determine_storage_type(const shader::ShaderCreateInfo &info, const VKDevice &device)
void init(const shader::ShaderCreateInfo &info, const VKShaderInterface &interface, StorageType storage_type, VKDescriptorSet::Location location)
VKDescriptorSet::Location location
shader::ShaderCreateInfo::Resource::BindType bind_type
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Vector< SpecializationConstant > specialization_constants_