GPU: Simplify Codegen dependency injection

Dependencies were previously merged manually
inside the generated_sources by EEVEE.
This caused issues with double includes.

Instead, we now only gather the name of the
nodetree dependencies and add them to the
dependencies of the `GeneratedSource`.

This also make the compositor use the `GeneratedSource`
mechanism.

Pull Request: https://projects.blender.org/blender/blender/pulls/146106
This commit is contained in:
Clément Foucault
2025-09-12 16:07:55 +02:00
committed by Clément Foucault
parent ad27211b77
commit f729e639f4
18 changed files with 117 additions and 139 deletions

View File

@@ -288,6 +288,7 @@ set(GLSL_SRC
shaders/library/gpu_shader_compositor_color_to_luminance.glsl
shaders/library/gpu_shader_compositor_difference_matte.glsl
shaders/library/gpu_shader_compositor_distance_matte.glsl
shaders/library/gpu_shader_compositor_eval.glsl
shaders/library/gpu_shader_compositor_exposure.glsl
shaders/library/gpu_shader_compositor_hue_correct.glsl
shaders/library/gpu_shader_compositor_hue_saturation_value.glsl
@@ -302,6 +303,7 @@ set(GLSL_SRC
shaders/library/gpu_shader_compositor_posterize.glsl
shaders/library/gpu_shader_compositor_separate_combine.glsl
shaders/library/gpu_shader_compositor_set_alpha.glsl
shaders/library/gpu_shader_compositor_store.glsl
shaders/library/gpu_shader_compositor_store_output.glsl
shaders/library/gpu_shader_compositor_summed_area_table_lib.glsl
shaders/library/gpu_shader_compositor_texture_utilities.glsl

View File

@@ -183,13 +183,13 @@ class ShaderOperation : public PixelOperation {
/* Add an image in the shader for each of the declared outputs. Additionally, emit code to define
* the storer functions that store the given value in the appropriate image identified by the
* given index. */
void generate_code_for_outputs(gpu::shader::ShaderCreateInfo &shader_create_info);
std::string generate_code_for_outputs(gpu::shader::ShaderCreateInfo &shader_create_info);
/* Add a texture will in the shader for each of the declared inputs/attributes in the operation,
* having the same name as the attribute. Additionally, emit code to initialize the attributes by
* sampling their corresponding textures. */
void generate_code_for_inputs(GPUMaterial *material,
gpu::shader::ShaderCreateInfo &shader_create_info);
std::string generate_code_for_inputs(GPUMaterial *material,
gpu::shader::ShaderCreateInfo &shader_create_info);
};
} // namespace blender::compositor

View File

@@ -540,22 +540,21 @@ void ShaderOperation::generate_code(void *thunk,
* functions that writes the outputs are defined outside the evaluate function. */
shader_create_info.compute_source("gpu_shader_compositor_main.glsl");
/* The main function is emitted in the shader before the evaluate function, so the evaluate
* function needs to be forward declared here.
* NOTE(Metal): Metal does not require forward declarations. */
if (GPU_backend_get_type() != GPU_BACKEND_METAL) {
shader_create_info.typedef_source_generated += "void evaluate();\n";
}
std::string store_code = operation->generate_code_for_outputs(shader_create_info);
shader_create_info.generated_sources.append(
{"gpu_shader_compositor_store.glsl", {}, store_code});
operation->generate_code_for_outputs(shader_create_info);
std::string eval_code;
eval_code += "void evaluate()\n{\n";
shader_create_info.compute_source_generated += "void evaluate()\n{\n";
eval_code += operation->generate_code_for_inputs(material, shader_create_info);
operation->generate_code_for_inputs(material, shader_create_info);
eval_code += code_generator_output->composite;
shader_create_info.compute_source_generated += code_generator_output->composite;
eval_code += "}\n";
shader_create_info.compute_source_generated += "}\n";
shader_create_info.generated_sources.append(
{"gpu_shader_compositor_eval.glsl", shader_create_info.dependencies_generated, eval_code});
}
/* Texture storers in the shader always take a [i]vec4 as an argument, so encode each type in an
@@ -627,7 +626,7 @@ static ImageType gpu_image_type_from_result_type(const ResultType type)
return ImageType::Float2D;
}
void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info)
std::string ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info)
{
const std::string store_float_function_header = "void store_float(const uint id, float value)";
/* GPUMaterial doesn't support int, so it is passed as a float. */
@@ -644,21 +643,6 @@ void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_
/* GPUMaterial doesn't support int, so it is passed as a float. */
const std::string store_menu_function_header = "void store_menu(const uint id, float value)";
/* The store functions are used by the node_compositor_store_output_[type] functions but are only
* defined later as part of the compute source, so they need to be forward declared. NOTE(Metal):
* Metal does not require forward declarations. */
if (GPU_backend_get_type() != GPU_BACKEND_METAL) {
shader_create_info.typedef_source_generated += store_float_function_header + ";\n";
shader_create_info.typedef_source_generated += store_int_function_header + ";\n";
shader_create_info.typedef_source_generated += store_bool_function_header + ";\n";
shader_create_info.typedef_source_generated += store_float3_function_header + ";\n";
shader_create_info.typedef_source_generated += store_color_function_header + ";\n";
shader_create_info.typedef_source_generated += store_float4_function_header + ";\n";
shader_create_info.typedef_source_generated += store_float2_function_header + ";\n";
shader_create_info.typedef_source_generated += store_int2_function_header + ";\n";
shader_create_info.typedef_source_generated += store_menu_function_header + ";\n";
}
/* Each of the store functions is essentially a single switch case on the given ID, so start by
* opening the function with a curly bracket followed by opening a switch statement in each of
* the functions. */
@@ -752,10 +736,9 @@ void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_
store_int2_function << store_function_end;
store_menu_function << store_function_end;
shader_create_info.compute_source_generated +=
store_float_function.str() + store_int_function.str() + store_bool_function.str() +
store_float3_function.str() + store_color_function.str() + store_float4_function.str() +
store_float2_function.str() + store_int2_function.str() + store_menu_function.str();
return store_float_function.str() + store_int_function.str() + store_bool_function.str() +
store_float3_function.str() + store_color_function.str() + store_float4_function.str() +
store_float2_function.str() + store_int2_function.str() + store_menu_function.str();
}
static const char *glsl_type_from_result_type(ResultType type)
@@ -827,16 +810,18 @@ static const char *glsl_swizzle_from_result_type(ResultType type)
return nullptr;
}
void ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
ShaderCreateInfo &shader_create_info)
std::string ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
ShaderCreateInfo &shader_create_info)
{
/* The attributes of the GPU material represents the inputs of the operation. */
ListBase attributes = GPU_material_attributes(material);
if (BLI_listbase_is_empty(&attributes)) {
return;
return "";
}
std::string code;
/* Add a texture sampler for each of the inputs with the same name as the attribute, we start
* counting the sampler slot location from the number of textures in the material, since some
* sampler slots may be reserved for things like color band textures. */
@@ -863,7 +848,7 @@ void ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
}
declare_attributes << "} var_attrs;\n\n";
shader_create_info.compute_source_generated += declare_attributes.str();
code += declare_attributes.str();
/* The texture loader utilities are needed to sample the input textures and initialize the
* attributes. */
@@ -883,7 +868,9 @@ void ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
}
initialize_attributes << "\n";
shader_create_info.compute_source_generated += initialize_attributes.str();
code += initialize_attributes.str();
return code;
}
} // namespace blender::compositor

