GPU: Add compilation constants

Compilation constants are constants defined in the create info.
They cannot be changed after the shader is created.
It is a replacement to macros with added type safety.

Reuse most of the logic from Specialization constants.

Pull Request: https://projects.blender.org/blender/blender/pulls/139703
This commit is contained in:
Clément Foucault
2025-06-03 17:34:04 +02:00
committed by Clément Foucault
parent c0ec3ff044
commit e69762b8dd
15 changed files with 225 additions and 69 deletions

View File

@@ -4,6 +4,12 @@
#include "gpu_shader_compositor_texture_utilities.glsl"
/* TODO(fclem): deduplicate. */
#define CMP_NODE_MASKTYPE_ADD 0
#define CMP_NODE_MASKTYPE_SUBTRACT 1
#define CMP_NODE_MASKTYPE_MULTIPLY 2
#define CMP_NODE_MASKTYPE_NOT 3
void main()
{
int2 texel = int2(gl_GlobalInvocationID.xy);
@@ -17,16 +23,19 @@ void main()
float base_mask_value = texture_load(base_mask_tx, texel).x;
float value = texture_load(mask_value_tx, texel).x;
#if defined(CMP_NODE_MASKTYPE_ADD)
float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
#elif defined(CMP_NODE_MASKTYPE_SUBTRACT)
float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0f, 1.0f) :
base_mask_value;
#elif defined(CMP_NODE_MASKTYPE_MULTIPLY)
float output_mask_value = is_inside ? base_mask_value * value : 0.0f;
#elif defined(CMP_NODE_MASKTYPE_NOT)
float output_mask_value = is_inside ? (base_mask_value > 0.0f ? 0.0f : value) : base_mask_value;
#endif
float output_mask_value;
if (node_type == CMP_NODE_MASKTYPE_ADD) {
output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
}
else if (node_type == CMP_NODE_MASKTYPE_SUBTRACT) {
output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0f, 1.0f) : base_mask_value;
}
else if (node_type == CMP_NODE_MASKTYPE_MULTIPLY) {
output_mask_value = is_inside ? base_mask_value * value : 0.0f;
}
else if (node_type == CMP_NODE_MASKTYPE_NOT) {
output_mask_value = is_inside ? (base_mask_value > 0.0f ? 0.0f : value) : base_mask_value;
}
imageStore(output_mask_img, texel, float4(output_mask_value));
}

View File

@@ -4,6 +4,12 @@
#include "gpu_shader_compositor_texture_utilities.glsl"
/* TODO(fclem): deduplicate. */
#define CMP_NODE_MASKTYPE_ADD 0
#define CMP_NODE_MASKTYPE_SUBTRACT 1
#define CMP_NODE_MASKTYPE_MULTIPLY 2
#define CMP_NODE_MASKTYPE_NOT 3
void main()
{
int2 texel = int2(gl_GlobalInvocationID.xy);
@@ -17,16 +23,19 @@ void main()
float base_mask_value = texture_load(base_mask_tx, texel).x;
float value = texture_load(mask_value_tx, texel).x;
#if defined(CMP_NODE_MASKTYPE_ADD)
float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
#elif defined(CMP_NODE_MASKTYPE_SUBTRACT)
float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0f, 1.0f) :
base_mask_value;
#elif defined(CMP_NODE_MASKTYPE_MULTIPLY)
float output_mask_value = is_inside ? base_mask_value * value : 0.0f;
#elif defined(CMP_NODE_MASKTYPE_NOT)
float output_mask_value = is_inside ? (base_mask_value > 0.0f ? 0.0f : value) : base_mask_value;
#endif
float output_mask_value;
if (node_type == CMP_NODE_MASKTYPE_ADD) {
output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
}
else if (node_type == CMP_NODE_MASKTYPE_SUBTRACT) {
output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0f, 1.0f) : base_mask_value;
}
else if (node_type == CMP_NODE_MASKTYPE_MULTIPLY) {
output_mask_value = is_inside ? base_mask_value * value : 0.0f;
}
else if (node_type == CMP_NODE_MASKTYPE_NOT) {
output_mask_value = is_inside ? (base_mask_value > 0.0f ? 0.0f : value) : base_mask_value;
}
imageStore(output_mask_img, texel, float4(output_mask_value));
}

