diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index c3f287175f1..e13dc889ab8 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -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 diff --git a/source/blender/compositor/COM_conversion_operation.hh b/source/blender/compositor/COM_conversion_operation.hh index f50daf13834..f96aa37911f 100644 --- a/source/blender/compositor/COM_conversion_operation.hh +++ b/source/blender/compositor/COM_conversion_operation.hh @@ -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 diff --git a/source/blender/compositor/intern/conversion_operation.cc b/source/blender/compositor/intern/conversion_operation.cc index d7d6a99fae1..848663d6949 100644 --- a/source/blender/compositor/intern/conversion_operation.cc +++ b/source/blender/compositor/intern/conversion_operation.cc @@ -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()), 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())); + return; + case ResultType::Color: + output.set_single_value(float_to_color(input.get_single_value())); + 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())); + return; + case ResultType::Color: + output.set_single_value(vector_to_color(input.get_single_value())); + 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())); + return; + case ResultType::Vector: + output.set_single_value(color_to_vector(input.get_single_value())); + 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(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(texel))); + }); + return; + case ResultType::Color: + parallel_for(input.domain().size, [&](const int2 texel) { + output.store_pixel(texel, float_to_color(input.load_pixel(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(texel))); + }); + return; + case ResultType::Color: + parallel_for(input.domain().size, [&](const int2 texel) { + output.store_pixel(texel, vector_to_color(input.load_pixel(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(texel))); + }); + return; + case ResultType::Vector: + parallel_for(input.domain().size, [&](const int2 texel) { + output.store_pixel(texel, color_to_vector(input.load_pixel(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()), 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(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(); - 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(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(); - 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(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(); - 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(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()), 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(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 diff --git a/source/blender/compositor/intern/multi_function_procedure_operation.cc b/source/blender/compositor/intern/multi_function_procedure_operation.cc index 98817fb48f9..9651d0d3eeb 100644 --- a/source/blender/compositor/intern/multi_function_procedure_operation.cc +++ b/source/blender/compositor/intern/multi_function_procedure_operation.cc @@ -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 To Vector", float_to_vector, mf::build::exec_presets::AllSpanOrSingle()); + static auto float_to_color_function = mf::build::SI1_SO( + "Float To Color", float_to_color, mf::build::exec_presets::AllSpanOrSingle()); + static auto vector_to_float_function = mf::build::SI1_SO( + "Vector To Float", vector_to_float, mf::build::exec_presets::AllSpanOrSingle()); + static auto vector_to_color_function = mf::build::SI1_SO( + "Vector To Color", vector_to_color, mf::build::exec_presets::AllSpanOrSingle()); + static auto color_to_float_function = mf::build::SI1_SO( + "Color To Float", color_to_float, mf::build::exec_presets::AllSpanOrSingle()); + static auto color_to_vector_function = mf::build::SI1_SO( + "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 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 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( - "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( - "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( - "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(); diff --git a/source/blender/compositor/utilities/COM_utilities_type_conversion.hh b/source/blender/compositor/utilities/COM_utilities_type_conversion.hh new file mode 100644 index 00000000000..d0ceda2648c --- /dev/null +++ b/source/blender/compositor/utilities/COM_utilities_type_conversion.hh @@ -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