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
11
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
52
53struct GPUSource {
57 std::string patched_source;
60 bool dependencies_init = false;
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
115
117 {
118 using namespace blender::gpu::shader;
119 switch (qualifier) {
121 return FUNCTION_QUAL_IN;
123 return FUNCTION_QUAL_OUT;
125 return FUNCTION_QUAL_INOUT;
126 }
128 return FUNCTION_QUAL_IN;
129 }
130
132 {
133 using namespace blender::gpu::shader;
134 switch (type) {
136 return GPU_FLOAT;
138 return GPU_VEC2;
140 return GPU_VEC3;
142 return GPU_VEC4;
144 return GPU_MAT3;
146 return GPU_MAT4;
148 return GPU_TEX1D_ARRAY;
150 return GPU_TEX2D_ARRAY;
152 return GPU_TEX2D;
154 return GPU_TEX3D;
156 return GPU_CLOSURE;
157 }
159 return GPU_NONE;
160 }
161
163 const char *path,
164 const char *file,
165 const char *datatoc,
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
175 {
176 builtins |= convert_builtin_bit(builtin);
177 }
178
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
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':
220 break;
221 case 'd':
223 break;
224 case 'f':
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
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. */
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. */
311 dependencies.append_non_duplicates(dict.lookup("gpu_shader_print_lib.glsl"));
312 }
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
350 {
351 shader::BuiltinBits out_builtins = builtins;
352 for (auto *dep : dependencies) {
353 out_builtins |= dep->builtins;
354 }
355 return out_builtins;
356 }
357
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;
386static bool force_printf_injection = false;
387
389{
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(),
419 g_formats,
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_")) {
438 break;
439 }
440 }
441 }
442 }
443#endif
444
445 if (GCaps.line_directive_workaround) {
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. */
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(...)
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
long long int int64_t
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool contains(const Key &key) const
Definition BLI_map.hh:353
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
void copy_utf8_truncated(char *dst, int64_t dst_size) const
Definition string_ref.cc:28
constexpr const char * c_str() const
const char * openSubdiv_getGLSLPatchBasisSource()
#define input
@ FUNCTION_QUAL_IN
@ FUNCTION_QUAL_OUT
@ FUNCTION_QUAL_INOUT
GPUFunction * gpu_material_library_use_function(GSet *used_libraries, const char *name)
static bool force_printf_injection
void gpu_shader_dependency_init()
void gpu_shader_dependency_exit()
static GPUFunctionDictionnary * g_functions
static GPUPrintFormatMap * g_formats
static GPUSourceDictionnary * g_sources
format
static void error(const char *str)
const PrintfFormat & gpu_shader_dependency_get_printf_format(uint32_t format_hash)
Vector< StringRefNull > gpu_shader_dependency_get_resolved_source(const StringRefNull shader_source_name)
BuiltinBits gpu_shader_dependency_get_builtins(const StringRefNull shader_source_name)
bool gpu_shader_dependency_force_gpu_print_injection()
StringRefNull gpu_shader_dependency_get_filename_from_source_string(const StringRef source_string)
Find the name of the file from which the given string was generated.
StringRefNull gpu_shader_dependency_get_source(const StringRefNull shader_source_name)
Map< StringRef, GPUFunction * > GPUFunctionDictionnary
GPUCapabilities GCaps
Map< uint32_t, shader::PrintfFormat > GPUPrintFormatMap
Map< StringRef, struct GPUSource * > GPUSourceDictionnary
eGPUType paramtype[MAX_PARAMETER]
char name[MAX_FUNCTION_NAME]
GPUFunctionQual paramqual[MAX_PARAMETER]
Vector< GPUSource * > dependencies
int init_dependencies(const GPUSourceDictionnary &dict, const GPUFunctionDictionnary &g_functions)
eGPUType convert_type(shader::metadata::Type type)
void build(Vector< StringRefNull > &result) const
shader::BuiltinBits builtins_get() const
shader::BuiltinBits convert_builtin_bit(shader::metadata::Builtin builtin)
void add_dependency(StringRef line)
GPUSource(const char *path, const char *file, const char *datatoc, GPUFunctionDictionnary *g_functions, GPUPrintFormatMap *g_formats, std::function< void(GPUSource &, GPUFunctionDictionnary *, GPUPrintFormatMap *)> metadata_fn)
Vector< StringRef > dependencies_names
GPUFunctionQual convert_qualifier(shader::metadata::Qualifier qualifier)
void add_builtin(shader::metadata::Builtin builtin)
void add_printf_format(uint32_t format_hash, std::string format, GPUPrintFormatMap *format_map)
void print_error(const StringRef &input, int64_t offset, const StringRef message)
void add_function(StringRefNull name, Span< shader::metadata::ArgumentFormat > arguments, GPUFunctionDictionnary *g_functions)
i
Definition text_draw.cc:230