View File

@@ -17,26 +17,32 @@ IMAGE(0, GPU_R16F, write, image2D, output_mask_img)
COMPUTE_SOURCE("compositor_box_mask.glsl")
GPU_SHADER_CREATE_END()
/* TODO(fclem): deduplicate. */
#define CMP_NODE_MASKTYPE_ADD 0
#define CMP_NODE_MASKTYPE_SUBTRACT 1
#define CMP_NODE_MASKTYPE_MULTIPLY 2
#define CMP_NODE_MASKTYPE_NOT 3
GPU_SHADER_CREATE_INFO(compositor_box_mask_add)
ADDITIONAL_INFO(compositor_box_mask_shared)
DEFINE("CMP_NODE_MASKTYPE_ADD")
COMPILATION_CONSTANT(int, node_type, CMP_NODE_MASKTYPE_ADD)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_box_mask_subtract)
ADDITIONAL_INFO(compositor_box_mask_shared)
DEFINE("CMP_NODE_MASKTYPE_SUBTRACT")
COMPILATION_CONSTANT(int, node_type, CMP_NODE_MASKTYPE_SUBTRACT)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_box_mask_multiply)
ADDITIONAL_INFO(compositor_box_mask_shared)
DEFINE("CMP_NODE_MASKTYPE_MULTIPLY")
COMPILATION_CONSTANT(int, node_type, CMP_NODE_MASKTYPE_MULTIPLY)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_box_mask_not)
ADDITIONAL_INFO(compositor_box_mask_shared)
DEFINE("CMP_NODE_MASKTYPE_NOT")
COMPILATION_CONSTANT(int, node_type, CMP_NODE_MASKTYPE_NOT)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()

View File

@@ -17,26 +17,32 @@ IMAGE(0, GPU_R16F, write, image2D, output_mask_img)
COMPUTE_SOURCE("compositor_ellipse_mask.glsl")
GPU_SHADER_CREATE_END()
/* TODO(fclem): deduplicate. */
#define CMP_NODE_MASKTYPE_ADD 0
#define CMP_NODE_MASKTYPE_SUBTRACT 1
#define CMP_NODE_MASKTYPE_MULTIPLY 2
#define CMP_NODE_MASKTYPE_NOT 3
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_add)
ADDITIONAL_INFO(compositor_ellipse_mask_shared)
DEFINE("CMP_NODE_MASKTYPE_ADD")
COMPILATION_CONSTANT(int, node_type, CMP_NODE_MASKTYPE_ADD)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_subtract)
ADDITIONAL_INFO(compositor_ellipse_mask_shared)
DEFINE("CMP_NODE_MASKTYPE_SUBTRACT")
COMPILATION_CONSTANT(int, node_type, CMP_NODE_MASKTYPE_SUBTRACT)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_multiply)
ADDITIONAL_INFO(compositor_ellipse_mask_shared)
DEFINE("CMP_NODE_MASKTYPE_MULTIPLY")
COMPILATION_CONSTANT(int, node_type, CMP_NODE_MASKTYPE_MULTIPLY)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_not)
ADDITIONAL_INFO(compositor_ellipse_mask_shared)
DEFINE("CMP_NODE_MASKTYPE_NOT")
COMPILATION_CONSTANT(int, node_type, CMP_NODE_MASKTYPE_NOT)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()

View File

@@ -261,4 +261,16 @@ struct SpecializationConstants {
}
};
struct CompilationConstant {
Type type;
StringRefNull name;
/* Reusing value type. */
SpecializationConstant::Value value;
bool operator==(const CompilationConstant &b) const
{
return this->type == b.type && this->name == b.name && this->value == b.value;
}
};
} // namespace blender::gpu::shader

View File

