From 1bffefb346c2974aad692905041ccfb84e666597 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Fri, 22 Sep 2023 13:58:02 +1000 Subject: [PATCH] Adding ability to include build-in include files (precursor to custom shader templates) --- doc/classes/ShaderIncludeDB.xml | 33 +++++ servers/register_server_types.cpp | 2 + .../renderer_rd/renderer_scene_render_rd.cpp | 11 ++ servers/rendering/renderer_rd/shader_rd.cpp | 33 ++++- servers/rendering/renderer_rd/shaders/SCsub | 10 +- servers/rendering/rendering_device.cpp | 7 +- servers/rendering/rendering_device_binds.cpp | 16 ++- servers/rendering/shader_include_db.cpp | 115 ++++++++++++++++++ servers/rendering/shader_include_db.h | 53 ++++++++ 9 files changed, 271 insertions(+), 9 deletions(-) create mode 100644 doc/classes/ShaderIncludeDB.xml create mode 100644 servers/rendering/shader_include_db.cpp create mode 100644 servers/rendering/shader_include_db.h diff --git a/doc/classes/ShaderIncludeDB.xml b/doc/classes/ShaderIncludeDB.xml new file mode 100644 index 00000000000..a431eabf4e8 --- /dev/null +++ b/doc/classes/ShaderIncludeDB.xml @@ -0,0 +1,33 @@ + + + + Internal database of built in shader include files. + + + This object contains shader fragments from Godot's internal shaders. These can be used when access to internal uniform buffers and/or internal functions is required for instance when composing compositor effects or compute shaders. Only fragments for the current rendering device are loaded. + + + + + + + + + Returns the code for the built-in shader fragment. You can also access this in your shader code through [code]#include "filename"[/code]. + + + + + + + Returns [code]true[/code] if an include file with this name exists. + + + + + + Returns a list of built-in include files that are currently registered. + + + + diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 18ee8630838..3e0caeea659 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -70,6 +70,7 @@ #include "rendering/renderer_rd/uniform_set_cache_rd.h" #include "rendering/rendering_device.h" #include "rendering/rendering_device_binds.h" +#include "rendering/shader_include_db.h" #include "rendering/storage/render_data.h" #include "rendering/storage/render_scene_buffers.h" #include "rendering/storage/render_scene_data.h" @@ -210,6 +211,7 @@ void register_server_types() { } GDREGISTER_ABSTRACT_CLASS(RenderingDevice); + GDREGISTER_CLASS(ShaderIncludeDB); GDREGISTER_CLASS(RDTextureFormat); GDREGISTER_CLASS(RDTextureView); GDREGISTER_CLASS(RDAttachmentFormat); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 4417f6832cd..0a7dd3eadcb 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -35,9 +35,13 @@ #include "core/os/os.h" #include "renderer_compositor_rd.h" #include "servers/rendering/renderer_rd/environment/fog.h" +#include "servers/rendering/renderer_rd/shaders/decal_data_inc.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/light_data_inc.glsl.gen.h" +#include "servers/rendering/renderer_rd/shaders/scene_data_inc.glsl.gen.h" #include "servers/rendering/renderer_rd/storage_rd/material_storage.h" #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" #include "servers/rendering/rendering_server_default.h" +#include "servers/rendering/shader_include_db.h" #include "servers/rendering/storage/camera_attributes_storage.h" void get_vogel_disk(float *r_kernel, int p_sample_count) { @@ -1452,6 +1456,13 @@ void RendererSceneRenderRD::init() { /* Forward ID */ forward_id_storage = create_forward_id_storage(); + /* Register the include files we make available by default to our users */ + { + ShaderIncludeDB::register_built_in_include_file("godot/decal_data_inc.glsl", decal_data_inc_shader_glsl); + ShaderIncludeDB::register_built_in_include_file("godot/light_data_inc.glsl", light_data_inc_shader_glsl); + ShaderIncludeDB::register_built_in_include_file("godot/scene_data_inc.glsl", scene_data_inc_shader_glsl); + } + /* SKY SHADER */ sky.init(); diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp index 6234cddee39..fb89c11a1b4 100644 --- a/servers/rendering/renderer_rd/shader_rd.cpp +++ b/servers/rendering/renderer_rd/shader_rd.cpp @@ -37,6 +37,7 @@ #include "core/version.h" #include "renderer_compositor_rd.h" #include "servers/rendering/rendering_device.h" +#include "servers/rendering/shader_include_db.h" #include "thirdparty/misc/smolv.h" #define ENABLE_SHADER_CACHE 1 @@ -46,7 +47,8 @@ void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) { String text; - for (int i = 0; i < lines.size(); i++) { + int line_count = lines.size(); + for (int i = 0; i < line_count; i++) { const String &l = lines[i]; bool push_chunk = false; @@ -78,6 +80,35 @@ void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) { chunk.type = StageTemplate::Chunk::TYPE_CODE; push_chunk = true; chunk.code = l.replace_first("#CODE", String()).replace(":", "").strip_edges().to_upper(); + } else if (l.begins_with("#include ")) { + String include_file = l.replace("#include ", "").strip_edges(); + if (include_file[0] == '"') { + int end_pos = include_file.find_char('"', 1); + if (end_pos >= 0) { + include_file = include_file.substr(1, end_pos - 1); + + String include_code = ShaderIncludeDB::get_built_in_include_file(include_file); + if (!include_code.is_empty()) { + // Add these lines into our parse list so we parse them as well. + Vector include_lines = include_code.split("\n"); + + for (int j = include_lines.size() - 1; j >= 0; j--) { + lines.insert(i + 1, include_lines[j]); + } + + line_count = lines.size(); + } else { + // Add it in as is. + text += l + "\n"; + } + } else { + // Add it in as is. + text += l + "\n"; + } + } else { + // Add it in as is. + text += l + "\n"; + } } else { text += l + "\n"; } diff --git a/servers/rendering/renderer_rd/shaders/SCsub b/servers/rendering/renderer_rd/shaders/SCsub index e102b839b59..f1b6710383d 100644 --- a/servers/rendering/renderer_rd/shaders/SCsub +++ b/servers/rendering/renderer_rd/shaders/SCsub @@ -4,16 +4,20 @@ from misc.utility.scons_hints import * Import("env") if "RD_GLSL" in env["BUILDERS"]: - # find all include files + # find just the include files gl_include_files = [str(f) for f in Glob("*_inc.glsl")] - # find all shader code(all glsl files excluding our include files) + # find all shader code (all glsl files excluding our include files) glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files] # make sure we recompile shaders if include files change env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#glsl_builders.py"]) - # compile shaders + # compile include files + for glsl_file in gl_include_files: + env.GLSL_HEADER(glsl_file) + + # compile RD shader for glsl_file in glsl_files: env.RD_GLSL(glsl_file) diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index e9fa38475e2..6cf2f6db7e4 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -32,6 +32,7 @@ #include "rendering_device.compat.inc" #include "rendering_device_binds.h" +#include "shader_include_db.h" #include "core/config/project_settings.h" #include "core/io/dir_access.h" @@ -189,6 +190,10 @@ void RenderingDevice::_free_dependencies(RID p_id) { } } +/*******************************/ +/**** SHADER INFRASTRUCTURE ****/ +/*******************************/ + void RenderingDevice::shader_set_compile_to_spirv_function(ShaderCompileToSPIRVFunction p_function) { compile_to_spirv_function = p_function; } @@ -211,7 +216,7 @@ Vector RenderingDevice::shader_compile_spirv_from_source(ShaderStage p_ ERR_FAIL_NULL_V(compile_to_spirv_function, Vector()); - return compile_to_spirv_function(p_stage, p_source_code, p_language, r_error, this); + return compile_to_spirv_function(p_stage, ShaderIncludeDB::parse_include_files(p_source_code), p_language, r_error, this); } String RenderingDevice::shader_get_spirv_cache_key() const { diff --git a/servers/rendering/rendering_device_binds.cpp b/servers/rendering/rendering_device_binds.cpp index e41a56b0a32..9ccf1e41083 100644 --- a/servers/rendering/rendering_device_binds.cpp +++ b/servers/rendering/rendering_device_binds.cpp @@ -30,6 +30,8 @@ #include "rendering_device_binds.h" +#include "shader_include_db.h" + Error RDShaderFile::parse_versions_from_text(const String &p_text, const String p_defines, OpenIncludeFunction p_include_func, void *p_include_func_userdata) { ERR_FAIL_NULL_V_MSG( RenderingDevice::get_singleton(), @@ -144,11 +146,17 @@ Error RDShaderFile::parse_versions_from_text(const String &p_text, const String break; } include = include.substr(1, include.length() - 2).strip_edges(); - String include_text = p_include_func(include, p_include_func_userdata); - if (!include_text.is_empty()) { - stage_code[stage] += "\n" + include_text + "\n"; + + String include_code = ShaderIncludeDB::get_built_in_include_file(include); + if (!include_code.is_empty()) { + stage_code[stage] += "\n" + include_code + "\n"; } else { - base_error = "#include failed for file '" + include + "'"; + String include_text = p_include_func(include, p_include_func_userdata); + if (!include_text.is_empty()) { + stage_code[stage] += "\n" + include_text + "\n"; + } else { + base_error = "#include failed for file '" + include + "'."; + } } } else { base_error = "#include used, but no include function provided."; diff --git a/servers/rendering/shader_include_db.cpp b/servers/rendering/shader_include_db.cpp new file mode 100644 index 00000000000..bc62c1fe029 --- /dev/null +++ b/servers/rendering/shader_include_db.cpp @@ -0,0 +1,115 @@ +/**************************************************************************/ +/* shader_include_db.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "shader_include_db.h" + +HashMap ShaderIncludeDB::built_in_includes; + +void ShaderIncludeDB::_bind_methods() { + ClassDB::bind_static_method("ShaderIncludeDB", D_METHOD("list_built_in_include_files"), &ShaderIncludeDB::list_built_in_include_files); + ClassDB::bind_static_method("ShaderIncludeDB", D_METHOD("has_built_in_include_file", "filename"), &ShaderIncludeDB::has_built_in_include_file); + ClassDB::bind_static_method("ShaderIncludeDB", D_METHOD("get_built_in_include_file", "filename"), &ShaderIncludeDB::get_built_in_include_file); +} + +void ShaderIncludeDB::register_built_in_include_file(const String &p_filename, const String &p_shader_code) { + built_in_includes[p_filename] = p_shader_code; +} + +PackedStringArray ShaderIncludeDB::list_built_in_include_files() { + PackedStringArray ret; + + for (const KeyValue &e : built_in_includes) { + ret.push_back(e.key); + } + + return ret; +} + +bool ShaderIncludeDB::has_built_in_include_file(const String &p_filename) { + return built_in_includes.has(p_filename); +} + +String ShaderIncludeDB::get_built_in_include_file(const String &p_filename) { + const String *ptr = built_in_includes.getptr(p_filename); + + return ptr ? *ptr : String(); +} + +String ShaderIncludeDB::parse_include_files(const String &p_code) { + // Prevent needless processing if we don't have any includes. + if (p_code.find("#include ") == -1) { + return p_code; + } + + const String include = "#include "; + String parsed_code; + + Vector lines = p_code.split("\n"); + int line_count = lines.size(); + for (int i = 0; i < line_count; i++) { + const String &l = lines[i]; + + if (l.begins_with(include)) { + String include_file = l.replace(include, "").strip_edges(); + if (include_file[0] == '"') { + int end_pos = include_file.find_char('"', 1); + if (end_pos >= 0) { + include_file = include_file.substr(1, end_pos - 1); + + String include_code = ShaderIncludeDB::get_built_in_include_file(include_file); + if (!include_code.is_empty()) { + // Add these lines into our parse list so we parse them as well. + Vector include_lines = include_code.split("\n"); + + for (int j = include_lines.size() - 1; j >= 0; j--) { + lines.insert(i + 1, include_lines[j]); + } + + line_count = lines.size(); + } else { + // Just add it back in, this will cause a compile error to alert the user. + parsed_code += l + "\n"; + } + } else { + // Include as is. + parsed_code += l + "\n"; + } + } else { + // Include as is. + parsed_code += l + "\n"; + } + } else { + // Include as is. + parsed_code += l + "\n"; + } + } + + return parsed_code; +} diff --git a/servers/rendering/shader_include_db.h b/servers/rendering/shader_include_db.h new file mode 100644 index 00000000000..d1967a3296d --- /dev/null +++ b/servers/rendering/shader_include_db.h @@ -0,0 +1,53 @@ +/**************************************************************************/ +/* shader_include_db.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef SHADER_INCLUDE_DB_H +#define SHADER_INCLUDE_DB_H + +#include "core/object/class_db.h" + +class ShaderIncludeDB : public Object { + GDCLASS(ShaderIncludeDB, Object) + +private: + static HashMap built_in_includes; + +protected: + static void _bind_methods(); + +public: + static void register_built_in_include_file(const String &p_filename, const String &p_shader_code); + static PackedStringArray list_built_in_include_files(); + static bool has_built_in_include_file(const String &p_filename); + static String get_built_in_include_file(const String &p_filename); + static String parse_include_files(const String &p_code); +}; + +#endif // SHADER_INCLUDE_DB_H