Blender V5.0
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::resource_guard_defines(std::string &defines) const
100{
101 if (name_.startswith("MA") || name_.startswith("WO")) {
102 defines += "#define CREATE_INFO_Material\n";
103 }
104 else {
105 defines += "#define CREATE_INFO_" + name_ + "\n";
106 }
107 for (const auto &info_name : additional_infos_) {
108 const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(
109 gpu_shader_create_info_get(info_name.c_str()));
110
111 info.resource_guard_defines(defines);
112 }
113}
114
115void ShaderCreateInfo::finalize(const bool recursive)
116{
117 if (finalized_) {
118 return;
119 }
120 finalized_ = true;
121
122 Set<StringRefNull> deps_merged;
123
125
126 for (auto &info_name : additional_infos_) {
127
128 /* Fetch create info. */
129 const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(
130 gpu_shader_create_info_get(info_name.c_str()));
131
132 if (recursive) {
133 const_cast<ShaderCreateInfo &>(info).finalize(recursive);
134 }
135 else {
137 }
138
140
141 /* NOTE: EEVEE Materials can result in nested includes. To avoid duplicate
142 * shader resources, we need to avoid inserting duplicates.
143 * TODO: Optimize create info preparation to include each individual "additional_info"
144 * only a single time. */
145 vertex_inputs_.extend_non_duplicates(info.vertex_inputs_);
146 fragment_outputs_.extend_non_duplicates(info.fragment_outputs_);
147 vertex_out_interfaces_.extend_non_duplicates(info.vertex_out_interfaces_);
148 geometry_out_interfaces_.extend_non_duplicates(info.geometry_out_interfaces_);
149 subpass_inputs_.extend_non_duplicates(info.subpass_inputs_);
150 specialization_constants_.extend_non_duplicates(info.specialization_constants_);
151 compilation_constants_.extend_non_duplicates(info.compilation_constants_);
152
154
156
157 /* Insert with duplicate check. */
158 push_constants_.extend_non_duplicates(info.push_constants_);
159 defines_.extend_non_duplicates(info.defines_);
160 batch_resources_.extend_non_duplicates(info.batch_resources_);
161 pass_resources_.extend_non_duplicates(info.pass_resources_);
162 geometry_resources_.extend_non_duplicates(info.geometry_resources_);
163 typedef_sources_.extend_non_duplicates(info.typedef_sources_);
164
165 /* API-specific parameters.
166 * We will only copy API-specific parameters if they are otherwise unassigned. */
167#ifdef WITH_METAL_BACKEND
168 if (mtl_max_threads_per_threadgroup_ == 0) {
169 mtl_max_threads_per_threadgroup_ = info.mtl_max_threads_per_threadgroup_;
170 }
171#endif
172
173 if (info.early_fragment_test_) {
176 }
177 /* Modify depth write if has been changed from default.
178 * `UNCHANGED` implies gl_FragDepth is not used at all. */
181 }
182
183 /* Inherit builtin bits from additional info. */
184 builtins_ |= info.builtins_;
185
186 validate_merge(info);
187
188 auto assert_no_overlap = [&](const bool test, const StringRefNull error) {
189 if (!test) {
190 std::cout << name_ << ": Validation failed while merging " << info.name_ << " : ";
191 std::cout << error << std::endl;
192 BLI_assert(0);
193 }
194 };
195
196 if (!deps_merged.add(info.name_)) {
197 assert_no_overlap(false, "additional info already merged via another info");
198 }
199
200 if (info.compute_layout_.local_size_x != -1) {
201 assert_no_overlap(compute_layout_.local_size_x == -1, "Compute layout already defined");
203 }
204
205 if (!info.vertex_source_.is_empty()) {
206 assert_no_overlap(vertex_source_.is_empty(), "Vertex source already existing");
208 }
209 if (!info.geometry_source_.is_empty()) {
210 assert_no_overlap(geometry_source_.is_empty(), "Geometry source already existing");
213 }
214 if (!info.fragment_source_.is_empty()) {
215 assert_no_overlap(fragment_source_.is_empty(), "Fragment source already existing");
217 }
218 if (!info.compute_source_.is_empty()) {
219 assert_no_overlap(compute_source_.is_empty(), "Compute source already existing");
221 }
222
223 if (info.vertex_entry_fn_ != "main") {
224 assert_no_overlap(vertex_entry_fn_ == "main", "Vertex function already existing");
226 }
227 if (info.geometry_entry_fn_ != "main") {
228 assert_no_overlap(geometry_entry_fn_ == "main", "Geometry function already existing");
230 }
231 if (info.fragment_entry_fn_ != "main") {
232 assert_no_overlap(fragment_entry_fn_ == "main", "Fragment function already existing");
234 }
235 if (info.compute_entry_fn_ != "main") {
236 assert_no_overlap(compute_entry_fn_ == "main", "Compute function already existing");
238 }
239 }
240
241 if (!geometry_source_.is_empty() && bool(builtins_ & BuiltinBits::LAYER)) {
242 std::cout << name_
243 << ": Validation failed. BuiltinBits::LAYER shouldn't be used with geometry shaders."
244 << std::endl;
245 BLI_assert(0);
246 }
247
249 int images = 0, samplers = 0, ubos = 0, ssbos = 0;
250
251 auto set_resource_slot = [&](Resource &res) {
252 switch (res.bind_type) {
254 res.slot = ubos++;
255 break;
257 res.slot = ssbos++;
258 break;
260 res.slot = samplers++;
261 break;
263 res.slot = images++;
264 break;
265 }
266 };
267
268 for (auto &res : batch_resources_) {
269 set_resource_slot(res);
270 }
271 for (auto &res : pass_resources_) {
272 set_resource_slot(res);
273 }
274 for (auto &res : geometry_resources_) {
275 set_resource_slot(res);
276 }
277 }
278}
279
281{
282 std::string error;
283
284 /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
285 if (this->compute_source_.is_empty()) {
286 if (this->vertex_source_.is_empty()) {
287 error += "Missing vertex shader in " + this->name_ + ".\n";
288 }
289 if (this->fragment_source_.is_empty()) {
290 error += "Missing fragment shader in " + this->name_ + ".\n";
291 }
292 }
293 else {
294 if (!this->vertex_source_.is_empty()) {
295 error += "Compute shader has vertex_source_ shader attached in " + this->name_ + ".\n";
296 }
297 if (!this->geometry_source_.is_empty()) {
298 error += "Compute shader has geometry_source_ shader attached in " + this->name_ + ".\n";
299 }
300 if (!this->fragment_source_.is_empty()) {
301 error += "Compute shader has fragment_source_ shader attached in " + this->name_ + ".\n";
302 }
303 }
304
305 if (!this->geometry_source_.is_empty()) {
306 if (bool(this->builtins_ & BuiltinBits::BARYCENTRIC_COORD)) {
307 error += "Shader " + this->name_ +
308 " has geometry stage and uses barycentric coordinates. This is not allowed as "
309 "fallback injects a geometry stage.\n";
310 }
311 if (bool(this->builtins_ & BuiltinBits::VIEWPORT_INDEX)) {
312 error += "Shader " + this->name_ +
313 " has geometry stage and uses multi-viewport. This is not allowed as "
314 "fallback injects a geometry stage.\n";
315 }
316 if (bool(this->builtins_ & BuiltinBits::LAYER)) {
317 error += "Shader " + this->name_ +
318 " has geometry stage and uses layer output. This is not allowed as "
319 "fallback injects a geometry stage.\n";
320 }
321 }
322
323 if ((G.debug & G_DEBUG_GPU) == 0) {
324 return error;
325 }
326
327 if (bool(this->builtins_ &
329 {
331 if (interface->instance_name.is_empty()) {
332 error += "Shader " + this->name_ + " uses interface " + interface->name +
333 " that doesn't contain an instance name, but is required for the fallback "
334 "geometry shader.\n";
335 }
336 }
337 }
338
339 if (!this->is_vulkan_compatible()) {
340 error += this->name_ +
341 " contains a stage interface using an instance name and mixed interpolation modes. "
342 "This is not compatible with Vulkan and need to be adjusted.\n";
343 }
344
345 /* Validate specialization constants. */
346 for (int i = 0; i < specialization_constants_.size(); i++) {
347 for (int j = i + 1; j < specialization_constants_.size(); j++) {
349 error += this->name_ + " contains two specialization constants with the name: " +
350 std::string(specialization_constants_[i].name);
351 }
352 }
353 }
354
355 /* Validate compilation constants. */
356 for (int i = 0; i < compilation_constants_.size(); i++) {
357 for (int j = i + 1; j < compilation_constants_.size(); j++) {
359 error += this->name_ + " contains two compilation constants with the name: " +
360 std::string(compilation_constants_[i].name);
361 }
362 }
363 }
364
365 /* Validate shared variables. */
366 for (int i = 0; i < shared_variables_.size(); i++) {
367 for (int j = i + 1; j < shared_variables_.size(); j++) {
369 error += this->name_ + " contains two specialization constants with the name: " +
370 std::string(shared_variables_[i].name);
371 }
372 }
373 }
374
375 return error;
376}
377
379{
381 /* Check same bind-points usage in OGL. */
382 Set<int> images, samplers, ubos, ssbos;
383
384 auto register_resource = [&](const Resource &res) -> bool {
385 switch (res.bind_type) {
387 return images.add(res.slot);
389 return samplers.add(res.slot);
391 return ubos.add(res.slot);
393 return ssbos.add(res.slot);
394 default:
395 return false;
396 }
397 };
398
399 auto print_error_msg = [&](const Resource &res, const Vector<Resource> &resources) {
400 auto print_resource_name = [&](const Resource &res) {
401 switch (res.bind_type) {
403 std::cout << "Uniform Buffer " << res.uniformbuf.name;
404 break;
406 std::cout << "Storage Buffer " << res.storagebuf.name;
407 break;
409 std::cout << "Sampler " << res.sampler.name;
410 break;
412 std::cout << "Image " << res.image.name;
413 break;
414 default:
415 std::cout << "Unknown Type";
416 break;
417 }
418 };
419
420 for (const Resource &_res : resources) {
421 if (&res != &_res && res.bind_type == _res.bind_type && res.slot == _res.slot) {
422 std::cout << name_ << ": Validation failed : Overlapping ";
423 print_resource_name(res);
424 std::cout << " and ";
425 print_resource_name(_res);
426 std::cout << " at (" << res.slot << ") while merging " << other_info.name_ << std::endl;
427 }
428 }
429 };
430
431 for (auto &res : batch_resources_) {
432 if (register_resource(res) == false) {
433 print_error_msg(res, resources_get_all_());
434 }
435 }
436
437 for (auto &res : pass_resources_) {
438 if (register_resource(res) == false) {
439 print_error_msg(res, resources_get_all_());
440 }
441 }
442
443 for (auto &res : geometry_resources_) {
444 if (register_resource(res) == false) {
445 print_error_msg(res, resources_get_all_());
446 }
447 }
448 }
449}
450
452{
453 uint32_t attr_bits = 0;
454 for (auto &attr : vertex_inputs_) {
455 if (attr.index >= 16 || attr.index < 0) {
456 std::cout << name_ << ": \"" << attr.name
457 << "\" : Type::float3x3_t unsupported as vertex attribute." << std::endl;
458 BLI_assert(0);
459 }
460 if (attr.index >= 16 || attr.index < 0) {
461 std::cout << name_ << ": Invalid index for attribute \"" << attr.name << "\"" << std::endl;
462 BLI_assert(0);
463 }
464 uint32_t attr_new = 0;
465 if (attr.type == Type::float4x4_t) {
466 for (int i = 0; i < 4; i++) {
467 attr_new |= 1 << (attr.index + i);
468 }
469 }
470 else {
471 attr_new |= 1 << attr.index;
472 }
473
474 if ((attr_bits & attr_new) != 0) {
475 std::cout << name_ << ": Attribute \"" << attr.name
476 << "\" overlap one or more index from another attribute."
477 " Note that mat4 takes up 4 indices.";
478 if (other_info) {
479 std::cout << " While merging " << other_info->name_ << std::endl;
480 }
481 std::cout << std::endl;
482 BLI_assert(0);
483 }
484 attr_bits |= attr_new;
485 }
486}
487
488} // namespace blender::gpu::shader
489
490using namespace blender::gpu::shader;
491
492#ifdef _MSC_VER
493/* Disable optimization for this function with MSVC. It does not like the fact
494 * shaders info are declared in the same function (same basic block or not does
495 * not change anything).
496 * Since it is just a function called to register shaders (once),
497 * the fact it's optimized or not does not matter, it's not on any hot
498 * code path. */
499# pragma optimize("", off)
500#endif
502{
505
506#define GPU_SHADER_NAMED_INTERFACE_INFO(_interface, _inst_name) \
507 StageInterfaceInfo *ptr_##_interface = new StageInterfaceInfo(#_interface, #_inst_name); \
508 StageInterfaceInfo &_interface = *ptr_##_interface; \
509 g_interfaces->add_new(#_interface, ptr_##_interface); \
510 _interface
511
512#define GPU_SHADER_INTERFACE_INFO(_interface) \
513 StageInterfaceInfo *ptr_##_interface = new StageInterfaceInfo(#_interface); \
514 StageInterfaceInfo &_interface = *ptr_##_interface; \
515 g_interfaces->add_new(#_interface, ptr_##_interface); \
516 _interface
517
518#define GPU_SHADER_CREATE_INFO(_info) \
519 ShaderCreateInfo *ptr_##_info = new ShaderCreateInfo(#_info); \
520 ShaderCreateInfo &_info = *ptr_##_info; \
521 g_create_infos->add_new(#_info, ptr_##_info); \
522 _info
523
524#define GPU_SHADER_NAMED_INTERFACE_END(_inst_name) ;
525#define GPU_SHADER_INTERFACE_END() ;
526#define GPU_SHADER_CREATE_END() ;
527
528/* Declare, register and construct the infos. */
529#include "glsl_compositor_infos_list.hh"
530#include "glsl_draw_infos_list.hh"
531#include "glsl_gpu_infos_list.hh"
532#include "glsl_ocio_infos_list.hh"
533#ifdef WITH_OPENSUBDIV
534# include "glsl_osd_infos_list.hh"
535#endif
536
538 /* WORKAROUND: Adding a dummy buffer that isn't used fixes a bug inside the Qualcomm driver. */
539 eevee_deferred_tile_classify.storage_buf(
540 12, Qualifier::read_write, "uint", "dummy_workaround_buf[]");
541 }
542
543 for (ShaderCreateInfo *info : g_create_infos->values()) {
544 info->is_generated_ = false;
545
546 info->builtins_ |= gpu_shader_dependency_get_builtins(info->vertex_source_);
547 info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_);
548 info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_);
549 info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_);
550
551#if GPU_SHADER_PRINTF_ENABLE
552 const bool is_material_shader = info->name_.startswith("eevee_surf_");
553 if ((info->builtins_ & BuiltinBits::USE_PRINTF) == BuiltinBits::USE_PRINTF ||
554 (gpu_shader_dependency_force_gpu_print_injection() && is_material_shader))
555 {
556 info->additional_info("gpu_print");
557 }
558#endif
559
560#ifndef NDEBUG
561 /* Automatically amend the create info for ease of use of the debug feature. */
562 if ((info->builtins_ & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) {
563 info->additional_info("draw_debug_draw");
564 }
565#endif
566 }
567
568 for (ShaderCreateInfo *info : g_create_infos->values()) {
569 info->finalize(true);
570 }
571
572 /* TEST */
573 // gpu_shader_create_info_compile(nullptr);
574}
575#ifdef _MSC_VER
576# pragma optimize("", on)
577#endif
578
580{
581 for (auto *value : g_create_infos->values()) {
582 delete value;
583 }
584 delete g_create_infos;
585
586 for (auto *value : g_interfaces->values()) {
587 delete value;
588 }
589 delete g_interfaces;
590}
591
592bool gpu_shader_create_info_compile(const char *name_starts_with_filter)
593{
594 using namespace blender;
595 using namespace blender::gpu;
596 int success = 0;
597 int skipped_filter = 0;
598 int skipped = 0;
599 int total = 0;
600
602
603 for (ShaderCreateInfo *info : g_create_infos->values()) {
604 info->finalize();
605 if (info->do_static_compilation_) {
606 if (name_starts_with_filter &&
607 !info->name_.startswith(blender::StringRefNull(name_starts_with_filter)))
608 {
609 skipped_filter++;
610 continue;
611 }
612 if ((info->metal_backend_only_ && GPU_backend_get_type() != GPU_BACKEND_METAL) ||
613 (GPU_geometry_shader_support() == false && info->geometry_source_ != nullptr))
614 {
615 skipped++;
616 continue;
617 }
618 total++;
619
620 infos.append(reinterpret_cast<const GPUShaderCreateInfo *>(info));
621 }
622 }
623
626
627 for (int i : result.index_range()) {
628 const ShaderCreateInfo *info = reinterpret_cast<const ShaderCreateInfo *>(infos[i]);
629 if (result[i] == nullptr) {
630 std::cerr << "Compilation " << info->name_.c_str() << " Failed\n";
631 }
632 else {
633 success++;
634#if 0 /* TODO(fclem): This is too verbose for now. Make it a cmake option. */
635 /* Test if any resource is optimized out and print a warning if that's the case. */
636 /* TODO(fclem): Limit this to OpenGL backend. */
637 const ShaderInterface *interface = shader->interface;
638
640
641 for (ShaderCreateInfo::Resource &res : all_resources) {
643 const ShaderInput *input = nullptr;
644
645 switch (res.bind_type) {
647 input = interface->ubo_get(res.slot);
648 name = res.uniformbuf.name;
649 break;
651 input = interface->ssbo_get(res.slot);
652 name = res.storagebuf.name;
653 break;
655 input = interface->texture_get(res.slot);
656 name = res.sampler.name;
657 break;
659 input = interface->texture_get(res.slot);
660 name = res.image.name;
661 break;
662 }
663
664 if (input == nullptr) {
665 std::cerr << "Error: " << info->name_;
666 std::cerr << ": Resource « " << name << " » not found in the shader interface\n";
667 }
668 else if (input->location == -1) {
669 std::cerr << "Warning: " << info->name_;
670 std::cerr << ": Resource « " << name << " » is optimized out\n";
671 }
672 }
673#endif
675 }
676 }
677
678 printf("Shader Test compilation result: %d / %d passed", success, total);
679 if (skipped_filter > 0) {
680 printf(" (skipped %d when filtering)", skipped_filter);
681 }
682 if (skipped > 0) {
683 printf(" (skipped %d for compatibility reasons)", skipped);
684 }
685 printf("\n");
686 return success == total;
687}
688
689const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name)
690{
691 if (g_create_infos->contains(info_name) == false) {
692 printf("Error: Cannot find shader create info named \"%s\"\n", info_name);
693 return nullptr;
694 }
695 ShaderCreateInfo *info = g_create_infos->lookup(info_name);
696 return reinterpret_cast<const GPUShaderCreateInfo *>(info);
697}
@ G_DEBUG_GPU
#define BLI_assert(a)
Definition BLI_assert.h:46
bool GPU_geometry_shader_support()
bool GPU_stencil_clasify_buffer_workaround()
GPUBackendType GPU_backend_get_type()
void GPU_shader_free(blender::gpu::Shader *shader)
int64_t BatchHandle
Definition GPU_shader.hh:82
blender::Vector< blender::gpu::Shader * > GPU_shader_batch_finalize(BatchHandle &handle)
BatchHandle GPU_shader_batch_create_from_infos(blender::Span< const GPUShaderCreateInfo * > infos, CompilationPriority priority=CompilationPriority::High)
void append(const T &value)
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr bool is_empty() const
constexpr const char * c_str() const
struct @021025263243242147216143265077100330027142264337::@225245033123204053237120173316075113304004012000 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)
static CreateInfoDictionary * g_create_infos
Map< StringRef, ShaderCreateInfo * > CreateInfoDictionary
BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull shader_source_name)
Map< StringRef, StageInterfaceInfo * > InterfaceDictionary
static InterfaceDictionary * g_interfaces
bool gpu_shader_dependency_force_gpu_print_injection()
static bool is_vulkan_compatible_interface(const StageInterfaceInfo &iface)
const char * name
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)
void resource_guard_defines(std::string &defines) const
Vector< SpecializationConstant > specialization_constants_
i
Definition text_draw.cc:230