diff options
Diffstat (limited to 'source/blender/gpu/intern/gpu_shader_dependency.cc')
-rw-r--r-- | source/blender/gpu/intern/gpu_shader_dependency.cc | 325 |
1 files changed, 278 insertions, 47 deletions
diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 2c6845334d3..7ee64d7e80f 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -10,11 +10,14 @@ #include <iomanip> #include <iostream> +#include <sstream> +#include "BLI_ghash.h" #include "BLI_map.hh" #include "BLI_set.hh" #include "BLI_string_ref.hh" +#include "gpu_material_library.h" #include "gpu_shader_create_info.hh" #include "gpu_shader_dependency_private.h" @@ -31,6 +34,7 @@ extern "C" { namespace blender::gpu { using GPUSourceDictionnary = Map<StringRef, struct GPUSource *>; +using GPUFunctionDictionnary = Map<StringRef, struct GPUFunction *>; struct GPUSource { StringRefNull fullpath; @@ -41,7 +45,10 @@ struct GPUSource { shader::BuiltinBits builtins = (shader::BuiltinBits)0; std::string processed_source; - GPUSource(const char *path, const char *file, const char *datatoc) + GPUSource(const char *path, + const char *file, + const char *datatoc, + GPUFunctionDictionnary *g_functions) : fullpath(path), filename(file), source(datatoc) { /* Scan for builtins. */ @@ -92,16 +99,20 @@ struct GPUSource { if (filename.endswith(".h") || filename.endswith(".hh")) { enum_preprocess(); } + + if (is_from_material_library()) { + material_functions_parse(g_functions); + } }; - bool is_in_comment(const StringRef &input, int64_t offset) + static bool is_in_comment(const StringRef &input, int64_t offset) { return (input.rfind("/*", offset) > input.rfind("*/", offset)) || (input.rfind("//", offset) > input.rfind("\n", offset)); } template<bool check_whole_word = true, bool reversed = false, typename T> - int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0) + static int64_t find_str(const StringRef &input, const T keyword, int64_t offset = 0) { while (true) { if constexpr (reversed) { @@ -114,7 +125,7 @@ struct GPUSource { if constexpr (check_whole_word) { /* Fix false positive if something has "enum" as suffix. */ char previous_char = input[offset - 1]; - if (!(ELEM(previous_char, '\n', '\t', ' ', ':'))) { + if (!(ELEM(previous_char, '\n', '\t', ' ', ':', '(', ','))) { offset += (reversed) ? -1 : 1; continue; } @@ -129,9 +140,13 @@ struct GPUSource { } } +#define find_keyword find_str<true, false> +#define rfind_keyword find_str<true, true> +#define find_token find_str<false, false> +#define rfind_token find_str<false, true> + void print_error(const StringRef &input, int64_t offset, const StringRef message) { - std::cout << " error: " << message << "\n"; StringRef sub = input.substr(0, offset); int64_t line_number = std::count(sub.begin(), sub.end(), '\n') + 1; int64_t line_end = input.find("\n", offset); @@ -152,6 +167,12 @@ struct GPUSource { std::cout << "^\n"; } +#define CHECK(test_value, str, ofs, msg) \ + if ((test_value) == -1) { \ + print_error(str, ofs, msg); \ + continue; \ + } + /** * Transform C,C++ enum declaration into GLSL compatible defines and constants: * @@ -193,16 +214,6 @@ struct GPUSource { int64_t last_pos = 0; const bool is_cpp = filename.endswith(".hh"); -#define find_keyword find_str<true, false> -#define find_token find_str<false, false> -#define rfind_token find_str<false, true> -#define CHECK(test_value, str, ofs, msg) \ - if ((test_value) == -1) { \ - print_error(str, ofs, msg); \ - cursor++; \ - continue; \ - } - while (true) { cursor = find_keyword(input, "enum ", cursor + 1); if (cursor == -1) { @@ -268,10 +279,6 @@ struct GPUSource { return; } -#undef find_keyword -#undef find_token -#undef rfind_token - if (last_pos != 0) { output += input.substr(last_pos); } @@ -279,37 +286,241 @@ struct GPUSource { source = processed_source.c_str(); }; + void material_functions_parse(GPUFunctionDictionnary *g_functions) + { + const StringRefNull input = source; + + const char whitespace_chars[] = " \n\t"; + + auto function_parse = [&](const StringRef &input, + int64_t &cursor, + StringRef &out_return_type, + StringRef &out_name, + StringRef &out_args) -> bool { + cursor = find_keyword(input, "void ", cursor + 1); + if (cursor == -1) { + return false; + } + int64_t arg_start = find_token(input, '(', cursor); + if (arg_start == -1) { + return false; + } + int64_t arg_end = find_token(input, ')', arg_start); + if (arg_end == -1) { + return false; + } + int64_t body_start = find_token(input, '{', arg_end); + int64_t next_semicolon = find_token(input, ';', arg_end); + if (body_start != -1 && next_semicolon != -1 && body_start > next_semicolon) { + /* Assert no prototypes but could also just skip them. */ + BLI_assert_msg(false, "No prototypes allowed in node GLSL libraries."); + } + int64_t name_start = input.find_first_not_of(whitespace_chars, input.find(' ', cursor)); + if (name_start == -1) { + return false; + } + int64_t name_end = input.find_last_not_of(whitespace_chars, arg_start); + if (name_end == -1) { + return false; + } + /* Only support void type for now. */ + out_return_type = "void"; + out_name = input.substr(name_start, name_end - name_start); + out_args = input.substr(arg_start + 1, arg_end - (arg_start + 1)); + return true; + }; + + auto keyword_parse = [&](const StringRef &str, int64_t &cursor) -> const StringRef { + int64_t keyword_start = str.find_first_not_of(whitespace_chars, cursor); + if (keyword_start == -1) { + /* No keyword found. */ + return str.substr(0, 0); + } + int64_t keyword_end = str.find_first_of(whitespace_chars, keyword_start); + if (keyword_end == -1) { + /* Last keyword. */ + keyword_end = str.size(); + } + cursor = keyword_end + 1; + return str.substr(keyword_start, keyword_end - keyword_start); + }; + + auto arg_parse = [&](const StringRef &str, + int64_t &cursor, + StringRef &out_qualifier, + StringRef &out_type, + StringRef &out_name) -> bool { + int64_t arg_start = cursor + 1; + if (arg_start >= str.size()) { + return false; + } + cursor = find_token(str, ',', arg_start); + if (cursor == -1) { + /* Last argument. */ + cursor = str.size(); + } + const StringRef arg = str.substr(arg_start, cursor - arg_start); + + int64_t keyword_cursor = 0; + out_qualifier = keyword_parse(arg, keyword_cursor); + out_type = keyword_parse(arg, keyword_cursor); + out_name = keyword_parse(arg, keyword_cursor); + if (out_name.is_empty()) { + /* No qualifier case. */ + out_name = out_type; + out_type = out_qualifier; + out_qualifier = arg.substr(0, 0); + } + return true; + }; + + int64_t cursor = -1; + StringRef func_return_type, func_name, func_args; + while (function_parse(input, cursor, func_return_type, func_name, func_args)) { + GPUFunction *func = MEM_new<GPUFunction>(__func__); + func_name.copy(func->name, sizeof(func->name)); + func->source = reinterpret_cast<void *>(this); + + bool insert = g_functions->add(func->name, func); + + /* NOTE: We allow overloading non void function, but only if the function comes from the + * same file. Otherwise the dependency system breaks. */ + if (!insert) { + GPUSource *other_source = reinterpret_cast<GPUSource *>( + g_functions->lookup(func_name)->source); + if (other_source != this) { + print_error(input, + source.find(func_name), + "Function redefinition or overload in two different files ..."); + print_error( + input, other_source->source.find(func_name), "... previous definition was here"); + } + else { + /* Non-void function overload. */ + MEM_delete(func); + } + continue; + } + + if (func_return_type != "void") { + continue; + } + + func->totparam = 0; + int64_t args_cursor = -1; + StringRef arg_qualifier, arg_type, arg_name; + while (arg_parse(func_args, args_cursor, arg_qualifier, arg_type, arg_name)) { + + if (func->totparam >= ARRAY_SIZE(func->paramtype)) { + print_error(input, source.find(func_name), "Too much parameter in function"); + break; + } + + auto parse_qualifier = [](StringRef &qualifier) -> GPUFunctionQual { + if (qualifier == "out") { + return FUNCTION_QUAL_OUT; + } + else if (qualifier == "inout") { + return FUNCTION_QUAL_INOUT; + } + else { + return FUNCTION_QUAL_IN; + } + }; + + auto parse_type = [](StringRef &type) -> eGPUType { + if (type == "float") { + return GPU_FLOAT; + } + else if (type == "vec2") { + return GPU_VEC2; + } + else if (type == "vec3") { + return GPU_VEC3; + } + else if (type == "vec4") { + return GPU_VEC4; + } + else if (type == "mat3") { + return GPU_MAT3; + } + else if (type == "mat4") { + return GPU_MAT4; + } + else if (type == "sampler1DArray") { + return GPU_TEX1D_ARRAY; + } + else if (type == "sampler2DArray") { + return GPU_TEX2D_ARRAY; + } + else if (type == "sampler2D") { + return GPU_TEX2D; + } + else if (type == "sampler3D") { + return GPU_TEX3D; + } + else if (type == "Closure") { + return GPU_CLOSURE; + } + else { + return GPU_NONE; + } + }; + + func->paramqual[func->totparam] = parse_qualifier(arg_qualifier); + func->paramtype[func->totparam] = parse_type(arg_type); + + if (func->paramtype[func->totparam] == GPU_NONE) { + std::string err = "Unknown parameter type \"" + arg_type + "\""; + int64_t err_ofs = source.find(func_name); + err_ofs = find_keyword(source, arg_name, err_ofs); + err_ofs = rfind_keyword(source, arg_type, err_ofs); + print_error(input, err_ofs, err); + } + + func->totparam++; + } + } + } + +#undef find_keyword +#undef rfind_keyword +#undef find_token +#undef rfind_token + /* Return 1 one error. */ - int init_dependencies(const GPUSourceDictionnary &dict) + int init_dependencies(const GPUSourceDictionnary &dict, + const GPUFunctionDictionnary &g_functions) { - if (dependencies_init) { + if (this->dependencies_init) { return 0; } - dependencies_init = true; - int64_t pos = 0; + this->dependencies_init = true; + int64_t pos = -1; + while (true) { - pos = source.find("pragma BLENDER_REQUIRE(", pos); - if (pos == -1) { - return 0; - } - int64_t start = source.find('(', pos) + 1; - int64_t end = source.find(')', pos); - if (end == -1) { - /* TODO Use clog. */ - std::cout << "Error: " << filename << " : Malformed BLENDER_REQUIRE: Missing \")\"." - << std::endl; - return 1; - } - StringRef dependency_name = source.substr(start, end - start); - GPUSource *dependency_source = dict.lookup_default(dependency_name, nullptr); - if (dependency_source == nullptr) { - /* TODO Use clog. */ - std::cout << "Error: " << filename << " : Dependency not found \"" << dependency_name - << "\"." << std::endl; - return 1; + GPUSource *dependency_source = nullptr; + + { + pos = source.find("pragma BLENDER_REQUIRE(", pos + 1); + if (pos == -1) { + return 0; + } + int64_t start = source.find('(', pos) + 1; + int64_t end = source.find(')', pos); + if (end == -1) { + print_error(source, start, "Malformed BLENDER_REQUIRE: Missing \")\" token"); + return 1; + } + StringRef dependency_name = source.substr(start, end - start); + dependency_source = dict.lookup_default(dependency_name, nullptr); + if (dependency_source == nullptr) { + print_error(source, start, "Dependency not found"); + return 1; + } } /* Recursive. */ - int result = dependency_source->init_dependencies(dict); + int result = dependency_source->init_dependencies(dict, g_functions); if (result != 0) { return 1; } @@ -318,8 +529,8 @@ struct GPUSource { dependencies.append_non_duplicates(dep); } dependencies.append_non_duplicates(dependency_source); - pos++; - }; + } + return 0; } /* Returns the final string with all includes done. */ @@ -339,6 +550,11 @@ struct GPUSource { } return out_builtins; } + + bool is_from_material_library() const + { + return filename.startswith("gpu_shader_material_") && filename.endswith(".glsl"); + } }; } // namespace blender::gpu @@ -346,13 +562,15 @@ struct GPUSource { using namespace blender::gpu; static GPUSourceDictionnary *g_sources = nullptr; +static GPUFunctionDictionnary *g_functions = nullptr; void gpu_shader_dependency_init() { g_sources = new GPUSourceDictionnary(); + g_functions = new GPUFunctionDictionnary(); #define SHADER_SOURCE(datatoc, filename, filepath) \ - g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc)); + g_sources->add_new(filename, new GPUSource(filepath, filename, datatoc, g_functions)); #include "glsl_draw_source_list.h" #include "glsl_gpu_source_list.h" #ifdef WITH_OCIO @@ -362,7 +580,7 @@ void gpu_shader_dependency_init() int errors = 0; for (auto *value : g_sources->values()) { - errors += value->init_dependencies(*g_sources); + errors += value->init_dependencies(*g_sources, *g_functions); } BLI_assert_msg(errors == 0, "Dependency errors detected: Aborting"); UNUSED_VARS_NDEBUG(errors); @@ -373,7 +591,20 @@ void gpu_shader_dependency_exit() for (auto *value : g_sources->values()) { delete value; } + for (auto *value : g_functions->values()) { + MEM_delete(value); + } delete g_sources; + delete g_functions; +} + +GPUFunction *gpu_material_library_use_function(GSet *used_libraries, const char *name) +{ + GPUFunction *function = g_functions->lookup_default(name, nullptr); + BLI_assert_msg(function != nullptr, "Requested function not in the function library"); + GPUSource *source = reinterpret_cast<GPUSource *>(function->source); + BLI_gset_add(used_libraries, const_cast<char *>(source->filename.c_str())); + return function; } namespace blender::gpu::shader { |