Refactor: Compositor: Reduplicate type conversion code

This commit is contained in:
Omar Emara
2025-01-02 14:02:13 +02:00
parent b109f26e05
commit b6be52c2b2
5 changed files with 321 additions and 362 deletions

View File

@@ -137,6 +137,7 @@ set(SRC
cached_resources/COM_van_vliet_gaussian_coefficients.hh
utilities/COM_utilities_diagonals.hh
utilities/COM_utilities_type_conversion.hh
)
set(LIB

View File

@@ -4,8 +4,6 @@
#pragma once
#include "GPU_shader.hh"
#include "COM_context.hh"
#include "COM_input_descriptor.hh"
#include "COM_result.hh"
@@ -20,7 +18,9 @@ namespace blender::compositor {
* classes for more details. */
class ConversionOperation : public SimpleOperation {
public:
using SimpleOperation::SimpleOperation;
ConversionOperation(Context &context,
const ResultType input_type,
const ResultType expected_type);
/* If the input result is a single value, execute_single is called. Otherwise, the shader
* provided by get_conversion_shader is dispatched for GPU contexts or execute_cpu is called for
@@ -34,111 +34,15 @@ class ConversionOperation : public SimpleOperation {
const Result &input_result,
const InputDescriptor &input_descriptor);
protected:
private:
/* Convert the input single value result to the output single value result. */
virtual void execute_single(const Result &input, Result &output) = 0;
void execute_single(const Result &input, Result &output);
/* Convert the input to the appropriate type and write the result to the output on the CPU. */
virtual void execute_cpu(const Result &input, Result &output) = 0;
void execute_cpu(const Result &input, Result &output);
/* Get the shader the will be used for conversion. */
virtual GPUShader *get_conversion_shader() const = 0;
};
/* --------------------------------------------------------------------
* Convert Float to Vector Operation
*
* Takes a float result and outputs a vector result. The first three components of the output are
* filled with the input float, while the fourth component is set to 1. */
class ConvertFloatToVectorOperation : public ConversionOperation {
public:
ConvertFloatToVectorOperation(Context &context);
void execute_single(const Result &input, Result &output) override;
void execute_cpu(const Result &input, Result &output) override;
GPUShader *get_conversion_shader() const override;
};
/* --------------------------------------------------------------------
* Convert Float to Color Operation
*
* Takes a float result and outputs a color result. All three color channels of the output are
* filled with the input float and the alpha channel is set to 1. */
class ConvertFloatToColorOperation : public ConversionOperation {
public:
ConvertFloatToColorOperation(Context &context);
void execute_single(const Result &input, Result &output) override;
void execute_cpu(const Result &input, Result &output) override;
GPUShader *get_conversion_shader() const override;
};
/* --------------------------------------------------------------------
* Convert Color to Float Operation
*
* Takes a color result and outputs a float result. The output is the average of the three color
* channels, the alpha channel is ignored. */
class ConvertColorToFloatOperation : public ConversionOperation {
public:
ConvertColorToFloatOperation(Context &context);
void execute_single(const Result &input, Result &output) override;
void execute_cpu(const Result &input, Result &output) override;
GPUShader *get_conversion_shader() const override;
};
/* --------------------------------------------------------------------
* Convert Color to Vector Operation
*
* Takes a color result and outputs a vector result. The output is an exact copy of the input since
* vectors are 4D. */
class ConvertColorToVectorOperation : public ConversionOperation {
public:
ConvertColorToVectorOperation(Context &context);
void execute_single(const Result &input, Result &output) override;
void execute_cpu(const Result &input, Result &output) override;
GPUShader *get_conversion_shader() const override;
};
/* --------------------------------------------------------------------
* Convert Vector to Float Operation
*
* Takes a vector result and outputs a float result. The output is the average of the three
*components. */
class ConvertVectorToFloatOperation : public ConversionOperation {
public:
ConvertVectorToFloatOperation(Context &context);
void execute_single(const Result &input, Result &output) override;
void execute_cpu(const Result &input, Result &output) override;
GPUShader *get_conversion_shader() const override;
};
/* --------------------------------------------------------------------
* Convert Vector to Color Operation
*
* Takes a vector result and outputs a color result. The output is a copy of the three vector
* components to the three color channels with the alpha channel set to 1. */
class ConvertVectorToColorOperation : public ConversionOperation {
public:
ConvertVectorToColorOperation(Context &context);
void execute_single(const Result &input, Result &output) override;
void execute_cpu(const Result &input, Result &output) override;
GPUShader *get_conversion_shader() const override;
/* Get the name of the shader the will be used for conversion. */
const char *get_conversion_shader_name();
};
} // namespace blender::compositor

View File

@@ -11,17 +11,23 @@
#include "COM_input_descriptor.hh"
#include "COM_result.hh"
#include "COM_utilities.hh"
#include "COM_utilities_type_conversion.hh"
namespace blender::compositor {
/* --------------------------------------------------------------------
* Conversion Operation
*/
ConversionOperation::ConversionOperation(Context &context,
const ResultType input_type,
const ResultType expected_type)
: SimpleOperation(context)
{
this->declare_input_descriptor(InputDescriptor{input_type});
this->populate_result(context.create_result(expected_type));
}
void ConversionOperation::execute()
{
Result &result = get_result();
const Result &input = get_input();
Result &result = this->get_result();
const Result &input = this->get_input();
if (input.is_single_value()) {
result.allocate_single_value();
@@ -30,8 +36,8 @@ void ConversionOperation::execute()
}
result.allocate_texture(input.domain());
if (context().use_gpu()) {
GPUShader *shader = get_conversion_shader();
if (this->context().use_gpu()) {
GPUShader *shader = this->context().get_shader(this->get_conversion_shader_name());
GPU_shader_bind(shader);
input.bind_as_texture(shader, "input_tx");
@@ -52,222 +58,211 @@ SimpleOperation *ConversionOperation::construct_if_needed(Context &context,
const Result &input_result,
const InputDescriptor &input_descriptor)
{
ResultType result_type = input_result.type();
ResultType expected_type = input_descriptor.type;
/* If the result type differs from the expected type, return an instance of an appropriate
* conversion operation. Otherwise, return a null pointer. */
if (result_type == ResultType::Float && expected_type == ResultType::Vector) {
return new ConvertFloatToVectorOperation(context);
const ResultType result_type = input_result.type();
const ResultType expected_type = input_descriptor.type;
if (result_type != expected_type) {
return new ConversionOperation(context, result_type, expected_type);
}
if (result_type == ResultType::Float && expected_type == ResultType::Color) {
return new ConvertFloatToColorOperation(context);
}
if (result_type == ResultType::Color && expected_type == ResultType::Float) {
return new ConvertColorToFloatOperation(context);
}
if (result_type == ResultType::Color && expected_type == ResultType::Vector) {
return new ConvertColorToVectorOperation(context);
}
if (result_type == ResultType::Vector && expected_type == ResultType::Float) {
return new ConvertVectorToFloatOperation(context);
}
if (result_type == ResultType::Vector && expected_type == ResultType::Color) {
return new ConvertVectorToColorOperation(context);
}
return nullptr;
}
/* --------------------------------------------------------------------
* Convert Float to Vector Operation
*/
ConvertFloatToVectorOperation::ConvertFloatToVectorOperation(Context &context)
: ConversionOperation(context)
const char *ConversionOperation::get_conversion_shader_name()
{
InputDescriptor input_descriptor;
input_descriptor.type = ResultType::Float;
declare_input_descriptor(input_descriptor);
populate_result(context.create_result(ResultType::Vector));
switch (this->get_input().type()) {
case ResultType::Float:
switch (this->get_result().type()) {
case ResultType::Vector:
return "compositor_convert_float_to_vector";
case ResultType::Color:
return "compositor_convert_float_to_color";
case ResultType::Float:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Vector:
switch (this->get_result().type()) {
case ResultType::Float:
return "compositor_convert_vector_to_float";
case ResultType::Color:
return "compositor_convert_vector_to_color";
case ResultType::Vector:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Color:
switch (this->get_result().type()) {
case ResultType::Float:
return "compositor_convert_color_to_float";
case ResultType::Vector:
return "compositor_convert_color_to_vector";
case ResultType::Color:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
BLI_assert_unreachable();
return nullptr;
}
void ConvertFloatToVectorOperation::execute_single(const Result &input, Result &output)
void ConversionOperation::execute_single(const Result &input, Result &output)
{
output.set_single_value(float4(float3(input.get_single_value<float>()), 1.0f));
switch (this->get_input().type()) {
case ResultType::Float:
switch (this->get_result().type()) {
case ResultType::Vector:
output.set_single_value(float_to_vector(input.get_single_value<float>()));
return;
case ResultType::Color:
output.set_single_value(float_to_color(input.get_single_value<float>()));
return;
case ResultType::Float:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Vector:
switch (this->get_result().type()) {
case ResultType::Float:
output.set_single_value(vector_to_float(input.get_single_value<float4>()));
return;
case ResultType::Color:
output.set_single_value(vector_to_color(input.get_single_value<float4>()));
return;
case ResultType::Vector:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Color:
switch (this->get_result().type()) {
case ResultType::Float:
output.set_single_value(color_to_float(input.get_single_value<float4>()));
return;
case ResultType::Vector:
output.set_single_value(color_to_vector(input.get_single_value<float4>()));
return;
case ResultType::Color:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
BLI_assert_unreachable();
}
void ConvertFloatToVectorOperation::execute_cpu(const Result &input, Result &output)
void ConversionOperation::execute_cpu(const Result &input, Result &output)
{
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, float4(float3(input.load_pixel<float>(texel)), 1.0f));
});
}
switch (this->get_input().type()) {
case ResultType::Float:
switch (this->get_result().type()) {
case ResultType::Vector:
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, float_to_vector(input.load_pixel<float>(texel)));
});
return;
case ResultType::Color:
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, float_to_color(input.load_pixel<float>(texel)));
});
return;
case ResultType::Float:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Vector:
switch (this->get_result().type()) {
case ResultType::Float:
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, vector_to_float(input.load_pixel<float4>(texel)));
});
return;
case ResultType::Color:
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, vector_to_color(input.load_pixel<float4>(texel)));
});
return;
case ResultType::Vector:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Color:
switch (this->get_result().type()) {
case ResultType::Float:
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, color_to_float(input.load_pixel<float4>(texel)));
});
return;
case ResultType::Vector:
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, color_to_vector(input.load_pixel<float4>(texel)));
});
return;
case ResultType::Color:
/* Same type, no conversion needed. */
break;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
GPUShader *ConvertFloatToVectorOperation::get_conversion_shader() const
{
return context().get_shader("compositor_convert_float_to_vector");
}
/* --------------------------------------------------------------------
* Convert Float to Color Operation
*/
ConvertFloatToColorOperation::ConvertFloatToColorOperation(Context &context)
: ConversionOperation(context)
{
InputDescriptor input_descriptor;
input_descriptor.type = ResultType::Float;
declare_input_descriptor(input_descriptor);
populate_result(context.create_result(ResultType::Color));
}
void ConvertFloatToColorOperation::execute_single(const Result &input, Result &output)
{
output.set_single_value(float4(float3(input.get_single_value<float>()), 1.0f));
}
void ConvertFloatToColorOperation::execute_cpu(const Result &input, Result &output)
{
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, float4(float3(input.load_pixel<float>(texel)), 1.0f));
});
}
GPUShader *ConvertFloatToColorOperation::get_conversion_shader() const
{
return context().get_shader("compositor_convert_float_to_color");
}
/* --------------------------------------------------------------------
* Convert Color to Float Operation
*/
ConvertColorToFloatOperation::ConvertColorToFloatOperation(Context &context)
: ConversionOperation(context)
{
InputDescriptor input_descriptor;
input_descriptor.type = ResultType::Color;
declare_input_descriptor(input_descriptor);
populate_result(context.create_result(ResultType::Float));
}
void ConvertColorToFloatOperation::execute_single(const Result &input, Result &output)
{
float4 color = input.get_single_value<float4>();
output.set_single_value((color.x + color.y + color.z) / 3.0f);
}
void ConvertColorToFloatOperation::execute_cpu(const Result &input, Result &output)
{
parallel_for(input.domain().size, [&](const int2 texel) {
const float4 color = input.load_pixel<float4>(texel);
output.store_pixel(texel, (color.x + color.y + color.z) / 3.0f);
});
}
GPUShader *ConvertColorToFloatOperation::get_conversion_shader() const
{
return context().get_shader("compositor_convert_color_to_float");
}
/* --------------------------------------------------------------------
* Convert Color to Vector Operation
*/
ConvertColorToVectorOperation::ConvertColorToVectorOperation(Context &context)
: ConversionOperation(context)
{
InputDescriptor input_descriptor;
input_descriptor.type = ResultType::Color;
declare_input_descriptor(input_descriptor);
populate_result(context.create_result(ResultType::Vector));
}
void ConvertColorToVectorOperation::execute_single(const Result &input, Result &output)
{
float4 color = input.get_single_value<float4>();
output.set_single_value(color);
}
void ConvertColorToVectorOperation::execute_cpu(const Result &input, Result &output)
{
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, input.load_pixel<float4>(texel));
});
}
GPUShader *ConvertColorToVectorOperation::get_conversion_shader() const
{
return context().get_shader("compositor_convert_color_to_vector");
}
/* --------------------------------------------------------------------
* Convert Vector to Float Operation
*/
ConvertVectorToFloatOperation::ConvertVectorToFloatOperation(Context &context)
: ConversionOperation(context)
{
InputDescriptor input_descriptor;
input_descriptor.type = ResultType::Vector;
declare_input_descriptor(input_descriptor);
populate_result(context.create_result(ResultType::Float));
}
void ConvertVectorToFloatOperation::execute_single(const Result &input, Result &output)
{
float4 vector = input.get_single_value<float4>();
output.set_single_value((vector[0] + vector[1] + vector[2]) / 3.0f);
}
void ConvertVectorToFloatOperation::execute_cpu(const Result &input, Result &output)
{
parallel_for(input.domain().size, [&](const int2 texel) {
const float4 vector = input.load_pixel<float4>(texel);
output.store_pixel(texel, (vector.x + vector.y + vector.z) / 3.0f);
});
}
GPUShader *ConvertVectorToFloatOperation::get_conversion_shader() const
{
return context().get_shader("compositor_convert_vector_to_float");
}
/* --------------------------------------------------------------------
* Convert Vector to Color Operation
*/
ConvertVectorToColorOperation::ConvertVectorToColorOperation(Context &context)
: ConversionOperation(context)
{
InputDescriptor input_descriptor;
input_descriptor.type = ResultType::Vector;
declare_input_descriptor(input_descriptor);
populate_result(context.create_result(ResultType::Color));
}
void ConvertVectorToColorOperation::execute_single(const Result &input, Result &output)
{
output.set_single_value(float4(float3(input.get_single_value<float4>()), 1.0f));
}
void ConvertVectorToColorOperation::execute_cpu(const Result &input, Result &output)
{
parallel_for(input.domain().size, [&](const int2 texel) {
output.store_pixel(texel, float4(input.load_pixel<float4>(texel).xyz(), 1.0f));
});
}
GPUShader *ConvertVectorToColorOperation::get_conversion_shader() const
{
return context().get_shader("compositor_convert_vector_to_color");
BLI_assert_unreachable();
}
} // namespace blender::compositor

