Blender V4.5
gpu_shader_dependency.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
12#include <algorithm>
13#include <iomanip>
14#include <iostream>
15#include <regex>
16#include <string>
17
18#include "BLI_ghash.h"
19#include "BLI_map.hh"
20#include "BLI_string_ref.hh"
21
26
27#ifdef WITH_OPENSUBDIV
30#endif
31
33
34extern "C" {
35#define SHADER_SOURCE(filename_underscore, filename, filepath) \
36 extern char datatoc_##filename_underscore[];
37#include "glsl_compositor_source_list.h"
38#include "glsl_draw_source_list.h"
39#include "glsl_gpu_source_list.h"
40#include "glsl_ocio_source_list.h"
41#ifdef WITH_OPENSUBDIV
42# include "glsl_osd_source_list.h"
43#endif
44#undef SHADER_SOURCE
45}
46
47namespace blender::gpu {
48
49using GPUPrintFormatMap = Map<uint32_t, shader::PrintfFormat>;
50using GPUSourceDictionnary = Map<StringRef, struct GPUSource *>;
51using GPUFunctionDictionnary = Map<StringRef, GPUFunction *>;
52
53struct GPUSource {
54 StringRefNull fullpath;
55 StringRefNull filename;
56 StringRefNull source;
57 std::string patched_source;
58 Vector<StringRef> dependencies_names;
59 Vector<GPUSource *> dependencies;
60 bool dependencies_init = false;
61 shader::BuiltinBits builtins = shader::BuiltinBits::NONE;
62
63 /* NOTE: The next few functions are needed to keep isolation of the preprocessor.
64 * Eventually, this should be revisited and the preprocessor should output
65 * GPU structures. */
66
67 shader::BuiltinBits convert_builtin_bit(shader::metadata::Builtin builtin)
68 {
69 using namespace blender::gpu::shader;
70 using namespace blender::gpu::shader::metadata;
71 switch (builtin) {
72 case Builtin::FragCoord:
73 return BuiltinBits::FRAG_COORD;
74 case Builtin::FrontFacing:
75 return BuiltinBits::FRONT_FACING;
76 case Builtin::GlobalInvocationID:
77 return BuiltinBits::GLOBAL_INVOCATION_ID;
78 case Builtin::InstanceID:
79 return BuiltinBits::INSTANCE_ID;
80 case Builtin::LocalInvocationID:
81 return BuiltinBits::LOCAL_INVOCATION_ID;
82 case Builtin::LocalInvocationIndex:
83 return BuiltinBits::LOCAL_INVOCATION_INDEX;
84 case Builtin::NumWorkGroup:
85 return BuiltinBits::NUM_WORK_GROUP;
86 case Builtin::PointCoord:
87 return BuiltinBits::POINT_COORD;
88 case Builtin::PointSize:
89 return BuiltinBits::POINT_SIZE;
90 case Builtin::PrimitiveID:
91 return BuiltinBits::PRIMITIVE_ID;
92 case Builtin::VertexID:
93 return BuiltinBits::VERTEX_ID;
94 case Builtin::WorkGroupID:
95 return BuiltinBits::WORK_GROUP_ID;
96 case Builtin::WorkGroupSize:
97 return BuiltinBits::WORK_GROUP_SIZE;
98 case Builtin::drw_debug:
99#ifndef NDEBUG
100 return BuiltinBits::USE_DEBUG_DRAW;
101#else
102 return BuiltinBits::NONE;
103#endif
104 case Builtin::assert:
105 case Builtin::printf:
106#if GPU_SHADER_PRINTF_ENABLE
107 return BuiltinBits::USE_PRINTF;
108#else
109 return BuiltinBits::NONE;
110#endif
111 }
113 return BuiltinBits::NONE;
114 }
115
116 GPUFunctionQual convert_qualifier(shader::metadata::Qualifier qualifier)
117 {
118 using namespace blender::gpu::shader;
119 switch (qualifier) {
120 case metadata::Qualifier::in:
121 return FUNCTION_QUAL_IN;
122 case metadata::Qualifier::out:
123 return FUNCTION_QUAL_OUT;
124 case metadata::Qualifier::inout:
125 return FUNCTION_QUAL_INOUT;
126 }
128 return FUNCTION_QUAL_IN;
129 }
130
131 eGPUType convert_type(shader::metadata::Type type)
132 {
133 using namespace blender::gpu::shader;
134 switch (type) {
135 case metadata::Type::float1:
136 return GPU_FLOAT;
137 case metadata::Type::float2:
138 return GPU_VEC2;
139 case metadata::Type::float3:
140 return GPU_VEC3;
141 case metadata::Type::float4:
142 return GPU_VEC4;
143 case metadata::Type::float3x3:
144 return GPU_MAT3;
145 case metadata::Type::float4x4:
146 return GPU_MAT4;
147 case metadata::Type::sampler1DArray:
148 return GPU_TEX1D_ARRAY;
149 case metadata::Type::sampler2DArray:
150 return GPU_TEX2D_ARRAY;
151 case metadata::Type::sampler2D:
152 return GPU_TEX2D;
153 case metadata::Type::sampler3D:
154 return GPU_TEX3D;
155 case metadata::Type::Closure:
156 return GPU_CLOSURE;
157 }
159 return GPU_NONE;
160 }
161
162 GPUSource(
163 const char *path,
164 const char *file,
165 const char *datatoc,
166 GPUFunctionDictionnary *g_functions,
167 GPUPrintFormatMap *g_formats,
168 std::function<void(GPUSource &, GPUFunctionDictionnary *, GPUPrintFormatMap *)> metadata_fn)
169 : fullpath(path), filename(file), source(datatoc)
170 {
171 metadata_fn(*this, g_functions, g_formats);
172 };
173
174 void add_builtin(shader::metadata::Builtin builtin)
175 {
176 builtins |= convert_builtin_bit(builtin);
177 }
178
179 void add_dependency(StringRef line)
180 {
181 dependencies_names.append(line);
182 }
183
184 void add_printf_format(uint32_t format_hash, std::string format, GPUPrintFormatMap *format_map)
185 {
186 /* TODO(fclem): Move this to gpu log. */
187 if (format_map->contains(format_hash)) {
188 if (format_map->lookup(format_hash).format_str != format) {
189 print_error(format, 0, "printf format hash collision.");
190 }
191 else {
192 /* The format map already have the same format. */
193 }
194 }
195 else {
197 /* Save for hash collision comparison. */
198 fmt.format_str = format;
199
200 /* Escape characters replacement. Do the most common ones. */
201 format = std::regex_replace(format, std::regex(R"(\\n)"), "\n");
202 format = std::regex_replace(format, std::regex(R"(\\v)"), "\v");
203 format = std::regex_replace(format, std::regex(R"(\\t)"), "\t");
204 format = std::regex_replace(format, std::regex(R"(\\')"), "\'");
205 format = std::regex_replace(format, std::regex(R"(\\")"), "\"");
206 format = std::regex_replace(format, std::regex(R"(\\\\)"), "\\");
207
209 shader::PrintfFormat::Block::ArgumentType::NONE;
210 int64_t start = 0, end = 0;
211 while ((end = format.find_first_of('%', start + 1)) != -1) {
212 /* Add the previous block without the newly found % character. */
213 fmt.format_blocks.append({type, format.substr(start, end - start)});
214 /* Format type of the next block. */
215 /* TODO(fclem): This doesn't support advance formats like `%3.2f`. */
216 switch (format[end + 1]) {
217 case 'x':
218 case 'u':
219 type = shader::PrintfFormat::Block::ArgumentType::UINT;
220 break;
221 case 'd':
222 type = shader::PrintfFormat::Block::ArgumentType::INT;
223 break;
224 case 'f':
225 type = shader::PrintfFormat::Block::ArgumentType::FLOAT;
226 break;
227 default:
228 BLI_assert_msg(0, "Printing format unsupported");
229 break;
230 }
231 /* Start of the next block. */
232 start = end;
233 }
234 fmt.format_blocks.append({type, format.substr(start, format.size() - start)});
235
236 format_map->add(format_hash, fmt);
237 }
238 }
239
240 void add_function(StringRefNull name,
242 GPUFunctionDictionnary *g_functions)
243 {
244 GPUFunction *func = MEM_new<GPUFunction>(__func__);
245 name.copy_utf8_truncated(func->name, sizeof(func->name));
246 func->source = reinterpret_cast<void *>(this);
247 func->totparam = 0;
248 for (auto arg : arguments) {
249 if (func->totparam >= ARRAY_SIZE(func->paramtype)) {
250 print_error(source, source.find(name), "Too many parameters in function");
251 break;
252 }
253 func->paramqual[func->totparam] = convert_qualifier(arg.qualifier);
254 func->paramtype[func->totparam] = convert_type(arg.type);
255 func->totparam++;
256 }
257
258 bool insert = g_functions->add(func->name, func);
259 /* NOTE: We allow overloading non void function, but only if the function comes from the
260 * same file. Otherwise the dependency system breaks. */
261 if (!insert) {
262 GPUSource *other_source = reinterpret_cast<GPUSource *>(g_functions->lookup(name)->source);
263 if (other_source != this) {
264 const char *msg = "Function redefinition or overload in two different files ...";
265 print_error(source, source.find(name), msg);
266 print_error(other_source->source,
267 other_source->source.find(name),
268 "... previous definition was here");
269 }
270 else {
271 /* Non-void function overload. */
272 MEM_delete(func);
273 }
274 }
275 }
276
277 void print_error(const StringRef &input, int64_t offset, const StringRef message)
278 {
279 StringRef sub = input.substr(0, offset);
280 int64_t line_number = std::count(sub.begin(), sub.end(), '\n') + 1;
281 int64_t line_end = input.find("\n", offset);
282 int64_t line_start = input.rfind("\n", offset) + 1;
283 int64_t char_number = offset - line_start + 1;
284
285 /* TODO Use clog. */
286
287 std::cerr << fullpath << ":" << line_number << ":" << char_number;
288
289 std::cerr << " error: " << message << "\n";
290 std::cerr << std::setw(5) << line_number << " | "
291 << input.substr(line_start, line_end - line_start) << "\n";
292 std::cerr << " | ";
293 for (int64_t i = 0; i < char_number - 1; i++) {
294 std::cerr << " ";
295 }
296 std::cerr << "^\n";
297 }
298
299 /* Return 1 one error. */
300 int init_dependencies(const GPUSourceDictionnary &dict,
301 const GPUFunctionDictionnary &g_functions)
302 {
303 if (this->dependencies_init) {
304 return 0;
305 }
306 this->dependencies_init = true;
307
308 using namespace shader;
309 /* Auto dependency injection for debug capabilities. */
310 if ((builtins & BuiltinBits::USE_PRINTF) == BuiltinBits::USE_PRINTF) {
311 dependencies.append_non_duplicates(dict.lookup("gpu_shader_print_lib.glsl"));
312 }
313 if ((builtins & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) {
314 dependencies.append_non_duplicates(dict.lookup("draw_debug_draw_lib.glsl"));
315 }
316
317 for (auto dependency_name : dependencies_names) {
318 GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr);
319 if (dependency_source == nullptr) {
320 std::string error = std::string("Dependency not found : ") + dependency_name;
321 print_error(source, 0, error.c_str());
322 return 1;
323 }
324
325 /* Recursive. */
326 int result = dependency_source->init_dependencies(dict, g_functions);
327 if (result != 0) {
328 return 1;
329 }
330
331 for (auto *dep : dependency_source->dependencies) {
332 dependencies.append_non_duplicates(dep);
333 }
334 dependencies.append_non_duplicates(dependency_source);
335 }
336 dependencies_names.clear();
337 return 0;
338 }
339
340 /* Returns the final string with all includes done. */
342 {
343 for (auto *dep : dependencies) {
344 result.append(dep->source);
345 }
346 result.append(source);
347 }
348
349 shader::BuiltinBits builtins_get() const
350 {
351 shader::BuiltinBits out_builtins = builtins;
352 for (auto *dep : dependencies) {
353 out_builtins |= dep->builtins;
354 }
355 return out_builtins;
356 }
357
358 bool is_from_material_library() const
359 {
360 return (filename.startswith("gpu_shader_material_") ||
361 filename.startswith("gpu_shader_common_") ||
362 filename.startswith("gpu_shader_compositor_")) &&
363 filename.endswith(".glsl");
364 }
365};
366
367namespace shader {
368
369#include "glsl_compositor_metadata_list.hh"
370#include "glsl_draw_metadata_list.hh"
371#include "glsl_gpu_metadata_list.hh"
372#include "glsl_ocio_metadata_list.hh"
373#ifdef WITH_OPENSUBDIV
374# include "glsl_osd_metadata_list.hh"
375#endif
376
377} // namespace shader
378
379} // namespace blender::gpu
380
381using namespace blender::gpu;
382
383static GPUPrintFormatMap *g_formats = nullptr;
384static GPUSourceDictionnary *g_sources = nullptr;
385static GPUFunctionDictionnary *g_functions = nullptr;
386static bool force_printf_injection = false;
387
389{
390 g_formats = new GPUPrintFormatMap();
391 g_sources = new GPUSourceDictionnary();
392 g_functions = new GPUFunctionDictionnary();
393
394#define SHADER_SOURCE(filename_underscore, filename, filepath) \
395 g_sources->add_new(filename, \
396 new GPUSource(filepath, \
397 filename, \
398 datatoc_##filename_underscore, \
399 g_functions, \
400 g_formats, \
401 blender::gpu::shader::metadata_##filename_underscore));
402
403#include "glsl_compositor_source_list.h"
404#include "glsl_draw_source_list.h"
405#include "glsl_gpu_source_list.h"
406#include "glsl_ocio_source_list.h"
407#ifdef WITH_OPENSUBDIV
408# include "glsl_osd_source_list.h"
409#endif
410#undef SHADER_SOURCE
411#ifdef WITH_OPENSUBDIV
413 g_sources->add_new(
414 "osd_patch_basis.glsl",
415 new GPUSource("osd_patch_basis.glsl",
416 "osd_patch_basis.glsl",
417 patch_basis_source.c_str(),
418 g_functions,
419 g_formats,
420 [](GPUSource &, GPUFunctionDictionnary *, GPUPrintFormatMap *) {}));
421#endif
422
423 int errors = 0;
424 for (auto *value : g_sources->values()) {
425 errors += value->init_dependencies(*g_sources, *g_functions);
426 }
427 BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting");
428 UNUSED_VARS_NDEBUG(errors);
429
430#if GPU_SHADER_PRINTF_ENABLE
431 if (!g_formats->is_empty()) {
432 /* Detect if there is any printf in node lib files.
433 * See gpu_shader_dependency_force_gpu_print_injection(). */
434 for (auto *value : g_sources->values()) {
435 if (bool(value->builtins & shader::BuiltinBits::USE_PRINTF)) {
436 if (value->filename.startswith("gpu_shader_material_")) {
437 force_printf_injection = true;
438 break;
439 }
440 }
441 }
442 }
443#endif
444
446 for (auto *value : g_sources->values()) {
447 value->patched_source = value->source;
448 value->source = value->patched_source.c_str();
449 size_t start_pos = 0;
450 while ((start_pos = value->patched_source.find("#line ", start_pos)) != std::string::npos) {
451 value->patched_source[start_pos] = '/';
452 value->patched_source[start_pos + 1] = '/';
453 }
454 }
455 }
456}
457
459{
460 for (auto *value : g_sources->values()) {
461 delete value;
462 }
463 for (auto *value : g_functions->values()) {
464 MEM_delete(value);
465 }
466 delete g_formats;
467 delete g_sources;
468 delete g_functions;
469 g_formats = nullptr;
470 g_sources = nullptr;
471 g_functions = nullptr;
472}
473
474GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char *name)
475{
476 GPUFunction *function = g_functions->lookup_default(name, nullptr);
477 BLI_assert_msg(function != nullptr, "Requested function not in the function library");
478 GPUSource *source = reinterpret_cast<GPUSource *>(function->source);
479 BLI_gset_add(used_libraries, const_cast<char *>(source->filename.c_str()));
480 return function;
481}
482
483namespace blender::gpu::shader {
484
486{
487 /* WORKAROUND: We cannot know what shader will require printing if the printf is inside shader
488 * node code. In this case, we just force injection inside all shaders. */
489 return force_printf_injection;
490}
491
493{
494 return (g_formats != nullptr) && !g_formats->is_empty();
495}
496
498{
499 return g_formats->lookup(format_hash);
500}
501
503{
504 if (shader_source_name.is_empty()) {
506 }
507 if (g_sources->contains(shader_source_name) == false) {
508 std::cerr << "Error: Could not find \"" << shader_source_name
509 << "\" in the list of registered source.\n";
510 BLI_assert(0);
512 }
513 GPUSource *source = g_sources->lookup(shader_source_name);
514 return source->builtins_get();
515}
516
518 const StringRefNull shader_source_name)
519{
521 GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr);
522 if (src == nullptr) {
523 std::cerr << "Error source not found : " << shader_source_name << std::endl;
524 }
525 src->build(result);
526 return result;
527}
528
530{
531 GPUSource *src = g_sources->lookup_default(shader_source_name, nullptr);
532 if (src == nullptr) {
533 std::cerr << "Error source not found : " << shader_source_name << std::endl;
534 }
535 return src->source;
536}
537
539{
540 for (auto &source : g_sources->values()) {
541 if (source->source == source_string) {
542 return source->filename;
543 }
544 }
545 return "";
546}
547
548} // namespace blender::gpu::shader
#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
struct GSet GSet
Definition BLI_ghash.h:337
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
void BLI_kdtree_nd_ insert(KDTree *tree, int index, const float co[KD_DIMS]) ATTR_NONNULL(1
#define ARRAY_SIZE(arr)
#define UNUSED_VARS_NDEBUG(...)
float[3] Vector
eGPUType
@ GPU_VEC2
@ GPU_MAT4
@ GPU_TEX1D_ARRAY
@ GPU_TEX2D_ARRAY
@ GPU_TEX2D
@ GPU_VEC4
@ GPU_NONE
@ GPU_CLOSURE
@ GPU_VEC3
@ GPU_MAT3
@ GPU_TEX3D
@ GPU_FLOAT
void build(btStridingMeshInterface *triangles, bool useQuantizedAabbCompression, const btVector3 &bvhAabbMin, const btVector3 &bvhAabbMax)
constexpr int64_t find(char c, int64_t pos=0) const
constexpr const char * begin() const
constexpr const char * end() const
constexpr bool is_empty() const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr const char * c_str() const
FILE * file
const char * openSubdiv_getGLSLPatchBasisSource()
StringRefNull name
#define input
unsigned int uint32_t
GPUFunction * gpu_material_library_use_function(GSet *used_libraries, const char *name)
@ FUNCTION_QUAL_IN
@ FUNCTION_QUAL_OUT
@ FUNCTION_QUAL_INOUT
void gpu_shader_dependency_init()
void gpu_shader_dependency_exit()
format
static void print_error(const char *message, va_list str_format_args)
static void error(const char *str)
Vector< StringRefNull > gpu_shader_dependency_get_resolved_source(StringRefNull source_name)
const PrintfFormat & gpu_shader_dependency_get_printf_format(uint32_t format_hash)
StringRefNull gpu_shader_dependency_get_source(StringRefNull source_name)
BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull source_name)
bool gpu_shader_dependency_has_printf()
bool gpu_shader_dependency_force_gpu_print_injection()
StringRefNull gpu_shader_dependency_get_filename_from_source_string(StringRef source_string)
Find the name of the file from which the given string was generated.
GPUCapabilities GCaps
__int64 int64_t
Definition stdint.h:89
eGPUType paramtype[MAX_PARAMETER]
char name[MAX_FUNCTION_NAME]
GPUFunctionQual paramqual[MAX_PARAMETER]