diff --git a/source/blender/compositor/COM_shader_node.hh b/source/blender/compositor/COM_shader_node.hh index e56f7c80a0e..332cdc410d3 100644 --- a/source/blender/compositor/COM_shader_node.hh +++ b/source/blender/compositor/COM_shader_node.hh @@ -24,10 +24,9 @@ using namespace nodes::derived_node_tree_types; * other shader nodes to construct a Shader Operation using the GPU material compiler. A GPU node * stack for each of the node inputs and outputs is stored and populated during construction in * order to represent the node as a GPU node inside the GPU material graph, see GPU_material.hh for - * more information. Derived classes should implement the compile method to add the node and link - * it to the GPU material given to the method. The compiler is expected to initialize the input - * links of the node before invoking the compile method. See the discussion in - * COM_shader_operation.hh for more information. */ + * more information. The compiler is expected to initialize the input links of the node inputs + * before invoking the compile method. See the discussion in COM_shader_operation.hh for more + * information. */ class ShaderNode { private: /* The node that this operation represents. */ diff --git a/source/blender/compositor/COM_shader_operation.hh b/source/blender/compositor/COM_shader_operation.hh index 398fe32336c..d98341cc2fb 100644 --- a/source/blender/compositor/COM_shader_operation.hh +++ b/source/blender/compositor/COM_shader_operation.hh @@ -109,13 +109,19 @@ class ShaderOperation : public PixelOperation { * operation, they are exposed as outputs to the shader operation itself. */ static void construct_material(void *thunk, GPUMaterial *material); - /* Link the inputs of the node if needed. Unlinked inputs are ignored as they will be linked by - * the node compile method. If the input is linked to a node that is not part of the shader - * operation, the input will be exposed as an input to the shader operation and linked to it. - * While if the input is linked to a node that is part of the shader operation, then it is linked - * to that node in the GPU material node graph. */ + /* Link the inputs of the node if needed. Unlinked inputs will be linked to constant values. If + * the input is linked to a node that is not part of the shader operation, the input will be + * exposed as an input to the shader operation and linked to it. While if the input is linked to + * a node that is part of the shader operation, then it is linked to that node in the GPU + * material node graph. */ void link_node_inputs(DNode node); + /* Link the GPU stack of the given unlinked input to a constant value setter GPU node that + * supplies the value of the unlinked input. The value us taken from the given origin input, + * which will be equal to the input in most cases, but can also be an unlinked input of a group + * node */ + void link_node_input_constant(const DInputSocket input, const DInputSocket origin); + /* Given the input socket of a node that is part of the shader operation which is linked to the * given output socket of a node that is also part of the shader operation, just link the output * link of the GPU node stack of the output socket to the input link of the GPU node stack of the diff --git a/source/blender/compositor/intern/shader_node.cc b/source/blender/compositor/intern/shader_node.cc index 8b64ac1847f..ebff55d1f9d 100644 --- a/source/blender/compositor/intern/shader_node.cc +++ b/source/blender/compositor/intern/shader_node.cc @@ -22,8 +22,8 @@ using namespace nodes::derived_node_tree_types; ShaderNode::ShaderNode(DNode node) : node_(node) { - populate_inputs(); - populate_outputs(); + this->populate_inputs(); + this->populate_outputs(); } void ShaderNode::compile(GPUMaterial *material) @@ -61,139 +61,20 @@ static eGPUType gpu_type_from_socket_type(eNodeSocketDatatype type) } } -/* Initializes the vector value of the given GPU node stack from the default value of the given - * socket. Note that the type of the stack may not match that of the socket, so perform implicit - * conversion if needed. */ -static void gpu_stack_vector_from_socket(GPUNodeStack &stack, const bNodeSocket *socket) -{ - const eNodeSocketDatatype input_type = static_cast(socket->type); - const eNodeSocketDatatype expected_type = static_cast(stack.sockettype); - - switch (input_type) { - case SOCK_FLOAT: { - const float value = socket->default_value_typed()->value; - switch (expected_type) { - case SOCK_FLOAT: - stack.vec[0] = value; - return; - case SOCK_INT: - /* GPUMaterial doesn't support int, so it is passed as a float. */ - stack.vec[0] = float(float_to_int(value)); - return; - case SOCK_VECTOR: - copy_v3_v3(stack.vec, float_to_float3(value)); - return; - case SOCK_RGBA: - copy_v4_v4(stack.vec, float_to_color(value)); - return; - default: - break; - } - break; - } - case SOCK_INT: { - const int value = socket->default_value_typed()->value; - switch (expected_type) { - case SOCK_FLOAT: - stack.vec[0] = int_to_float(value); - return; - case SOCK_INT: - /* GPUMaterial doesn't support int, so it is passed as a float. */ - stack.vec[0] = float(value); - return; - case SOCK_VECTOR: - copy_v3_v3(stack.vec, int_to_float3(value)); - return; - case SOCK_RGBA: - copy_v4_v4(stack.vec, int_to_color(value)); - return; - default: - break; - } - break; - } - case SOCK_VECTOR: { - const float3 value = float3(socket->default_value_typed()->value); - switch (expected_type) { - case SOCK_FLOAT: - stack.vec[0] = float3_to_float(value); - return; - case SOCK_INT: - /* GPUMaterial doesn't support int, so it is passed as a float. */ - stack.vec[0] = float(float3_to_int(value)); - return; - case SOCK_VECTOR: - copy_v3_v3(stack.vec, value); - return; - case SOCK_RGBA: - copy_v4_v4(stack.vec, float3_to_color(value)); - return; - default: - break; - } - break; - } - case SOCK_RGBA: { - const float4 value = socket->default_value_typed()->value; - switch (expected_type) { - case SOCK_FLOAT: - stack.vec[0] = color_to_float(value); - return; - case SOCK_INT: - /* GPUMaterial doesn't support int, so it is passed as a float. */ - stack.vec[0] = float(color_to_int(value)); - return; - case SOCK_VECTOR: - copy_v3_v3(stack.vec, color_to_float3(value)); - return; - case SOCK_RGBA: - copy_v4_v4(stack.vec, value); - return; - default: - break; - } - break; - } - default: - /* Unsupported sockets are skipped by GPU material compiler and we needn't initialize their - * value. This is flagged by using the GPU_NONE type, see gpu_type_from_socket_type function - * for more information. */ - break; - } -} - static void populate_gpu_node_stack(DSocket socket, GPUNodeStack &stack) { /* Make sure this stack is not marked as the end of the stack array. */ stack.end = false; /* This will be initialized later by the GPU material compiler or the compile method. */ stack.link = nullptr; + /* This will be initialized by the GPU material compiler if needed. */ + zero_v4(stack.vec); stack.sockettype = socket->type; - stack.type = gpu_type_from_socket_type((eNodeSocketDatatype)socket->type); + stack.type = gpu_type_from_socket_type(static_cast(socket->type)); - if (socket->is_input()) { - const DInputSocket input(socket); - - DSocket origin = get_input_origin_socket(input); - - /* The input is linked if the origin socket is an output socket. Had it been an input socket, - * then it is an unlinked input of a group input node. */ - stack.hasinput = origin->is_output(); - - /* Get the socket value from the origin if it is an input, because then it would either be an - * unlinked input or an unlinked input of a group input node that the socket is linked to, - * otherwise, get the value from the socket itself. */ - if (origin->is_input()) { - gpu_stack_vector_from_socket(stack, origin.bsocket()); - } - else { - gpu_stack_vector_from_socket(stack, socket.bsocket()); - } - } - else { - stack.hasoutput = socket->is_logically_linked(); - } + stack.hasinput = socket->is_logically_linked(); + stack.hasoutput = socket->is_logically_linked(); } void ShaderNode::populate_inputs() diff --git a/source/blender/compositor/intern/shader_operation.cc b/source/blender/compositor/intern/shader_operation.cc index 79c3292336a..3b50c168e05 100644 --- a/source/blender/compositor/intern/shader_operation.cc +++ b/source/blender/compositor/intern/shader_operation.cc @@ -128,30 +128,108 @@ void ShaderOperation::construct_material(void *thunk, GPUMaterial *material) void ShaderOperation::link_node_inputs(DNode node) { - for (const bNodeSocket *input : node->input_sockets()) { - const DInputSocket dinput{node.context(), input}; + for (int i = 0; i < node->input_sockets().size(); i++) { + const DInputSocket input{node.context(), node->input_sockets()[i]}; - /* Get the output linked to the input. If it is null, that means the input is unlinked. - * Unlinked inputs are linked by the node compile method, so skip this here. */ - const DOutputSocket doutput = get_output_linked_to_input(dinput); - if (!doutput) { + if (!input->is_available()) { continue; } + /* The origin socket is an input, that means the input is unlinked and we link a constant + * setter node for it. */ + const DSocket origin = get_input_origin_socket(input); + if (origin->is_input()) { + this->link_node_input_constant(input, DInputSocket(origin)); + continue; + } + + /* Otherwise, the origin socket is an output, which means it is linked. */ + const DOutputSocket output = DOutputSocket(origin); + /* If the origin node is part of the shader operation, then the link is internal to the GPU * material graph and is linked appropriately. */ - if (compile_unit_.contains(doutput.node())) { - link_node_input_internal(dinput, doutput); + if (compile_unit_.contains(output.node())) { + this->link_node_input_internal(input, output); continue; } /* Otherwise, the origin node is not part of the shader operation, then the link is external to * the GPU material graph and an input to the shader operation must be declared and linked to * the node input. */ - link_node_input_external(dinput, doutput); + this->link_node_input_external(input, output); } } +/* Initializes the vector value of the given GPU node stack from the default value of the given + * input socket. */ +static void initialize_input_stack_value(const DInputSocket input, GPUNodeStack &stack) +{ + switch (input->type) { + case SOCK_FLOAT: { + const float value = input->default_value_typed()->value; + stack.vec[0] = value; + break; + } + case SOCK_INT: { + /* GPUMaterial doesn't support int, so it is stored as a float. */ + const int value = input->default_value_typed()->value; + stack.vec[0] = int(value); + break; + } + case SOCK_VECTOR: { + const float3 value = float3(input->default_value_typed()->value); + copy_v3_v3(stack.vec, value); + break; + } + case SOCK_RGBA: { + const float4 value = float4(input->default_value_typed()->value); + copy_v4_v4(stack.vec, value); + break; + } + default: + BLI_assert_unreachable(); + break; + } +} + +static const char *get_set_function_name(const ResultType type) +{ + switch (type) { + case ResultType::Float: + return "set_value"; + case ResultType::Int: + /* GPUMaterial doesn't support int, 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: + case ResultType::Int2: + /* Those types are internal and needn't be handled by operations. */ + break; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::link_node_input_constant(const DInputSocket input, const DInputSocket origin) +{ + ShaderNode &node = *shader_nodes_.lookup(input.node()); + GPUNodeStack &stack = node.get_input(input->identifier); + + /* Create a uniform link that carry the value of the origin. */ + initialize_input_stack_value(origin, stack); + GPUNodeLink *link = GPU_uniform(stack.vec); + + const ResultType type = get_node_socket_result_type(origin.bsocket()); + const char *function_name = get_set_function_name(type); + GPU_link(material_, function_name, link, &stack.link); +} + void ShaderOperation::link_node_input_internal(DInputSocket input_socket, DOutputSocket output_socket) { @@ -196,30 +274,6 @@ void ShaderOperation::link_node_input_external(DInputSocket input_socket, stack.link = output_to_material_attribute_map_.lookup(output_socket); } -static const char *get_set_function_name(ResultType type) -{ - switch (type) { - case ResultType::Float: - return "set_value"; - case ResultType::Int: - /* GPUMaterial doesn't support int, 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: - case ResultType::Int2: - /* Those types are internal and needn't be handled by operations. */ - break; - } - - BLI_assert_unreachable(); - return nullptr; -} - void ShaderOperation::declare_operation_input(DInputSocket input_socket, DOutputSocket output_socket) {