View File

@@ -38,6 +38,7 @@
#include "COM_result.hh"
#include "COM_scheduler.hh"
#include "COM_utilities.hh"
#include "COM_utilities_type_conversion.hh"
namespace blender::compositor {
@@ -290,54 +291,70 @@ mf::Variable *MultiFunctionProcedureOperation::get_multi_function_input_variable
static mf::MultiFunction *get_conversion_function(const ResultType variable_type,
const ResultType expected_type)
{
/* No conversion needed. */
if (expected_type == variable_type) {
return nullptr;
}
static auto float_to_vector_function = mf::build::SI1_SO<float, float4>(
"Float To Vector", float_to_vector, mf::build::exec_presets::AllSpanOrSingle());
static auto float_to_color_function = mf::build::SI1_SO<float, float4>(
"Float To Color", float_to_color, mf::build::exec_presets::AllSpanOrSingle());
static auto vector_to_float_function = mf::build::SI1_SO<float4, float>(
"Vector To Float", vector_to_float, mf::build::exec_presets::AllSpanOrSingle());
static auto vector_to_color_function = mf::build::SI1_SO<float4, float4>(
"Vector To Color", vector_to_color, mf::build::exec_presets::AllSpanOrSingle());
static auto color_to_float_function = mf::build::SI1_SO<float4, float>(
"Color To Float", color_to_float, mf::build::exec_presets::AllSpanOrSingle());
static auto color_to_vector_function = mf::build::SI1_SO<float4, float4>(
"Color To Vector", color_to_vector, mf::build::exec_presets::AllSpanOrSingle());
if (variable_type == ResultType::Float && expected_type == ResultType::Vector) {
static auto float_to_vector_function = mf::build::SI1_SO<float, float4>(
"Float To Vector",
[](const float &input) -> float4 { return float4(float3(input), 1.0f); },
mf::build::exec_presets::AllSpanOrSingle());
return &float_to_vector_function;
}
if (variable_type == ResultType::Float && expected_type == ResultType::Color) {
static auto float_to_color_function = mf::build::SI1_SO<float, float4>(
"Float To Color",
[](const float &input) -> float4 { return float4(float3(input), 1.0f); },
mf::build::exec_presets::AllSpanOrSingle());
return &float_to_color_function;
}
if (variable_type == ResultType::Vector && expected_type == ResultType::Float) {
static auto vector_to_float_function = mf::build::SI1_SO<float4, float>(
"Vector To Float",
[](const float4 &input) -> float { return (input.x + input.y + input.z) / 3.0f; },
mf::build::exec_presets::AllSpanOrSingle());
return &vector_to_float_function;
}
if (variable_type == ResultType::Vector && expected_type == ResultType::Color) {
static auto vector_to_color_function = mf::build::SI1_SO<float4, float4>(
"Vector To Color",
[](const float4 &input) -> float4 { return float4(input.xyz(), 1.0f); },
mf::build::exec_presets::AllSpanOrSingle());
return &vector_to_color_function;
}
if (variable_type == ResultType::Color && expected_type == ResultType::Float) {
static auto color_to_float_function = mf::build::SI1_SO<float4, float>(
"Color To Float",
[](const float4 &input) -> float { return (input.x + input.y + input.z) / 3.0f; },
mf::build::exec_presets::AllSpanOrSingle());
return &color_to_float_function;
}
if (variable_type == ResultType::Color && expected_type == ResultType::Vector) {
/* No conversion needed. */
return nullptr;
switch (variable_type) {
case ResultType::Float:
switch (expected_type) {
case ResultType::Vector:
return &float_to_vector_function;
case ResultType::Color:
return &float_to_color_function;
case ResultType::Float:
/* Same type, no conversion needed. */
return nullptr;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Vector:
switch (expected_type) {
case ResultType::Float:
return &vector_to_float_function;
case ResultType::Color:
return &vector_to_color_function;
case ResultType::Vector:
/* Same type, no conversion needed. */
return nullptr;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Color:
switch (expected_type) {
case ResultType::Float:
return &color_to_float_function;
case ResultType::Vector:
return &color_to_vector_function;
case ResultType::Color:
/* Same type, no conversion needed. */
return nullptr;
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
case ResultType::Float2:
case ResultType::Float3:
case ResultType::Int2:
/* Types are not user facing, so we needn't implement them. */
break;
}
BLI_assert_unreachable();

View File

@@ -0,0 +1,42 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_math_vector.hh"
#include "BLI_math_vector_types.hh"
namespace blender::compositor {
inline float4 float_to_vector(const float &input)
{
return float4(float3(input), 1.0f);
}
inline float4 float_to_color(const float &input)
{
return float4(float3(input), 1.0f);
}
inline float vector_to_float(const float4 &input)
{
return math::reduce_add(input.xyz()) / 3.0f;
}
inline float4 vector_to_color(const float4 &input)
{
return float4(input.xyz(), 1.0f);
}
inline float color_to_float(const float4 &input)
{
return math::reduce_add(input.xyz()) / 3.0f;
}
inline float4 color_to_vector(const float4 &input)
{
return input;
}
} // namespace blender::compositor