View File

@@ -0,0 +1,10 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/* This file must replaced at runtime. The following content is only a possible implementation. */
#pragma runtime_generated
/* Function generated from #ShaderOperation::generate_code. */
void evaluate() {}

View File

@@ -2,6 +2,8 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_compositor_eval.glsl"
/* The compute shader that will be dispatched by the compositor ShaderOperation. It just calls the
* evaluate function that will be dynamically generated and appended to this shader in the
* ShaderOperation::generate_code method. */

View File

@@ -0,0 +1,21 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/* This file must replaced at runtime. The following content is only a possible implementation. */
#pragma runtime_generated
/* Functions generated from #ShaderOperation::generate_code_for_outputs. */
/* Commented out because they mess with the library parser. */
/**
* void store_float(const uint id, float value) {}
* void store_int(const uint id, float value) {}
* void store_bool(const uint id, float value) {}
* void store_float3(const uint id, vec3 value) {}
* void store_color(const uint id, vec4 value) {}
* void store_float4(const uint id, vec4 value) {}
* void store_float2(const uint id, vec3 value) {}
* void store_int2(const uint id, vec3 value) {}
* void store_menu(const uint id, float value) {}
*/

View File

@@ -11,6 +11,8 @@
* The store_[type] functions are dynamically generated in
* ShaderOperation::generate_code_for_outputs. */
#include "gpu_shader_compositor_store.glsl"
void node_compositor_store_output_float(const float id, float value, out float out_value)
{
store_float(floatBitsToUint(id), value);

View File

@@ -893,13 +893,6 @@ void ShaderModule::material_create_info_amend(GPUMaterial *gpumat, GPUCodegenOut
<< attr_load.str();
}
/* TODO(fclem): This should become part of the dependency system. */
std::string deps_concat;
for (const StringRefNull &str : info.dependencies_generated) {
deps_concat += str;
}
info.dependencies_generated = {};
{
const bool use_vertex_displacement = !codegen.displacement.empty() &&
(displacement_type != MAT_DISPLACEMENT_BUMP) &&
@@ -910,9 +903,10 @@ void ShaderModule::material_create_info_amend(GPUMaterial *gpumat, GPUCodegenOut
vert_gen << ((use_vertex_displacement) ? codegen.displacement : "return float3(0);\n");
vert_gen << "}\n\n";
info.generated_sources.append({"eevee_nodetree_vert_lib.glsl",
{"eevee_nodetree_lib.glsl"},
deps_concat + vert_gen.str()});
Vector<StringRefNull> dependencies = {"eevee_nodetree_lib.glsl"};
dependencies.extend(info.dependencies_generated);
info.generated_sources.append({"eevee_nodetree_vert_lib.glsl", dependencies, vert_gen.str()});
}
if (pipeline_type != MAT_PIPE_VOLUME_OCCUPANCY) {
@@ -973,9 +967,10 @@ void ShaderModule::material_create_info_amend(GPUMaterial *gpumat, GPUCodegenOut
frag_gen << (!codegen.volume.empty() ? codegen.volume : "return Closure(0);\n");
frag_gen << "}\n\n";
info.generated_sources.append({"eevee_nodetree_frag_lib.glsl",
{"eevee_nodetree_lib.glsl"},
deps_concat + frag_gen.str()});
Vector<StringRefNull> dependencies = {"eevee_nodetree_lib.glsl"};
dependencies.extend(info.dependencies_generated);
info.generated_sources.append({"eevee_nodetree_frag_lib.glsl", dependencies, frag_gen.str()});
}
int reserved_attr_slots = 0;

View File

@@ -294,7 +294,7 @@ void GPUCodegen::generate_library()
GPUCodegenCreateInfo &info = *create_info;
void *value;
Vector<std::string> source_files;
Vector<StringRefNull> source_files;
/* Iterate over libraries. We need to keep this struct intact in case it is required for the
* optimization pass. The first pass just collects the keys from the GSET, given items in a GSET
@@ -311,8 +311,7 @@ void GPUCodegen::generate_library()
std::sort(source_files.begin(), source_files.end());
for (auto &key : source_files) {
auto deps = gpu_shader_dependency_get_resolved_source(key.c_str(), {});
info.dependencies_generated.extend_non_duplicates(deps);
info.dependencies_generated.append_non_duplicates(key);
}
}

View File

@@ -725,7 +725,6 @@ Shader *ShaderCompiler::compile(const shader::ShaderCreateInfo &info, bool is_ba
sources.append(resources);
sources.append(interface);
sources.extend(code);
sources.extend(info.dependencies_generated);
sources.append(info.vertex_source_generated);
if (info.vertex_entry_fn_ != "main") {
@@ -753,7 +752,6 @@ Shader *ShaderCompiler::compile(const shader::ShaderCreateInfo &info, bool is_ba
sources.append(resources);
sources.append(interface);
sources.extend(code);
sources.extend(info.dependencies_generated);
sources.append(info.fragment_source_generated);
if (info.fragment_entry_fn_ != "main") {
@@ -804,7 +802,6 @@ Shader *ShaderCompiler::compile(const shader::ShaderCreateInfo &info, bool is_ba
sources.append(resources);
sources.append(layout);
sources.extend(code);
sources.extend(info.dependencies_generated);
sources.append(info.compute_source_generated);
if (info.compute_entry_fn_ != "main") {

View File

@@ -713,7 +713,7 @@ struct ShaderCreateInfo {
std::string compute_source_generated;
std::string geometry_source_generated;
std::string typedef_source_generated;
/** Manually set generated dependencies. */
/** Manually set generated dependencies file names. */
Vector<StringRefNull, 0> dependencies_generated;
GeneratedSourceList generated_sources;

View File

@@ -61,7 +61,6 @@ struct GPUSource {
shader::BuiltinBits builtins = shader::BuiltinBits::NONE;
/* True if this file content is supposed to be generated at runtime. */
bool generated = false;
int d[sizeof(shader::ShaderCreateInfo::dependencies_generated)];
/* NOTE: The next few functions are needed to keep isolation of the preprocessor.
* Eventually, this should be revisited and the preprocessor should output

View File

@@ -6,24 +6,20 @@
#include "gpu_glsl_cpp_stubs.hh"
/* WORKAROUND: to guard against double include in EEVEE. */
#ifndef GPU_SHADER_MATH_BASE_LIB_GLSL
# define GPU_SHADER_MATH_BASE_LIB_GLSL
# define M_PI 3.14159265358979323846f /* pi */
# define M_TAU 6.28318530717958647692f /* tau = 2*pi */
# define M_PI_2 1.57079632679489661923f /* pi/2 */
# define M_PI_4 0.78539816339744830962f /* pi/4 */
# define M_SQRT2 1.41421356237309504880f /* sqrt(2) */
# define M_SQRT1_2 0.70710678118654752440f /* 1/sqrt(2) */
# define M_SQRT3 1.73205080756887729352f /* sqrt(3) */
# define M_SQRT1_3 0.57735026918962576450f /* 1/sqrt(3) */
# define M_1_PI 0.318309886183790671538f /* 1/pi */
# define M_E 2.7182818284590452354f /* e */
# define M_LOG2E 1.4426950408889634074f /* log_2 e */
# define M_LOG10E 0.43429448190325182765f /* log_10 e */
# define M_LN2 0.69314718055994530942f /* log_e 2 */
# define M_LN10 2.30258509299404568402f /* log_e 10 */
#define M_PI 3.14159265358979323846f /* pi */
#define M_TAU 6.28318530717958647692f /* tau = 2*pi */
#define M_PI_2 1.57079632679489661923f /* pi/2 */
#define M_PI_4 0.78539816339744830962f /* pi/4 */
#define M_SQRT2 1.41421356237309504880f /* sqrt(2) */
#define M_SQRT1_2 0.70710678118654752440f /* 1/sqrt(2) */
#define M_SQRT3 1.73205080756887729352f /* sqrt(3) */
#define M_SQRT1_3 0.57735026918962576450f /* 1/sqrt(3) */
#define M_1_PI 0.318309886183790671538f /* 1/pi */
#define M_E 2.7182818284590452354f /* e */
#define M_LOG2E 1.4426950408889634074f /* log_2 e */
#define M_LOG10E 0.43429448190325182765f /* log_10 e */
#define M_LN2 0.69314718055994530942f /* log_e 2 */
#define M_LN10 2.30258509299404568402f /* log_e 10 */
/* `powf` is really slow for raising to integer powers. */
@@ -106,7 +102,7 @@ float _atan2(float y, float x)
{
return atan(y, x);
}
# define atan2 _atan2
#define atan2 _atan2
/**
* Safe `a` modulo `b`.
@@ -277,5 +273,3 @@ float wrap(float a, float b, float c)
}
/** \} */
#endif /* GPU_SHADER_MATH_BASE_LIB_GLSL */

View File

@@ -6,10 +6,6 @@
#include "gpu_shader_math_base_lib.glsl"
/* WORKAROUND: to guard against double include in EEVEE. */
#ifndef GPU_SHADER_MATH_FAST_LIB_GLSL
# define GPU_SHADER_MATH_FAST_LIB_GLSL
/* [Drobot2014a] Low Level Optimizations for GCN. */
float sqrt_fast(float v)
{
@@ -54,5 +50,3 @@ float atan_fast(float x)
}
return (x < 0.0f) ? -r : r;
}
#endif /* GPU_SHADER_MATH_FAST_LIB_GLSL */

View File

@@ -7,10 +7,6 @@
#include "gpu_shader_math_rotation_lib.glsl"
#include "gpu_shader_math_vector_lib.glsl"
/* WORKAROUND: to guard against double include in EEVEE. */
#ifndef GPU_SHADER_MATH_MATRIX_LIB_GLSL
# define GPU_SHADER_MATH_MATRIX_LIB_GLSL
/* -------------------------------------------------------------------- */
/** \name Static constructors
* \{ */
@@ -73,7 +69,7 @@ float4x4 mat4x4_identity()
/** \} */
/* Metal does not need prototypes. */
# ifndef GPU_METAL
#ifndef GPU_METAL
/* -------------------------------------------------------------------- */
/** \name Matrix Operations
@@ -461,7 +457,7 @@ bool is_uniformly_scaled(float3x3 mat);
/** \} */
# endif /* GPU_METAL */
#endif /* GPU_METAL */
/* ---------------------------------------------------------------------- */
/** \name Implementation
@@ -496,7 +492,7 @@ float4x4 invert(float4x4 mat, out bool r_success)
return r_success ? inverse(mat) : float4x4(0.0f);
}
# if defined(GPU_OPENGL) || defined(GPU_METAL)
#if defined(GPU_OPENGL) || defined(GPU_METAL)
float2 normalize(float2 a)
{
return a * inversesqrt(length_squared(a));
@@ -509,7 +505,7 @@ float4 normalize(float4 a)
{
return a * inversesqrt(length_squared(a));
}
# endif
#endif
float2x2 normalize(float2x2 mat)
{
@@ -1577,5 +1573,3 @@ bool is_unit_scale(float2x2 m)
}
/** \} */
#endif /* GPU_SHADER_MATH_MATRIX_LIB_GLSL */

View File

@@ -7,10 +7,6 @@
#include "gpu_shader_math_vector_lib.glsl"
#include "gpu_shader_utildefines_lib.glsl"
/* WORKAROUND: to guard against double include in EEVEE. */
#ifndef GPU_SHADER_MATH_ROTATION_LIB_GLSL
# define GPU_SHADER_MATH_ROTATION_LIB_GLSL
/* -------------------------------------------------------------------- */
/** \name Rotation Types
* \{ */
@@ -181,5 +177,3 @@ AxisAngle to_axis_angle(EulerXYZ eul)
}
/** \} */
#endif /* GPU_SHADER_MATH_ROTATION_LIB_GLSL */

View File

@@ -6,12 +6,8 @@
#include "gpu_shader_math_base_lib.glsl"
/* WORKAROUND: to guard against double include in EEVEE. */
#ifndef GPU_SHADER_MATH_VECTOR_LIB_GLSL
# define GPU_SHADER_MATH_VECTOR_LIB_GLSL
/* Metal does not need prototypes. */
# ifndef GPU_METAL
#ifndef GPU_METAL
/**
* Return true if all components is equal to zero.
@@ -260,7 +256,7 @@ float average(float2 a);
float average(float3 a);
float average(float4 a);
# endif /* !GPU_METAL */
#endif /* !GPU_METAL */
/* ---------------------------------------------------------------------- */
/** \name Implementation
@@ -714,7 +710,7 @@ float average(float4 a)
return reduce_add(a) * (1.0f / 4.0f);
}
# define ASSERT_UNIT_EPSILON 0.0002f
#define ASSERT_UNIT_EPSILON 0.0002f
/* Checks are flipped so NAN doesn't assert because we're making sure the value was
* normalized and in the case we don't want NAN to be raising asserts since there
@@ -739,5 +735,3 @@ bool is_unit_scale(float4 v)
}
/** \} */
#endif /* GPU_SHADER_MATH_VECTOR_LIB_GLSL */

View File

@@ -6,36 +6,32 @@
#include "gpu_glsl_cpp_stubs.hh"
/* WORKAROUND: to guard against double include in EEVEE. */
#ifndef GPU_SHADER_UTILDEFINES_GLSL
# define GPU_SHADER_UTILDEFINES_GLSL
#ifndef FLT_MAX
# define FLT_MAX uintBitsToFloat(0x7F7FFFFFu)
# define FLT_MIN uintBitsToFloat(0x00800000u)
# define FLT_EPSILON 1.192092896e-07F
#endif
#ifndef SHRT_MAX
# define SHRT_MAX 0x00007FFF
# define INT_MAX 0x7FFFFFFF
# define USHRT_MAX 0x0000FFFFu
# define UINT_MAX 0xFFFFFFFFu
#endif
#define NAN_FLT uintBitsToFloat(0x7FC00000u)
#define FLT_11_MAX uintBitsToFloat(0x477E0000)
#define FLT_10_MAX uintBitsToFloat(0x477C0000)
#define FLT_11_11_10_MAX float3(FLT_11_MAX, FLT_11_MAX, FLT_10_MAX)
# ifndef FLT_MAX
# define FLT_MAX uintBitsToFloat(0x7F7FFFFFu)
# define FLT_MIN uintBitsToFloat(0x00800000u)
# define FLT_EPSILON 1.192092896e-07F
# endif
# ifndef SHRT_MAX
# define SHRT_MAX 0x00007FFF
# define INT_MAX 0x7FFFFFFF
# define USHRT_MAX 0x0000FFFFu
# define UINT_MAX 0xFFFFFFFFu
# endif
# define NAN_FLT uintBitsToFloat(0x7FC00000u)
# define FLT_11_MAX uintBitsToFloat(0x477E0000)
# define FLT_10_MAX uintBitsToFloat(0x477C0000)
# define FLT_11_11_10_MAX float3(FLT_11_MAX, FLT_11_MAX, FLT_10_MAX)
# define UNPACK2(a) (a)[0], (a)[1]
# define UNPACK3(a) (a)[0], (a)[1], (a)[2]
# define UNPACK4(a) (a)[0], (a)[1], (a)[2], (a)[3]
#define UNPACK2(a) (a)[0], (a)[1]
#define UNPACK3(a) (a)[0], (a)[1], (a)[2]
#define UNPACK4(a) (a)[0], (a)[1], (a)[2], (a)[3]
/**
* Clamp input into [0..1] range.
*/
# define saturate(a) clamp(a, 0.0f, 1.0f)
#define saturate(a) clamp(a, 0.0f, 1.0f)
# define isfinite(a) (!isinf(a) && !isnan(a))
#define isfinite(a) (!isinf(a) && !isnan(a))
/* clang-format off */
#define in_range_inclusive(val, min_v, max_v) (all(greaterThanEqual(val, min_v)) && all(lessThanEqual(val, max_v)))
@@ -80,7 +76,7 @@ void set_flag_from_test(inout int value, bool test, int flag)
}
/* Keep define to match C++ implementation. */
# define SET_FLAG_FROM_TEST(value, test, flag) set_flag_from_test(value, test, flag)
#define SET_FLAG_FROM_TEST(value, test, flag) set_flag_from_test(value, test, flag)
/**
* Return true if the bit inside bitmask at bit_index is set high.
@@ -162,5 +158,3 @@ float3 offset_ray(float3 P, float3 Ng)
(abs(P.y) < origin) ? uf.y : P_i.y,
(abs(P.z) < origin) ? uf.z : P_i.z);
}
#endif /* GPU_SHADER_UTILDEFINES_GLSL */