Blender V4.5
gpu_shader_create_info.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BLI_map.hh"
12#include "BLI_set.hh"
13#include "BLI_string_ref.hh"
14
15#include "BKE_global.hh"
16
17#include "GPU_capabilities.hh"
18#include "GPU_context.hh"
19#include "GPU_platform.hh"
20#include "GPU_shader.hh"
21
25
26#undef GPU_SHADER_NAMED_INTERFACE_INFO
27#undef GPU_SHADER_INTERFACE_INFO
28#undef GPU_SHADER_CREATE_INFO
29#undef GPU_SHADER_NAMED_INTERFACE_END
30#undef GPU_SHADER_INTERFACE_END
31#undef GPU_SHADER_CREATE_END
32
33namespace blender::gpu::shader {
34
37
40
41/* -------------------------------------------------------------------- */
45
47{
48 if (iface.instance_name.is_empty()) {
49 return true;
50 }
51
52 bool use_flat = false;
53 bool use_smooth = false;
54 bool use_noperspective = false;
55 for (const StageInterfaceInfo::InOut &attr : iface.inouts) {
56 switch (attr.interp) {
58 use_flat = true;
59 break;
61 use_smooth = true;
62 break;
64 use_noperspective = true;
65 break;
66 }
67 }
68 int num_used_interpolation_types = (use_flat ? 1 : 0) + (use_smooth ? 1 : 0) +
69 (use_noperspective ? 1 : 0);
70
71#if 0
72 if (num_used_interpolation_types > 1) {
73 std::cout << "'" << iface.name << "' uses multiple interpolation types\n";
74 }
75#endif
76
77 return num_used_interpolation_types <= 1;
78}
79
81{
82 /* Vulkan doesn't support setting an interpolation mode per attribute in a struct. */
83 for (const StageInterfaceInfo *iface : vertex_out_interfaces_) {
84 if (!is_vulkan_compatible_interface(*iface)) {
85 return false;
86 }
87 }
88 for (const StageInterfaceInfo *iface : geometry_out_interfaces_) {
89 if (!is_vulkan_compatible_interface(*iface)) {
90 return false;
91 }
92 }
93
94 return true;
95}
96
98
99void ShaderCreateInfo::finalize(const bool recursive)
100{
101 if (finalized_) {
102 return;
103 }
104 finalized_ = true;
105
106 Set<StringRefNull> deps_merged;
107
109
110 for (auto &info_name : additional_infos_) {
111
112 /* Fetch create info. */
113 const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(
114 gpu_shader_create_info_get(info_name.c_str()));
115
116 if (recursive) {
117 const_cast<ShaderCreateInfo &>(info).finalize(recursive);
118 }
119 else {
121 }
122
124
125 /* NOTE: EEVEE Materials can result in nested includes. To avoid duplicate
126 * shader resources, we need to avoid inserting duplicates.
127 * TODO: Optimize create info preparation to include each individual "additional_info"
128 * only a single time. */
129 vertex_inputs_.extend_non_duplicates(info.vertex_inputs_);
130 fragment_outputs_.extend_non_duplicates(info.fragment_outputs_);
131 vertex_out_interfaces_.extend_non_duplicates(info.vertex_out_interfaces_);
132 geometry_out_interfaces_.extend_non_duplicates(info.geometry_out_interfaces_);
133 subpass_inputs_.extend_non_duplicates(info.subpass_inputs_);
134 specialization_constants_.extend_non_duplicates(info.specialization_constants_);
135 compilation_constants_.extend_non_duplicates(info.compilation_constants_);
136
138
139 /* Insert with duplicate check. */
140 push_constants_.extend_non_duplicates(info.push_constants_);
141 defines_.extend_non_duplicates(info.defines_);
142 batch_resources_.extend_non_duplicates(info.batch_resources_);
143 pass_resources_.extend_non_duplicates(info.pass_resources_);
144 geometry_resources_.extend_non_duplicates(info.geometry_resources_);
145 typedef_sources_.extend_non_duplicates(info.typedef_sources_);
146
147 /* API-specific parameters.
148 * We will only copy API-specific parameters if they are otherwise unassigned. */
149#ifdef WITH_METAL_BACKEND
150 if (mtl_max_threads_per_threadgroup_ == 0) {
151 mtl_max_threads_per_threadgroup_ = info.mtl_max_threads_per_threadgroup_;
152 }
153#endif
154
155 if (info.early_fragment_test_) {
158 }
159 /* Modify depth write if has been changed from default.
160 * `UNCHANGED` implies gl_FragDepth is not used at all. */
163 }
164
165 /* Inherit builtin bits from additional info. */
166 builtins_ |= info.builtins_;
167
168 validate_merge(info);
169
170 auto assert_no_overlap = [&](const bool test, const StringRefNull error) {
171 if (!test) {
172 std::cout << name_ << ": Validation failed while merging " << info.name_ << " : ";
173 std::cout << error << std::endl;
174 BLI_assert(0);
175 }
176 };
177
178 if (!deps_merged.add(info.name_)) {
179 assert_no_overlap(false, "additional info already merged via another info");
180 }
181
182 if (info.compute_layout_.local_size_x != -1) {
183 assert_no_overlap(compute_layout_.local_size_x == -1, "Compute layout already defined");
185 }
186
187 if (!info.vertex_source_.is_empty()) {
188 assert_no_overlap(vertex_source_.is_empty(), "Vertex source already existing");
190 }
191 if (!info.geometry_source_.is_empty()) {
192 assert_no_overlap(geometry_source_.is_empty(), "Geometry source already existing");
195 }
196 if (!info.fragment_source_.is_empty()) {
197 assert_no_overlap(fragment_source_.is_empty(), "Fragment source already existing");
199 }
200 if (!info.compute_source_.is_empty()) {
201 assert_no_overlap(compute_source_.is_empty(), "Compute source already existing");
203 }
204 }
205
206 if (!geometry_source_.is_empty() && bool(builtins_ & BuiltinBits::LAYER)) {
207 std::cout << name_
208 << ": Validation failed. BuiltinBits::LAYER shouldn't be used with geometry shaders."
209 << std::endl;
210 BLI_assert(0);
211 }
212
214 int images = 0, samplers = 0, ubos = 0, ssbos = 0;
215
216 auto set_resource_slot = [&](Resource &res) {
217 switch (res.bind_type) {
219 res.slot = ubos++;
220 break;
222 res.slot = ssbos++;
223 break;
225 res.slot = samplers++;
226 break;
228 res.slot = images++;
229 break;
230 }
231 };
232
233 for (auto &res : batch_resources_) {
234 set_resource_slot(res);
235 }
236 for (auto &res : pass_resources_) {
237 set_resource_slot(res);
238 }
239 for (auto &res : geometry_resources_) {
240 set_resource_slot(res);
241 }
242 }
243}
244
246{
247 std::string error;
248
249 /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
250 if (this->compute_source_.is_empty()) {
251 if (this->vertex_source_.is_empty()) {
252 error += "Missing vertex shader in " + this->name_ + ".\n";
253 }
254 if (this->fragment_source_.is_empty()) {
255 error += "Missing fragment shader in " + this->name_ + ".\n";
256 }
257 }
258 else {
259 if (!this->vertex_source_.is_empty()) {
260 error += "Compute shader has vertex_source_ shader attached in " + this->name_ + ".\n";
261 }
262 if (!this->geometry_source_.is_empty()) {
263 error += "Compute shader has geometry_source_ shader attached in " + this->name_ + ".\n";
264 }
265 if (!this->fragment_source_.is_empty()) {
266 error += "Compute shader has fragment_source_ shader attached in " + this->name_ + ".\n";
267 }
268 }
269
270 if (!this->geometry_source_.is_empty()) {
271 if (bool(this->builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
272 error += "Shader " + this->name_ +
273 " has geometry stage and uses barycentric coordinates. This is not allowed as "
274 "fallback injects a geometry stage.\n";
275 }
276 if (bool(this->builtins_ & BuiltinBits::VIEWPORT_INDEX)) {
277 error += "Shader " + this->name_ +
278 " has geometry stage and uses multi-viewport. This is not allowed as "
279 "fallback injects a geometry stage.\n";
280 }
281 if (bool(this->builtins_ & BuiltinBits::LAYER)) {
282 error += "Shader " + this->name_ +
283 " has geometry stage and uses layer output. This is not allowed as "
284 "fallback injects a geometry stage.\n";
285 }
286 }
287
288 if ((G.debug & G_DEBUG_GPU) == 0) {
289 return error;
290 }
291
292 if (bool(this->builtins_ &
294 {
296 if (interface->instance_name.is_empty()) {
297 error += "Shader " + this->name_ + " uses interface " + interface->name +
298 " that doesn't contain an instance name, but is required for the fallback "
299 "geometry shader.\n";
300 }
301 }
302 }
303
304 if (!this->is_vulkan_compatible()) {
305 error += this->name_ +
306 " contains a stage interface using an instance name and mixed interpolation modes. "
307 "This is not compatible with Vulkan and need to be adjusted.\n";
308 }
309
310 /* Validate specialization constants. */
311 for (int i = 0; i < specialization_constants_.size(); i++) {
312 for (int j = i + 1; j < specialization_constants_.size(); j++) {
314 error += this->name_ + " contains two specialization constants with the name: " +
315 std::string(specialization_constants_[i].name);
316 }
317 }
318 }
319
320 /* Validate compilation constants. */
321 for (int i = 0; i < compilation_constants_.size(); i++) {
322 for (int j = i + 1; j < compilation_constants_.size(); j++) {
323 if (compilation_constants_[i].name == compilation_constants_[j].name) {
324 error += this->name_ + " contains two compilation constants with the name: " +
325 std::string(compilation_constants_[i].name);
326 }
327 }
328 }
329
330 return error;
331}
332
334{
336 /* Check same bind-points usage in OGL. */
337 Set<int> images, samplers, ubos, ssbos;
338
339 auto register_resource = [&](const Resource &res) -> bool {
340 switch (res.bind_type) {
342 return images.add(res.slot);
344 return samplers.add(res.slot);
346 return ubos.add(res.slot);
348 return ssbos.add(res.slot);
349 default:
350 return false;
351 }
352 };
353
354 auto print_error_msg = [&](const Resource &res, const Vector<Resource> &resources) {
355 auto print_resource_name = [&](const Resource &res) {
356 switch (res.bind_type) {
358 std::cout << "Uniform Buffer " << res.uniformbuf.name;
359 break;
361 std::cout << "Storage Buffer " << res.storagebuf.name;
362 break;
364 std::cout << "Sampler " << res.sampler.name;
365 break;
367 std::cout << "Image " << res.image.name;
368 break;
369 default:
370 std::cout << "Unknown Type";
371 break;
372 }
373 };
374
375 for (const Resource &_res : resources) {
376 if (&res != &_res && res.bind_type == _res.bind_type && res.slot == _res.slot) {
377 std::cout << name_ << ": Validation failed : Overlapping ";
378 print_resource_name(res);
379 std::cout << " and ";
380 print_resource_name(_res);
381 std::cout << " at (" << res.slot << ") while merging " << other_info.name_ << std::endl;
382 }
383 }
384 };
385
386 for (auto &res : batch_resources_) {
387 if (register_resource(res) == false) {
388 print_error_msg(res, resources_get_all_());
389 }
390 }
391
392 for (auto &res : pass_resources_) {
393 if (register_resource(res) == false) {
394 print_error_msg(res, resources_get_all_());
395 }
396 }
397
398 for (auto &res : geometry_resources_) {
399 if (register_resource(res) == false) {
400 print_error_msg(res, resources_get_all_());
401 }
402 }
403 }
404}
405
407{
408 uint32_t attr_bits = 0;
409 for (auto &attr : vertex_inputs_) {
410 if (attr.index >= 16 || attr.index < 0) {
411 std::cout << name_ << ": \"" << attr.name
412 << "\" : Type::float3x3_t unsupported as vertex attribute." << std::endl;
413 BLI_assert(0);
414 }
415 if (attr.index >= 16 || attr.index < 0) {
416 std::cout << name_ << ": Invalid index for attribute \"" << attr.name << "\"" << std::endl;
417 BLI_assert(0);
418 }
419 uint32_t attr_new = 0;
420 if (attr.type == Type::float4x4_t) {
421 for (int i = 0; i < 4; i++) {
422 attr_new |= 1 << (attr.index + i);
423 }
424 }
425 else {
426 attr_new |= 1 << attr.index;
427 }
428
429 if ((attr_bits & attr_new) != 0) {
430 std::cout << name_ << ": Attribute \"" << attr.name
431 << "\" overlap one or more index from another attribute."
432 " Note that mat4 takes up 4 indices.";
433 if (other_info) {
434 std::cout << " While merging " << other_info->name_ << std::endl;
435 }
436 std::cout << std::endl;
437 BLI_assert(0);
438 }
439 attr_bits |= attr_new;
440 }
441}
442
443} // namespace blender::gpu::shader
444
445using namespace blender::gpu::shader;
446
447#ifdef _MSC_VER
448/* Disable optimization for this function with MSVC. It does not like the fact
449 * shaders info are declared in the same function (same basic block or not does
450 * not change anything).
451 * Since it is just a function called to register shaders (once),
452 * the fact it's optimized or not does not matter, it's not on any hot
453 * code path. */
454# pragma optimize("", off)
455#endif
457{
460
461#define GPU_SHADER_NAMED_INTERFACE_INFO(_interface, _inst_name) \
462 StageInterfaceInfo *ptr_##_interface = new StageInterfaceInfo(#_interface, #_inst_name); \
463 StageInterfaceInfo &_interface = *ptr_##_interface; \
464 g_interfaces->add_new(#_interface, ptr_##_interface); \
465 _interface
466
467#define GPU_SHADER_INTERFACE_INFO(_interface) \
468 StageInterfaceInfo *ptr_##_interface = new StageInterfaceInfo(#_interface); \
469 StageInterfaceInfo &_interface = *ptr_##_interface; \
470 g_interfaces->add_new(#_interface, ptr_##_interface); \
471 _interface
472
473#define GPU_SHADER_CREATE_INFO(_info) \
474 ShaderCreateInfo *ptr_##_info = new ShaderCreateInfo(#_info); \
475 ShaderCreateInfo &_info = *ptr_##_info; \
476 g_create_infos->add_new(#_info, ptr_##_info); \
477 _info
478
479#define GPU_SHADER_NAMED_INTERFACE_END(_inst_name) ;
480#define GPU_SHADER_INTERFACE_END() ;
481#define GPU_SHADER_CREATE_END() ;
482
483/* Declare, register and construct the infos. */
485
486 /* WORKAROUND: Replace the use of gpu_BaseInstance by an instance attribute. */
487 if (GPU_shader_draw_parameters_support() == false) {
488 draw_resource_id = draw_resource_id_fallback;
489 draw_resource_with_custom_id = draw_resource_with_custom_id_fallback;
490 }
491
493 /* WORKAROUND: Adding a dummy buffer that isn't used fixes a bug inside the Qualcomm driver. */
494 eevee_deferred_tile_classify.storage_buf(
495 12, Qualifier::read_write, "uint", "dummy_workaround_buf[]");
496 }
497
498 for (ShaderCreateInfo *info : g_create_infos->values()) {
499 info->builtins_ |= gpu_shader_dependency_get_builtins(info->vertex_source_);
500 info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_);
501 info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_);
502 info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_);
503
504#if GPU_SHADER_PRINTF_ENABLE
505 const bool is_material_shader = info->name_.startswith("eevee_surf_");
506 if ((info->builtins_ & BuiltinBits::USE_PRINTF) == BuiltinBits::USE_PRINTF ||
507 (gpu_shader_dependency_force_gpu_print_injection() && is_material_shader))
508 {
509 info->additional_info("gpu_print");
510 }
511#endif
512
513#ifndef NDEBUG
514 /* Automatically amend the create info for ease of use of the debug feature. */
515 if ((info->builtins_ & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) {
516 info->additional_info("draw_debug_draw");
517 }
518#endif
519 }
520
521 for (ShaderCreateInfo *info : g_create_infos->values()) {
522 info->finalize(true);
523 }
524
525 /* TEST */
526 // gpu_shader_create_info_compile(nullptr);
527}
528#ifdef _MSC_VER
529# pragma optimize("", on)
530#endif
531
533{
534 for (auto *value : g_create_infos->values()) {
535 delete value;
536 }
537 delete g_create_infos;
538
539 for (auto *value : g_interfaces->values()) {
540 delete value;
541 }
542 delete g_interfaces;
543}
544
545bool gpu_shader_create_info_compile(const char *name_starts_with_filter)
546{
547 using namespace blender;
548 using namespace blender::gpu;
549 int success = 0;
550 int skipped_filter = 0;
551 int skipped = 0;
552 int total = 0;
553
555
556 for (ShaderCreateInfo *info : g_create_infos->values()) {
557 info->finalize();
558 if (info->do_static_compilation_) {
559 if (name_starts_with_filter &&
560 !info->name_.startswith(blender::StringRefNull(name_starts_with_filter)))
561 {
562 skipped_filter++;
563 continue;
564 }
565 if ((info->metal_backend_only_ && GPU_backend_get_type() != GPU_BACKEND_METAL) ||
566 (GPU_geometry_shader_support() == false && info->geometry_source_ != nullptr))
567 {
568 skipped++;
569 continue;
570 }
571 total++;
572
573 infos.append(reinterpret_cast<const GPUShaderCreateInfo *>(info));
574 }
575 }
576
579
580 for (int i : result.index_range()) {
581 const ShaderCreateInfo *info = reinterpret_cast<const ShaderCreateInfo *>(infos[i]);
582 if (result[i] == nullptr) {
583 std::cerr << "Compilation " << info->name_.c_str() << " Failed\n";
584 }
585 else {
586 success++;
587#if 0 /* TODO(fclem): This is too verbose for now. Make it a cmake option. */
588 /* Test if any resource is optimized out and print a warning if that's the case. */
589 /* TODO(fclem): Limit this to OpenGL backend. */
590 const ShaderInterface *interface = unwrap(shader)->interface;
591
593
594 for (ShaderCreateInfo::Resource &res : all_resources) {
595 blender::StringRefNull name = "";
596 const ShaderInput *input = nullptr;
597
598 switch (res.bind_type) {
600 input = interface->ubo_get(res.slot);
601 name = res.uniformbuf.name;
602 break;
604 input = interface->ssbo_get(res.slot);
605 name = res.storagebuf.name;
606 break;
608 input = interface->texture_get(res.slot);
609 name = res.sampler.name;
610 break;
612 input = interface->texture_get(res.slot);
613 name = res.image.name;
614 break;
615 }
616
617 if (input == nullptr) {
618 std::cerr << "Error: " << info->name_;
619 std::cerr << ": Resource « " << name << " » not found in the shader interface\n";
620 }
621 else if (input->location == -1) {
622 std::cerr << "Warning: " << info->name_;
623 std::cerr << ": Resource « " << name << " » is optimized out\n";
624 }
625 }
626#endif
628 }
629 }
630
631 printf("Shader Test compilation result: %d / %d passed", success, total);
632 if (skipped_filter > 0) {
633 printf(" (skipped %d when filtering)", skipped_filter);
634 }
635 if (skipped > 0) {
636 printf(" (skipped %d for compatibility reasons)", skipped);
637 }
638 printf("\n");
639 return success == total;
640}
641
642const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name)
643{
644 if (g_create_infos->contains(info_name) == false) {
645 printf("Error: Cannot find shader create info named \"%s\"\n", info_name);
646 return nullptr;
647 }
648 ShaderCreateInfo *info = g_create_infos->lookup(info_name);
649 return reinterpret_cast<const GPUShaderCreateInfo *>(info);
650}
@ G_DEBUG_GPU
#define BLI_assert(a)
Definition BLI_assert.h:46
float[3] Vector
bool GPU_geometry_shader_support()
bool GPU_stencil_clasify_buffer_workaround()
bool GPU_shader_draw_parameters_support()
eGPUBackendType GPU_backend_get_type()
blender::Vector< GPUShader * > GPU_shader_batch_finalize(BatchHandle &handle)
int64_t BatchHandle
Definition GPU_shader.hh:83
void GPU_shader_free(GPUShader *shader)
BatchHandle GPU_shader_batch_create_from_infos(blender::Span< const GPUShaderCreateInfo * > infos, CompilationPriority priority=CompilationPriority::High)
void append(const T &value)
ValueIterator values() const &
Definition BLI_map.hh:884
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr bool is_empty() const
constexpr const char * c_str() const
struct @064345207361167251075330302113175271221317160336::@113254110077376341056327177062323111323010325277 batch
#define input
#define interface
#define printf(...)
const GPUShaderCreateInfo * gpu_shader_create_info_get(const char *info_name)
void gpu_shader_create_info_exit()
bool gpu_shader_create_info_compile(const char *name_starts_with_filter)
void gpu_shader_create_info_init()
#define G(x, y, z)
static void error(const char *str)
Map< StringRef, ShaderCreateInfo * > CreateInfoDictionnary
Map< StringRef, StageInterfaceInfo * > InterfaceDictionnary
BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull shader_source_name)
static InterfaceDictionnary * g_interfaces
bool gpu_shader_dependency_force_gpu_print_injection()
static bool is_vulkan_compatible_interface(const StageInterfaceInfo &iface)
static CreateInfoDictionnary * g_create_infos
Describe inputs & outputs, stage interfaces, resources and sources of a shader. If all data is correc...
Vector< StageInterfaceInfo * > vertex_out_interfaces_
Vector< std::array< StringRefNull, 2 > > defines_
Vector< CompilationConstant, 0 > compilation_constants_
void validate_vertex_attributes(const ShaderCreateInfo *other_info=nullptr)
void finalize(const bool recursive=false)
Vector< StageInterfaceInfo * > geometry_out_interfaces_
void validate_merge(const ShaderCreateInfo &other_info)
Vector< SpecializationConstant > specialization_constants_
i
Definition text_draw.cc:230