This patch adds support for using integer sockets in compositor nodes. This involves updating the Result class, node tree compiler, implicit conversion operation, multi-function procedure operation, shader operation, and some operations that supports multiple types. Shader operation internally treats integers as floats, doing conversion to and from int when reading and writing. That's because the GPUMaterial compiler doesn't support integers. This is also the same workaround used by the shader system. Though the GPU module are eyeing adding support for integers, so we will update the code once they do that. Domain realization is not yet supported for integer types, but this is an internal limitation so far, as we do not plan to add nodes that outputs integers soon. We are not yet sure how realization should happen with regards to interpolation and we do not have base functions to sample integer images, that's why I decided to delay its implementation when it is actually needed. Pull Request: https://projects.blender.org/blender/blender/pulls/132599
374 lines
12 KiB
C++
374 lines
12 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "BLI_math_vector_types.hh"
|
|
|
|
#include "GPU_shader.hh"
|
|
|
|
#include "COM_context.hh"
|
|
#include "COM_conversion_operation.hh"
|
|
#include "COM_input_descriptor.hh"
|
|
#include "COM_result.hh"
|
|
#include "COM_utilities.hh"
|
|
#include "COM_utilities_type_conversion.hh"
|
|
|
|
namespace blender::compositor {
|
|
|
|
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 = this->get_result();
|
|
const Result &input = this->get_input();
|
|
|
|
if (input.is_single_value()) {
|
|
result.allocate_single_value();
|
|
this->execute_single(input, result);
|
|
return;
|
|
}
|
|
|
|
result.allocate_texture(input.domain());
|
|
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");
|
|
result.bind_as_image(shader, "output_img");
|
|
|
|
compute_dispatch_threads_at_least(shader, input.domain().size);
|
|
|
|
input.unbind_as_texture();
|
|
result.unbind_as_image();
|
|
GPU_shader_unbind();
|
|
}
|
|
else {
|
|
this->execute_cpu(input, result);
|
|
}
|
|
}
|
|
|
|
SimpleOperation *ConversionOperation::construct_if_needed(Context &context,
|
|
const Result &input_result,
|
|
const InputDescriptor &input_descriptor)
|
|
{
|
|
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);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const char *ConversionOperation::get_conversion_shader_name()
|
|
{
|
|
switch (this->get_input().type()) {
|
|
case ResultType::Float:
|
|
switch (this->get_result().type()) {
|
|
case ResultType::Int:
|
|
return "compositor_convert_float_to_int";
|
|
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;
|
|
}
|
|
break;
|
|
case ResultType::Int:
|
|
switch (this->get_result().type()) {
|
|
case ResultType::Float:
|
|
return "compositor_convert_int_to_float";
|
|
case ResultType::Vector:
|
|
return "compositor_convert_int_to_vector";
|
|
case ResultType::Color:
|
|
return "compositor_convert_int_to_color";
|
|
case ResultType::Int:
|
|
/* 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;
|
|
}
|
|
break;
|
|
case ResultType::Vector:
|
|
switch (this->get_result().type()) {
|
|
case ResultType::Float:
|
|
return "compositor_convert_vector_to_float";
|
|
case ResultType::Int:
|
|
return "compositor_convert_vector_to_int";
|
|
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;
|
|
}
|
|
break;
|
|
case ResultType::Color:
|
|
switch (this->get_result().type()) {
|
|
case ResultType::Float:
|
|
return "compositor_convert_color_to_float";
|
|
case ResultType::Int:
|
|
return "compositor_convert_color_to_int";
|
|
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;
|
|
}
|
|
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 ConversionOperation::execute_single(const Result &input, Result &output)
|
|
{
|
|
switch (this->get_input().type()) {
|
|
case ResultType::Float:
|
|
switch (this->get_result().type()) {
|
|
case ResultType::Int:
|
|
output.set_single_value(float_to_int(input.get_single_value<float>()));
|
|
return;
|
|
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;
|
|
}
|
|
break;
|
|
case ResultType::Int:
|
|
switch (this->get_result().type()) {
|
|
case ResultType::Float:
|
|
output.set_single_value(int_to_float(input.get_single_value<int>()));
|
|
return;
|
|
case ResultType::Vector:
|
|
output.set_single_value(int_to_vector(input.get_single_value<int>()));
|
|
return;
|
|
case ResultType::Color:
|
|
output.set_single_value(int_to_color(input.get_single_value<int>()));
|
|
return;
|
|
case ResultType::Int:
|
|
/* 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;
|
|
}
|
|
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::Int:
|
|
output.set_single_value(vector_to_int(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;
|
|
}
|
|
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::Int:
|
|
output.set_single_value(color_to_int(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;
|
|
}
|
|
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 ConversionOperation::execute_cpu(const Result &input, Result &output)
|
|
{
|
|
switch (this->get_input().type()) {
|
|
case ResultType::Float:
|
|
switch (this->get_result().type()) {
|
|
case ResultType::Int:
|
|
parallel_for(input.domain().size, [&](const int2 texel) {
|
|
output.store_pixel(texel, float_to_int(input.load_pixel<float>(texel)));
|
|
});
|
|
return;
|
|
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;
|
|
}
|
|
break;
|
|
case ResultType::Int:
|
|
switch (this->get_result().type()) {
|
|
case ResultType::Float:
|
|
parallel_for(input.domain().size, [&](const int2 texel) {
|
|
output.store_pixel(texel, int_to_float(input.load_pixel<int>(texel)));
|
|
});
|
|
return;
|
|
case ResultType::Vector:
|
|
parallel_for(input.domain().size, [&](const int2 texel) {
|
|
output.store_pixel(texel, int_to_vector(input.load_pixel<int>(texel)));
|
|
});
|
|
return;
|
|
case ResultType::Color:
|
|
parallel_for(input.domain().size, [&](const int2 texel) {
|
|
output.store_pixel(texel, int_to_color(input.load_pixel<int>(texel)));
|
|
});
|
|
return;
|
|
case ResultType::Int:
|
|
/* 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;
|
|
}
|
|
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::Int:
|
|
parallel_for(input.domain().size, [&](const int2 texel) {
|
|
output.store_pixel(texel, vector_to_int(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;
|
|
}
|
|
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::Int:
|
|
parallel_for(input.domain().size, [&](const int2 texel) {
|
|
output.store_pixel(texel, color_to_int(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;
|
|
}
|
|
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();
|
|
}
|
|
|
|
} // namespace blender::compositor
|