@@ -132,6 +132,7 @@ void ShaderCreateInfo::finalize(const bool recursive)
geometry_out_interfaces_.extend_non_duplicates(info.geometry_out_interfaces_);
subpass_inputs_.extend_non_duplicates(info.subpass_inputs_);
specialization_constants_.extend_non_duplicates(info.specialization_constants_);
compilation_constants_.extend_non_duplicates(info.compilation_constants_);
validate_vertex_attributes(&info);
@@ -316,6 +317,16 @@ std::string ShaderCreateInfo::check_error() const
}
}
/* Validate compilation constants. */
for (int i = 0; i < compilation_constants_.size(); i++) {
for (int j = i + 1; j < compilation_constants_.size(); j++) {
if (compilation_constants_[i].name == compilation_constants_[j].name) {
error += this->name_ + " contains two compilation constants with the name: " +
std::string(compilation_constants_[i].name);
}
}
}
return error;
}

View File

@@ -137,6 +137,9 @@
# define SPECIALIZATION_CONSTANT(type, name, default_value) \
.specialization_constant(Type::type##_t, #name, default_value)
# define COMPILATION_CONSTANT(type, name, value) \
.compilation_constant(Type::type##_t, #name, value)
# define PUSH_CONSTANT(type, name) .push_constant(Type::type##_t, #name)
# define PUSH_CONSTANT_ARRAY(type, name, array_size) \
.push_constant(Type::type##_t, #name, array_size)
@@ -223,6 +226,8 @@
# define SPECIALIZATION_CONSTANT(type, name, default_value) \
constexpr type name = type(default_value);
# define COMPILATION_CONSTANT(type, name, value) constexpr type name = type(value);
# define PUSH_CONSTANT(type, name) extern const type name;
# define PUSH_CONSTANT_ARRAY(type, name, array_size) extern const type name[array_size];
@@ -749,6 +754,7 @@ struct ShaderCreateInfo {
};
Vector<SubpassIn> subpass_inputs_;
Vector<CompilationConstant, 0> compilation_constants_;
Vector<SpecializationConstant> specialization_constants_;
struct Sampler {
@@ -990,6 +996,38 @@ struct ShaderCreateInfo {
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shader compilation constants
*
* Compilation constants are constants defined in the create info.
* They cannot be changed after the shader is created.
* It is a replacement to macros with added type safety.
* \{ */
Self &compilation_constant(Type type, StringRefNull name, double default_value)
{
CompilationConstant constant;
constant.type = type;
constant.name = name;
switch (type) {
case Type::int_t:
constant.value.i = int(default_value);
break;
case Type::bool_t:
case Type::uint_t:
constant.value.u = uint(default_value);
break;
default:
BLI_assert_msg(0, "Only scalar integer and bool types can be used as constants");
break;
}
compilation_constants_.append(constant);
interface_names_size_ += name.size() + 1;
return *(Self *)this;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Shader specialization constants
* \{ */

View File

@@ -360,6 +360,30 @@ static void generate_specialization_constant_declarations(const shader::ShaderCr
}
}
static void generate_compilation_constant_declarations(const shader::ShaderCreateInfo *info,
std::stringstream &ss)
{
for (const CompilationConstant &cc : info->compilation_constants_) {
std::string value;
std::string value_define;
switch (cc.type) {
case Type::uint_t:
value = std::to_string(cc.value.u);
break;
case Type::int_t:
value = std::to_string(cc.value.i);
break;
case Type::bool_t:
value = cc.value.u ? "true" : "false";
value_define = std::to_string(cc.value.u);
break;
default:
BLI_assert_unreachable();
}
ss << "constant " << cc.type << " " << cc.name << " = " << value << ";\n";
}
}
bool MTLShader::generate_msl_from_glsl(const shader::ShaderCreateInfo *info)
{
/* Verify if create-info is available.
@@ -500,6 +524,10 @@ bool MTLShader::generate_msl_from_glsl(const shader::ShaderCreateInfo *info)
generate_specialization_constant_declarations(info, ss_vertex);
generate_specialization_constant_declarations(info, ss_fragment);
/* Generate compilation constants. */
generate_compilation_constant_declarations(info, ss_vertex);
generate_compilation_constant_declarations(info, ss_fragment);
/*** Generate VERTEX Stage ***/
/* Conditional defines. */
if (msl_iface.use_argument_buffer_for_samplers()) {
@@ -878,6 +906,7 @@ bool MTLShader::generate_msl_from_glsl_compute(const shader::ShaderCreateInfo *i
}
generate_specialization_constant_declarations(info, ss_compute);
generate_compilation_constant_declarations(info, ss_compute);
/* Conditional defines. */
if (msl_iface.use_argument_buffer_for_samplers()) {

View File

@@ -593,6 +593,24 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const
{
std::stringstream ss;
ss << "\n/* Compilation Constants (pass-through). */\n";
for (const CompilationConstant &sc : info.compilation_constants_) {
ss << "const ";
switch (sc.type) {
case Type::int_t:
ss << "int " << sc.name << "=" << std::to_string(sc.value.i) << ";\n";
break;
case Type::uint_t:
ss << "uint " << sc.name << "=" << std::to_string(sc.value.u) << "u;\n";
break;
case Type::bool_t:
ss << "bool " << sc.name << "=" << (sc.value.u ? "true" : "false") << ";\n";
break;
default:
BLI_assert_unreachable();
break;
}
}
/* 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. */

View File

@@ -17,38 +17,38 @@ FRAGMENT_SHADER_CREATE_INFO(gpu_shader_icon)
void main()
{
/* Sample texture with LOD BIAS. Used instead of custom LOD bias in GPU_SAMPLER_CUSTOM_ICON. */
fragColor = texture(image, texCoord_interp, -0.5f) * finalColor;
fragColor = texture(image, texCoord_interp, -0.5f) * final_color;
#ifdef DO_CORNER_MASKING
/* Top-left rounded corner parameters. */
constexpr float circle_radius_outer = 0.1f;
constexpr float circle_radius_inner = 0.075f;
if (do_corner_masking) {
/* Top-left rounded corner parameters. */
constexpr float circle_radius_outer = 0.1f;
constexpr float circle_radius_inner = 0.075f;
/**
* Add a bit transparency to see a bit of the icon, without
* getting on the way of readability. */
constexpr float mask_transparency = 0.25f;
/**
* Add a bit transparency to see a bit of the icon, without
* getting on the way of readability. */
constexpr float mask_transparency = 0.25f;
float2 circle_center = float2(circle_radius_outer - text_width, 0.5f);
float2 circle_center = float2(circle_radius_outer - text_width, 0.5f);
/* Radius in icon space (1 is the icon width). */
float radius = length(mask_coord_interp - circle_center);
float mask = smoothstep(circle_radius_inner, circle_radius_outer, radius);
/* Radius in icon space (1 is the icon width). */
float radius = length(mask_coord_interp - circle_center);
float mask = smoothstep(circle_radius_inner, circle_radius_outer, radius);
bool lower_half = mask_coord_interp.y < circle_center.y;
bool right_half = mask_coord_interp.x > circle_center.x;
bool lower_half = mask_coord_interp.y < circle_center.y;
bool right_half = mask_coord_interp.x > circle_center.x;
if (right_half && mask_coord_interp.y < circle_center.y + circle_radius_outer) {
mask = smoothstep(circle_center.y + circle_radius_inner,
circle_center.y + circle_radius_outer,
mask_coord_interp.y);
if (right_half && mask_coord_interp.y < circle_center.y + circle_radius_outer) {
mask = smoothstep(circle_center.y + circle_radius_inner,
circle_center.y + circle_radius_outer,
mask_coord_interp.y);
}
if (lower_half && mask_coord_interp.x > circle_center.x - circle_radius_outer) {
mask = smoothstep(circle_center.x - circle_radius_inner,
circle_center.x - circle_radius_outer,
mask_coord_interp.x);
}
fragColor = mix(float4(0.0f), fragColor, max(mask_transparency, mask));
}
if (lower_half && mask_coord_interp.x > circle_center.x - circle_radius_outer) {
mask = smoothstep(circle_center.x - circle_radius_inner,
circle_center.x - circle_radius_outer,
mask_coord_interp.x);
}
fragColor = mix(float4(0.0f), fragColor, max(mask_transparency, mask));
#endif
}

View File

@@ -15,7 +15,7 @@ void main()
{
float4 rect = multi_icon_data.calls_data[gl_InstanceID * 3];
float4 tex = multi_icon_data.calls_data[gl_InstanceID * 3 + 1];
finalColor = multi_icon_data.calls_data[gl_InstanceID * 3 + 2];
final_color = multi_icon_data.calls_data[gl_InstanceID * 3 + 2];
/* Use pos to select the right swizzle (instead of gl_VertexID)
* in order to workaround an OSX driver bug. */

View File

@@ -42,4 +42,5 @@ void main()
gl_Position = ModelViewProjectionMatrix * float4(co, 0.0f, 1.0f);
texCoord_interp = uv;
final_color = finalColor;
}

View File

@@ -34,12 +34,8 @@ GPU_SHADER_INTERFACE_INFO(smooth_radii_outline_iface)
SMOOTH(float4, radii)
GPU_SHADER_INTERFACE_END()
GPU_SHADER_INTERFACE_INFO(flat_color_smooth_tex_coord_interp_iface)
FLAT(float4, finalColor)
SMOOTH(float2, texCoord_interp)
GPU_SHADER_INTERFACE_END()
GPU_SHADER_INTERFACE_INFO(smooth_icon_interp_iface)
GPU_SHADER_INTERFACE_INFO(icon_interp_iface)
FLAT(float4, final_color)
SMOOTH(float2, texCoord_interp)
SMOOTH(float2, mask_coord_interp)
GPU_SHADER_INTERFACE_END()

View File

@@ -11,15 +11,13 @@
# include "gpu_glsl_cpp_stubs.hh"
# include "GPU_shader_shared.hh"
# define DO_CORNER_MASKING
#endif
#include "gpu_interface_info.hh"
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(gpu_shader_icon)
DEFINE("DO_CORNER_MASKING")
VERTEX_OUT(smooth_icon_interp_iface)
GPU_SHADER_CREATE_INFO(gpu_shader_icon_shared)
VERTEX_OUT(icon_interp_iface)
FRAGMENT_OUT(0, float4, fragColor)
PUSH_CONSTANT(float4x4, ModelViewProjectionMatrix)
PUSH_CONSTANT(float4, finalColor)
@@ -27,19 +25,23 @@ PUSH_CONSTANT(float4, rect_icon)
PUSH_CONSTANT(float4, rect_geom)
PUSH_CONSTANT(float, text_width)
SAMPLER(0, sampler2D, image)
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(gpu_shader_icon)
COMPILATION_CONSTANT(bool, do_corner_masking, true)
VERTEX_SOURCE("gpu_shader_icon_vert.glsl")
FRAGMENT_SOURCE("gpu_shader_icon_frag.glsl")
ADDITIONAL_INFO(gpu_shader_icon_shared)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(gpu_shader_icon_multi)
COMPILATION_CONSTANT(bool, do_corner_masking, false)
VERTEX_IN(0, float2, pos)
VERTEX_OUT(flat_color_smooth_tex_coord_interp_iface)
FRAGMENT_OUT(0, float4, fragColor)
UNIFORM_BUF(0, MultiIconCallData, multi_icon_data)
SAMPLER(0, sampler2D, image)
TYPEDEF_SOURCE("GPU_shader_shared.hh")
VERTEX_SOURCE("gpu_shader_icon_multi_vert.glsl")
FRAGMENT_SOURCE("gpu_shader_icon_frag.glsl")
ADDITIONAL_INFO(gpu_shader_icon_shared)
DO_STATIC_COMPILATION()
GPU_SHADER_CREATE_END()

View File

@@ -787,6 +787,25 @@ std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) co
}
}
ss << "\n/* Compilation Constants (pass-through). */\n";
for (const CompilationConstant &sc : info.compilation_constants_) {
ss << "const ";
switch (sc.type) {
case Type::int_t:
ss << "int " << sc.name << "=" << std::to_string(sc.value.i) << ";\n";
break;
case Type::uint_t:
ss << "uint " << sc.name << "=" << std::to_string(sc.value.u) << "u;\n";
break;
case Type::bool_t:
ss << "bool " << sc.name << "=" << (sc.value.u ? "true" : "false") << ";\n";
break;
default:
BLI_assert_unreachable();
break;
}
}
ss << "\n/* Pass Resources. */\n";
for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
print_resource(ss, vk_interface, res);