Compositor: Add internal support for 2D vectors

This patch adds proper internal support for 2D vectors in GPU materials,
while previously it used a 3D vector with the Z component ignored as a
workaround. This has no user-facing consequences.

The type conversion file was split into an additional code generation
file for easier refactoring.

Pull Request: https://projects.blender.org/blender/blender/pulls/147983
This commit is contained in:
Omar Emara
2025-10-13 17:35:23 +02:00
committed by Omar Emara
parent 2a88d748af
commit 97e3fb0e03
7 changed files with 211 additions and 185 deletions

View File

@@ -344,6 +344,7 @@ set(GLSL_SRC
shaders/library/gpu_shader_compositor_bright_contrast.glsl
shaders/library/gpu_shader_compositor_channel_matte.glsl
shaders/library/gpu_shader_compositor_chroma_matte.glsl
shaders/library/gpu_shader_compositor_code_generation.glsl
shaders/library/gpu_shader_compositor_color_balance.glsl
shaders/library/gpu_shader_compositor_color_correction.glsl
shaders/library/gpu_shader_compositor_color_matte.glsl
@@ -367,6 +368,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_set_value.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

View File

@@ -182,7 +182,7 @@ void ShaderOperation::link_node_input_unavailable(const DInputSocket input)
zero_v4(stack.vec);
GPUNodeLink *link = GPU_constant(stack.vec);
GPU_link(material_, "set_value", link, &stack.link);
GPU_link(material_, "set_float", link, &stack.link);
}
/* Initializes the vector value of the given GPU node stack from the default value of the given
@@ -238,28 +238,27 @@ static const char *get_set_function_name(const ResultType type)
{
switch (type) {
case ResultType::Float:
return "set_value";
return "set_float";
case ResultType::Float2:
return "set_float2";
case ResultType::Float3:
return "set_float3";
case ResultType::Float4:
return "set_float4";
case ResultType::Color:
return "set_color";
case ResultType::Int:
/* GPUMaterial doesn't support int, so it is passed as a float. */
return "set_value";
return "set_float";
case ResultType::Int2:
/* GPUMaterial doesn't support int2, so it is passed as a float2. */
return "set_float2";
case ResultType::Bool:
/* GPUMaterial doesn't support bool, so it is passed as a float. */
return "set_value";
case ResultType::Float3:
return "set_rgb";
case ResultType::Color:
return "set_rgba";
case ResultType::Float4:
return "set_rgba";
case ResultType::Float2:
/* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
return "set_rgb";
case ResultType::Int2:
/* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
return "set_rgb";
return "set_float";
case ResultType::Menu:
/* GPUMaterial doesn't support int, so it is passed as a float. */
return "set_value";
return "set_float";
case ResultType::String:
/* Single only types do not support GPU code path. */
BLI_assert(Result::is_single_value_only_type(type));
@@ -460,20 +459,20 @@ static const char *get_store_function_name(ResultType type)
switch (type) {
case ResultType::Float:
return "node_compositor_store_output_float";
case ResultType::Int:
return "node_compositor_store_output_int";
case ResultType::Bool:
return "node_compositor_store_output_bool";
case ResultType::Float3:
return "node_compositor_store_output_float3";
case ResultType::Color:
return "node_compositor_store_output_color";
case ResultType::Float4:
return "node_compositor_store_output_float4";
case ResultType::Float2:
return "node_compositor_store_output_float2";
case ResultType::Float3:
return "node_compositor_store_output_float3";
case ResultType::Float4:
return "node_compositor_store_output_float4";
case ResultType::Color:
return "node_compositor_store_output_color";
case ResultType::Int:
return "node_compositor_store_output_int";
case ResultType::Int2:
return "node_compositor_store_output_int2";
case ResultType::Bool:
return "node_compositor_store_output_bool";
case ResultType::Menu:
return "node_compositor_store_output_menu";
case ResultType::String:
@@ -530,9 +529,8 @@ void ShaderOperation::generate_code(void *thunk,
shader_create_info.local_group_size(16, 16);
/* Add implementation for implicit conversion operations inserted by the code generator. This
* file should include the functions [float|vec2|vec3|vec4]_from_[float|vec2|vec3|vec4]. */
shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl");
/* Add implementation for the functions inserted by the code generator.. */
shader_create_info.typedef_source("gpu_shader_compositor_code_generation.glsl");
/* The source shader is a compute shader with a main function that calls the dynamically
* generated evaluate function. The evaluate function includes the serialized GPU material graph
@@ -565,28 +563,26 @@ static const char *glsl_store_expression_from_result_type(ResultType type)
switch (type) {
case ResultType::Float:
return "vec4(value)";
case ResultType::Float2:
return "vec4(value, 0.0f, 0.0f)";
case ResultType::Float3:
return "vec4(value, 0.0f)";
case ResultType::Float4:
return "value";
case ResultType::Color:
return "value";
case ResultType::Int:
/* GPUMaterial doesn't support int, so it is passed as a float, and we need to convert it
* back to int before writing it. */
return "ivec4(int(value))";
case ResultType::Int2:
/* GPUMaterial doesn't support int2, so it is passed as a float2, and we need to convert it
* back to int2 before writing it. */
return "ivec4(ivec2(value), 0, 0)";
case ResultType::Bool:
/* GPUMaterial doesn't support bool, so it is passed as a float and stored as an int, and we
* need to convert it back to bool and then to an int before writing it. */
return "ivec4(bool(value))";
case ResultType::Float3:
return "vec4(value, 0.0)";
case ResultType::Color:
return "value";
case ResultType::Float4:
return "value";
case ResultType::Float2:
/* GPUMaterial doesn't support float2, so it is passed as a float3, and we need to convert it
* back to float2 before writing it. */
return "vec4(value.xy, 0.0, 0.0)";
case ResultType::Int2:
/* GPUMaterial doesn't support int2, so it is passed as a float3, and we need to convert it
* back to int2 before writing it. */
return "ivec4(ivec2(value.xy), 0, 0)";
case ResultType::Menu:
/* GPUMaterial doesn't support int, so it is passed as a float, and we need to convert it
* back to int before writing it. */
@@ -630,17 +626,16 @@ static ImageType gpu_image_type_from_result_type(const ResultType type)
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)";
const std::string store_float2_function_header = "void store_float2(const uint id, vec2 value)";
const std::string store_float3_function_header = "void store_float3(const uint id, vec3 value)";
const std::string store_float4_function_header = "void store_float4(const uint id, vec4 value)";
const std::string store_color_function_header = "void store_color(const uint id, vec4 value)";
/* GPUMaterial doesn't support int, so it is passed as a float. */
const std::string store_int_function_header = "void store_int(const uint id, float value)";
/* GPUMaterial doesn't support int2, so it is passed as a float2. */
const std::string store_int2_function_header = "void store_int2(const uint id, vec2 value)";
/* GPUMaterial doesn't support bool, so it is passed as a float. */
const std::string store_bool_function_header = "void store_bool(const uint id, float value)";
const std::string store_float3_function_header = "void store_float3(const uint id, vec3 value)";
const std::string store_color_function_header = "void store_color(const uint id, vec4 value)";
const std::string store_float4_function_header = "void store_float4(const uint id, vec4 value)";
/* GPUMaterial doesn't support float2, so it is passed as a float3. */
const std::string store_float2_function_header = "void store_float2(const uint id, vec3 value)";
/* GPUMaterial doesn't support int2, so it is passed as a float3. */
const std::string store_int2_function_header = "void store_int2(const uint id, vec3 value)";
/* 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)";
@@ -648,23 +643,23 @@ std::string ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_
* opening the function with a curly bracket followed by opening a switch statement in each of
* the functions. */
std::stringstream store_float_function;
std::stringstream store_int_function;
std::stringstream store_bool_function;
std::stringstream store_float3_function;
std::stringstream store_color_function;
std::stringstream store_float4_function;
std::stringstream store_float2_function;
std::stringstream store_float3_function;
std::stringstream store_float4_function;
std::stringstream store_color_function;
std::stringstream store_int_function;
std::stringstream store_int2_function;
std::stringstream store_bool_function;
std::stringstream store_menu_function;
const std::string store_function_start = "\n{\n switch (id) {\n";
store_float_function << store_float_function_header << store_function_start;
store_int_function << store_int_function_header << store_function_start;
store_bool_function << store_bool_function_header << store_function_start;
store_float3_function << store_float3_function_header << store_function_start;
store_color_function << store_color_function_header << store_function_start;
store_float4_function << store_float4_function_header << store_function_start;
store_float2_function << store_float2_function_header << store_function_start;
store_float3_function << store_float3_function_header << store_function_start;
store_float4_function << store_float4_function_header << store_function_start;
store_color_function << store_color_function_header << store_function_start;
store_int_function << store_int_function_header << store_function_start;
store_int2_function << store_int2_function_header << store_function_start;
store_bool_function << store_bool_function_header << store_function_start;
store_menu_function << store_menu_function_header << store_function_start;
int output_index = 0;
@@ -693,27 +688,27 @@ std::string ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_
case ResultType::Float:
store_float_function << case_code.str();
break;
case ResultType::Int:
store_int_function << case_code.str();
break;
case ResultType::Bool:
store_bool_function << case_code.str();
case ResultType::Float2:
store_float2_function << case_code.str();
break;
case ResultType::Float3:
store_float3_function << case_code.str();
break;
case ResultType::Color:
store_color_function << case_code.str();
break;
case ResultType::Float4:
store_float4_function << case_code.str();
break;
case ResultType::Float2:
store_float2_function << case_code.str();
case ResultType::Color:
store_color_function << case_code.str();
break;
case ResultType::Int:
store_int_function << case_code.str();
break;
case ResultType::Int2:
store_int2_function << case_code.str();
break;
case ResultType::Bool:
store_bool_function << case_code.str();
break;
case ResultType::Menu:
store_menu_function << case_code.str();
break;
@@ -728,18 +723,18 @@ std::string ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_
/* Close the previously opened switch statement as well as the function itself. */
const std::string store_function_end = " }\n}\n\n";
store_float_function << store_function_end;
store_int_function << store_function_end;
store_bool_function << store_function_end;
store_float3_function << store_function_end;
store_color_function << store_function_end;
store_float4_function << store_function_end;
store_float2_function << store_function_end;
store_float3_function << store_function_end;
store_float4_function << store_function_end;
store_color_function << store_function_end;
store_int_function << store_function_end;
store_int2_function << store_function_end;
store_bool_function << store_function_end;
store_menu_function << store_function_end;
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();
return store_float_function.str() + store_float2_function.str() + store_float3_function.str() +
store_float4_function.str() + store_color_function.str() + store_int_function.str() +
store_int2_function.str() + store_bool_function.str() + store_menu_function.str();
}
static const char *glsl_type_from_result_type(ResultType type)
@@ -747,23 +742,23 @@ static const char *glsl_type_from_result_type(ResultType type)
switch (type) {
case ResultType::Float:
return "float";
case ResultType::Float2:
return "vec2";
case ResultType::Float3:
return "vec3";
case ResultType::Float4:
return "vec4";
case ResultType::Color:
return "vec4";
case ResultType::Int:
/* GPUMaterial doesn't support int, so it is passed as a float. */
return "float";
case ResultType::Int2:
/* GPUMaterial doesn't support int2, so it is passed as a float2. */
return "vec2";
case ResultType::Bool:
/* GPUMaterial doesn't support bool, so it is passed as a float. */
return "float";
case ResultType::Float3:
return "vec3";
case ResultType::Color:
case ResultType::Float4:
return "vec4";
case ResultType::Float2:
/* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
return "vec3";
case ResultType::Int2:
/* GPUMaterial doesn't support int2, so it is passed as a float3 with z ignored. */
return "vec3";
case ResultType::Menu:
/* GPUMaterial doesn't support int, so it is passed as a float. */
return "float";
@@ -784,22 +779,24 @@ static const char *glsl_swizzle_from_result_type(ResultType type)
{
switch (type) {
case ResultType::Float:
case ResultType::Int:
case ResultType::Bool:
case ResultType::Menu:
return "x";
case ResultType::Float2:
return "xy";
case ResultType::Float3:
return "xyz";
case ResultType::Color:
return "rgba";
case ResultType::Float4:
return "xyzw";
case ResultType::Float2:
/* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
return "xyz";
case ResultType::Color:
return "rgba";
case ResultType::Int:
return "x";
case ResultType::Int2:
/* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
return "xyz";
/* GPUMaterial doesn't support float2, so it is passed as a float2. */
return "xy";
case ResultType::Bool:
return "x";
case ResultType::Menu:
return "x";
case ResultType::String:
/* Single only types do not support GPU code path. */
BLI_assert(Result::is_single_value_only_type(type));

View File

@@ -0,0 +1,66 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/* The file contains the implementation of functions inserted by the GPUMatrial code generator,
* this mainly contains the [type]_from_[type] implicit conversion functions. */
float float_from_vec4(float4 value, float3 luminance_coefficients)
{
return dot(value.xyz(), luminance_coefficients);
}
float float_from_vec3(float3 value)
{
return dot(value, float3(1.0f)) / 3.0f;
}
float float_from_vec2(float2 value)
{
return dot(value, float2(1.0f)) / 2.0f;
}
float2 vec2_from_vec4(float4 value)
{
return value.xy;
}
float2 vec2_from_vec3(float3 value)
{
return value.xy;
}
float2 vec2_from_float(float value)
{
return float2(value);
}
float3 vec3_from_vec4(float4 value)
{
return value.xyz;
}
float3 vec3_from_vec2(float2 value)
{
return float3(value, 0.0f);
}
float3 vec3_from_float(float value)
{
return float3(value);
}
float4 vec4_from_vec2(float2 value)
{
return float4(value, 0.0f, 1.0f);
}
float4 vec4_from_vec3(float3 value)
{
return float4(value, 1.0f);
}
float4 vec4_from_float(float value)
{
return float4(float3(value), 1.0f);
}

View File

@@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
void set_float(float input_value, out float output_value)
{
output_value = input_value;
}
void set_float2(float2 input_value, out float2 output_value)
{
output_value = input_value;
}
void set_float3(float3 input_value, out float3 output_value)
{
output_value = input_value;
}
void set_float4(float4 input_value, out float4 output_value)
{
output_value = input_value;
}
void set_color(float4 input_value, out float4 output_value)
{
output_value = input_value;
}

View File

@@ -10,12 +10,12 @@
/* 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_float2(const uint id, vec2 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_color(const uint id, vec4 value) {}
* void store_int(const uint id, float value) {}
* void store_int2(const uint id, vec2 value) {}
* void store_bool(const uint id, float value) {}
* void store_menu(const uint id, float value) {}
*/

View File

@@ -19,10 +19,9 @@ void node_compositor_store_output_float(const float id, float value, out float o
out_value = value;
}
/* GPUMaterial doesn't support int, so it is passed as a float. */
void node_compositor_store_output_int(const float id, float value, out float out_value)
void node_compositor_store_output_float2(const float id, float2 value, out float2 out_value)
{
store_int(floatBitsToUint(id), value);
store_float2(floatBitsToUint(id), value);
out_value = value;
}
@@ -32,27 +31,27 @@ void node_compositor_store_output_float3(const float id, float3 value, out float
out_value = value;
}
void node_compositor_store_output_color(const float id, float4 value, out float4 out_value)
{
store_color(floatBitsToUint(id), value);
out_value = value;
}
void node_compositor_store_output_float4(const float id, float4 value, out float4 out_value)
{
store_float4(floatBitsToUint(id), value);
out_value = value;
}
/* GPUMaterial doesn't support float2, so it is passed as a float3 with z ignored. */
void node_compositor_store_output_float2(const float id, float3 value, out float3 out_value)
void node_compositor_store_output_color(const float id, float4 value, out float4 out_value)
{
store_float2(floatBitsToUint(id), value);
store_color(floatBitsToUint(id), value);
out_value = value;
}
/* GPUMaterial doesn't support int2, so it is passed as a float3 with z ignored. */
void node_compositor_store_output_int2(const float id, float3 value, out float3 out_value)
/* GPUMaterial doesn't support int, so it is passed as a float. */
void node_compositor_store_output_int(const float id, float value, out float out_value)
{
store_int(floatBitsToUint(id), value);
out_value = value;
}
/* GPUMaterial doesn't support int2, so it is passed as a float2. */
void node_compositor_store_output_int2(const float id, float2 value, out float2 out_value)
{
store_int2(floatBitsToUint(id), value);
out_value = value;

View File

@@ -313,69 +313,3 @@ int2 bool_to_int2(bool value)
{
return int2(value);
}
/* --------------------------------------------------------------------
* GPUMatrial-specific implicit conversion functions.
*
* Those should have the same interface and names as the macros in gpu_shader_codegen_lib.glsl
* since the GPUMaterial compiler inserts those hard coded names. */
float float_from_vec4(float4 vector, float3 luminance_coefficients)
{
return color_to_float(vector, luminance_coefficients);
}
float float_from_vec3(float3 vector)
{
return dot(vector, float3(1.0f)) / 3.0f;
}
float float_from_vec2(float2 vector)
{
return dot(vector, float2(1.0f)) / 2.0f;
}
float2 vec2_from_vec4(float4 vector)
{
return vector.xy;
}
float2 vec2_from_vec3(float3 vector)
{
return vector.xy;
}
float2 vec2_from_float(float value)
{
return float2(value);
}
float3 vec3_from_vec4(float4 vector)
{
return vector.xyz;
}
float3 vec3_from_vec2(float2 vector)
{
return float3(vector, 0.0f);
}
float3 vec3_from_float(float value)
{
return float3(value);
}
float4 vec4_from_vec2(float2 vector)
{
return float4(vector, 0.0f, 1.0f);
}
float4 vec4_from_vec3(float3 vector)
{
return float4(vector, 1.0f);
}
float4 vec4_from_float(float value)
{
return float4(float3(value), 1.0f);
}