From 869fc2cf4e95ad714e639d4f8beab2e7c1b9d792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 19 Aug 2025 18:27:49 +0200 Subject: [PATCH] GPU: Shader: Add Shared Variable in shader create info This allows to control the type of data inside the create infos for easy variation of the same shader. Also this wraps the last global shader resource usage for the future SRD. Pull Request: https://projects.blender.org/blender/blender/pulls/144769 --- .../eevee_light_culling_zbin_comp.glsl | 4 - .../shaders/eevee_shadow_page_mask_comp.glsl | 4 - .../shaders/infos/eevee_light_culling_info.hh | 3 + .../eevee/shaders/infos/eevee_shadow_info.hh | 3 + .../gpu/intern/gpu_shader_create_info.hh | 23 ++++ .../blender/gpu/intern/gpu_shader_srd_cpp.hh | 1 + .../blender/gpu/intern/gpu_shader_srd_info.hh | 1 + .../blender/gpu/metal/mtl_shader_generator.hh | 2 +- .../blender/gpu/metal/mtl_shader_generator.mm | 109 ++++++++++++++++-- source/blender/gpu/opengl/gl_shader.cc | 4 + source/blender/gpu/vulkan/vk_shader.cc | 5 + 11 files changed, 143 insertions(+), 16 deletions(-) diff --git a/source/blender/draw/engines/eevee/shaders/eevee_light_culling_zbin_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_light_culling_zbin_comp.glsl index 18dc1e656ea..c54c15bdf8c 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_light_culling_zbin_comp.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_light_culling_zbin_comp.glsl @@ -16,10 +16,6 @@ COMPUTE_SHADER_CREATE_INFO(eevee_light_culling_zbin) #include "eevee_light_iter_lib.glsl" #include "gpu_shader_math_base_lib.glsl" -/* Fits the limit of 32KB. */ -shared uint zbin_max[CULLING_ZBIN_COUNT]; -shared uint zbin_min[CULLING_ZBIN_COUNT]; - void main() { constexpr uint zbin_iter = CULLING_ZBIN_COUNT / gl_WorkGroupSize.x; diff --git a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mask_comp.glsl b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mask_comp.glsl index 9d42896fd00..805431f4822 100644 --- a/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mask_comp.glsl +++ b/source/blender/draw/engines/eevee/shaders/eevee_shadow_page_mask_comp.glsl @@ -21,10 +21,6 @@ COMPUTE_SHADER_CREATE_INFO(eevee_shadow_page_mask) /* Visibility value to write back. */ #define SHADOW_TILE_MASKED SHADOW_IS_ALLOCATED -shared uint tiles_local[SHADOW_TILEDATA_PER_TILEMAP]; -shared uint levels_rendered; -shared uint force_base_page; - int shadow_tile_offset_lds(int2 tile, int lod) { return shadow_tile_offset(uint2(tile), 0, lod); diff --git a/source/blender/draw/engines/eevee/shaders/infos/eevee_light_culling_info.hh b/source/blender/draw/engines/eevee/shaders/infos/eevee_light_culling_info.hh index 23021d6395d..fcfcca95c57 100644 --- a/source/blender/draw/engines/eevee/shaders/infos/eevee_light_culling_info.hh +++ b/source/blender/draw/engines/eevee/shaders/infos/eevee_light_culling_info.hh @@ -55,6 +55,9 @@ DO_STATIC_COMPILATION() ADDITIONAL_INFO(eevee_shared) ADDITIONAL_INFO(draw_view) LOCAL_GROUP_SIZE(CULLING_ZBIN_GROUP_SIZE) +/* Fits the limit of 32KB. */ +GROUP_SHARED(uint, zbin_max[CULLING_ZBIN_COUNT]) +GROUP_SHARED(uint, zbin_min[CULLING_ZBIN_COUNT]) STORAGE_BUF(0, read, LightCullingData, light_cull_buf) STORAGE_BUF(1, read, LightData, light_buf[]) STORAGE_BUF(2, write, uint, out_zbin_buf[]) diff --git a/source/blender/draw/engines/eevee/shaders/infos/eevee_shadow_info.hh b/source/blender/draw/engines/eevee/shaders/infos/eevee_shadow_info.hh index 477a33a00c8..856a1151c2c 100644 --- a/source/blender/draw/engines/eevee/shaders/infos/eevee_shadow_info.hh +++ b/source/blender/draw/engines/eevee/shaders/infos/eevee_shadow_info.hh @@ -149,6 +149,9 @@ GPU_SHADER_CREATE_END() GPU_SHADER_CREATE_INFO(eevee_shadow_page_mask) DO_STATIC_COMPILATION() LOCAL_GROUP_SIZE(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES) +GROUP_SHARED(uint, tiles_local[SHADOW_TILEDATA_PER_TILEMAP]) +GROUP_SHARED(uint, levels_rendered) +GROUP_SHARED(uint, force_base_page) PUSH_CONSTANT(int, max_view_per_tilemap) STORAGE_BUF(0, read_write, ShadowTileMapData, tilemaps_buf[]) STORAGE_BUF(1, read_write, uint, tiles_buf[]) diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 7ff1926f611..13cdde66427 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -182,6 +182,8 @@ #name, \ Frequency::freq) +# define GROUP_SHARED(type, name) .shared_variable(Type::type##_t, #name) + # define BUILTINS(builtin) .builtins(builtin) # define VERTEX_SOURCE(filename) .vertex_source(filename) @@ -279,6 +281,8 @@ # define IMAGE(slot, format, qualifiers, type, name) _##qualifiers type name; # define IMAGE_FREQ(slot, format, qualifiers, type, name, freq) _##qualifiers type name; +# define GROUP_SHARED(type, name) type name; + # define BUILTINS(builtin) # define VERTEX_SOURCE(filename) @@ -814,6 +818,13 @@ struct ShaderCreateInfo { Vector compilation_constants_; Vector specialization_constants_; + struct SharedVariable { + Type type; + StringRefNull name; + }; + + Vector shared_variables_; + struct Sampler { ImageType type; GPUSamplerState sampler; @@ -1150,6 +1161,18 @@ struct ShaderCreateInfo { /** \} */ + /* -------------------------------------------------------------------- */ + /** \name Compute shader Shared variables + * \{ */ + + Self &shared_variable(Type type, StringRefNull name) + { + shared_variables_.append({type, name}); + return *(Self *)this; + } + + /** \} */ + /* -------------------------------------------------------------------- */ /** \name Resources bindings points * \{ */ diff --git a/source/blender/gpu/intern/gpu_shader_srd_cpp.hh b/source/blender/gpu/intern/gpu_shader_srd_cpp.hh index 936d5a5be95..996e7c866cf 100644 --- a/source/blender/gpu/intern/gpu_shader_srd_cpp.hh +++ b/source/blender/gpu/intern/gpu_shader_srd_cpp.hh @@ -40,6 +40,7 @@ #define SRD_RESOURCE_BEGIN(srd) SRD_STRUCT_BEGIN(srd) #define SRD_RESOURCE_END(srd) SRD_STRUCT_END(srd) #define SRD_RESOURCE_SPECIALIZATION_CONSTANT(srd, type, name, default) type name = {}; +#define SRD_RESOURCE_GROUP_SHARED(srd, type, name) type name; #define SRD_RESOURCE_PUSH_CONSTANT(srd, type, name) type name = {}; #define SRD_RESOURCE_PUSH_CONSTANT_ARRAY(srd, type, name, array) type(*name) = {}; #define SRD_RESOURCE_SAMPLER(srd, binding, type, name) type name = {}; diff --git a/source/blender/gpu/intern/gpu_shader_srd_info.hh b/source/blender/gpu/intern/gpu_shader_srd_info.hh index d31bc8e6667..211fc14c945 100644 --- a/source/blender/gpu/intern/gpu_shader_srd_info.hh +++ b/source/blender/gpu/intern/gpu_shader_srd_info.hh @@ -45,6 +45,7 @@ #define SRD_RESOURCE_END(srd) SRD_STRUCT_END(srd) #define SRD_RESOURCE_SPECIALIZATION_CONSTANT(srd, type, name, default) \ info.specialization_constant(Type::type##_t, #name, default); +#define SRD_RESOURCE_GROUP_SHARED(srd, type, name) info.shared_variable(Type::type##_t, name) #define SRD_RESOURCE_PUSH_CONSTANT(srd, type, name) info.push_constant(Type::type##_t, #name); #define SRD_RESOURCE_PUSH_CONSTANT_ARRAY(srd, type, name, array) \ info.push_constant(Type::type##_t, #name, array); diff --git a/source/blender/gpu/metal/mtl_shader_generator.hh b/source/blender/gpu/metal/mtl_shader_generator.hh index ae6ee96a6e9..224a922976f 100644 --- a/source/blender/gpu/metal/mtl_shader_generator.hh +++ b/source/blender/gpu/metal/mtl_shader_generator.hh @@ -430,7 +430,7 @@ class MSLGeneratorInterface { std::string generate_msl_compute_inputs_string(); std::string generate_msl_vertex_entry_stub(); std::string generate_msl_fragment_entry_stub(); - std::string generate_msl_compute_entry_stub(); + std::string generate_msl_compute_entry_stub(const shader::ShaderCreateInfo &info); std::string generate_msl_fragment_tile_input_population(); std::string generate_msl_global_uniform_population(ShaderStage stage); std::string generate_ubo_block_macro_chain(MSLBufferBlock block); diff --git a/source/blender/gpu/metal/mtl_shader_generator.mm b/source/blender/gpu/metal/mtl_shader_generator.mm index 185e797dc41..91da87d6b39 100644 --- a/source/blender/gpu/metal/mtl_shader_generator.mm +++ b/source/blender/gpu/metal/mtl_shader_generator.mm @@ -57,6 +57,19 @@ char *MSLGeneratorInterface::msl_patch_default = nullptr; /** \name Shader Translation utility functions. * \{ */ +static void split_array(StringRefNull input, std::string &r_name, std::string &r_array) +{ + size_t array_start = input.find('['); + if (array_start != std::string::npos) { + r_name = input.substr(0, array_start); + r_array = input.substr(array_start); + } + else { + r_name = input; + r_array = ""; + } +} + static eMTLDataType to_mtl_type(Type type) { switch (type) { @@ -241,6 +254,12 @@ std::string MTLShader::resources_declare(const ShaderCreateInfo &info) const * are generated during class-wrapper construction in `generate_msl_from_glsl`. */ std::stringstream ss; + ss << "\n/* Shared Variables. */\n"; + for (const ShaderCreateInfo::SharedVariable &sv : info.shared_variables_) { + std::string array, name; + split_array(sv.name, name, array); + ss << "threadgroup " << to_string(sv.type) << " (&" << name << ")" << array << ";\n"; + } /* Generate resource stubs for UBOs and textures. */ ss << "\n/* Pass Resources. */\n"; for (const ShaderCreateInfo::Resource &res : info.pass_resources_) { @@ -348,6 +367,54 @@ char *MSLGeneratorInterface::msl_patch_default_get() return msl_patch_default; } +static void shared_variable_args(const shader::ShaderCreateInfo &info, std::stringstream &ss) +{ + bool first = true; + for (const shader::ShaderCreateInfo::SharedVariable &var : info.shared_variables_) { + std::string array, name; + split_array(var.name, name, array); + ss << (first ? ' ' : ',') << "threadgroup " << to_string(var.type) << "(&_" << name << ")" + << array; + first = false; + } +} + +static void shared_variable_assign(const shader::ShaderCreateInfo &info, std::stringstream &ss) +{ + bool first = true; + for (const shader::ShaderCreateInfo::SharedVariable &var : info.shared_variables_) { + std::string array, name; + split_array(var.name, name, array); + ss << (first ? ':' : ',') << name << "(_" << name << ")"; + first = false; + } +} + +static void shared_variable_declare(const shader::ShaderCreateInfo &info, std::stringstream &ss) +{ + for (const shader::ShaderCreateInfo::SharedVariable &var : info.shared_variables_) { + std::string array, name; + split_array(var.name, name, array); + ss << "threadgroup " << to_string(var.type) << ' ' << name << array << ";\n"; + } +} + +static void shared_variable_pass(const shader::ShaderCreateInfo &info, std::stringstream &ss) +{ + bool first = true; + if (info.shared_variables_.is_empty()) { + return; + } + ss << "("; + for (const shader::ShaderCreateInfo::SharedVariable &var : info.shared_variables_) { + std::string array, name; + split_array(var.name, name, array); + ss << (first ? ' ' : ',') << name; + first = false; + } + ss << ")"; +} + /* Specialization constants will evaluate using a dynamic value if provided at PSO compile time. */ static void generate_specialization_constant_declarations(const shader::ShaderCreateInfo *info, std::stringstream &ss) @@ -978,14 +1045,27 @@ bool MTLShader::generate_msl_from_glsl_compute(const shader::ShaderCreateInfo *i /* Compute constructor for Shared memory blocks, as we must pass * local references from entry-point function scope into the class * instantiation. */ - ss_compute << get_stage_class_name(ShaderStage::COMPUTE) - << "(MSL_SHARED_VARS_ARGS) MSL_SHARED_VARS_ASSIGN {}\n"; + ss_compute << get_stage_class_name(ShaderStage::COMPUTE) << "( "; + if (!info->shared_variables_.is_empty()) { + shared_variable_args(*info, ss_compute); + } + else { + ss_compute << "MSL_SHARED_VARS_ARGS"; + } + ss_compute << ")"; + if (!info->shared_variables_.is_empty()) { + shared_variable_assign(*info, ss_compute); + } + else { + ss_compute << " MSL_SHARED_VARS_ASSIGN "; + } + ss_compute << "{}\n"; /* Class Closing Bracket to end shader global scope. */ ss_compute << "};" << std::endl; /* Generate Vertex shader entry-point function containing resource bindings. */ - ss_compute << msl_iface.generate_msl_compute_entry_stub(); + ss_compute << msl_iface.generate_msl_compute_entry_stub(*info); #ifndef NDEBUG /* In debug mode, we inject the name of the shader into the entry-point function @@ -1561,7 +1641,8 @@ std::string MSLGeneratorInterface::generate_msl_fragment_entry_stub() return out.str(); } -std::string MSLGeneratorInterface::generate_msl_compute_entry_stub() +std::string MSLGeneratorInterface::generate_msl_compute_entry_stub( + const shader::ShaderCreateInfo &info) { static const char *shader_stage_inst_name = get_shader_stage_instance_name(ShaderStage::COMPUTE); std::stringstream out; @@ -1587,9 +1668,23 @@ std::string MSLGeneratorInterface::generate_msl_compute_entry_stub() out << this->generate_msl_compute_inputs_string(); out << ") {" << std::endl << std::endl; - out << "MSL_SHARED_VARS_DECLARE\n"; - out << "\t" << get_stage_class_name(ShaderStage::COMPUTE) << " " << shader_stage_inst_name - << " MSL_SHARED_VARS_PASS;\n"; + if (!info.shared_variables_.is_empty()) { + shared_variable_declare(info, out); + } + else { + out << "MSL_SHARED_VARS_DECLARE\n"; + } + + out << "\t" << get_stage_class_name(ShaderStage::COMPUTE) << " " << shader_stage_inst_name; + /* Shared vars should be either all be declared in shader (MSL_SHARED_VARS_* path) or all in + * create infos (shared_variable_* path). */ + if (!info.shared_variables_.is_empty()) { + shared_variable_pass(info, out); + } + else { + out << " MSL_SHARED_VARS_PASS "; + } + out << ";\n"; /* Copy global variables. */ /* Entry point parameters for gl Globals. */ diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 8b5cf5ceeaa..a5b1cb266f6 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -617,6 +617,10 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const break; } } + ss << "\n/* Shared Variables. */\n"; + for (const ShaderCreateInfo::SharedVariable &sv : info.shared_variables_) { + ss << "shared " << to_string(sv.type) << " " << sv.name << ";\n"; + } /* NOTE: We define macros in GLSL to trigger compilation error if the resource names * are reused for local variables. This is to match other backend behavior which needs accessors * macros. */ diff --git a/source/blender/gpu/vulkan/vk_shader.cc b/source/blender/gpu/vulkan/vk_shader.cc index 33c33b818a0..f888acc43a5 100644 --- a/source/blender/gpu/vulkan/vk_shader.cc +++ b/source/blender/gpu/vulkan/vk_shader.cc @@ -806,6 +806,11 @@ std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) co } } + ss << "\n/* Shared Variables. */\n"; + for (const ShaderCreateInfo::SharedVariable &sv : info.shared_variables_) { + ss << "shared " << to_string(sv.type) << " " << sv.name << ";\n"; + } + ss << "\n/* Pass Resources. */\n"; for (const ShaderCreateInfo::Resource &res : info.pass_resources_) { print_resource(ss, vk_interface, res);