diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 771af7b90e0..44b04f74e2f 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -25,6 +25,7 @@ set(SRC COM_derived_resources.hh COM_domain.hh COM_evaluator.hh + COM_implicit_input_operation.hh COM_input_descriptor.hh COM_input_single_value_operation.hh COM_meta_data.hh @@ -49,6 +50,7 @@ set(SRC intern/conversion_operation.cc intern/domain.cc intern/evaluator.cc + intern/implicit_input_operation.cc intern/input_single_value_operation.cc intern/meta_data.cc intern/multi_function_procedure_operation.cc diff --git a/source/blender/compositor/COM_compile_state.hh b/source/blender/compositor/COM_compile_state.hh index ba5366e51d8..60e58b44034 100644 --- a/source/blender/compositor/COM_compile_state.hh +++ b/source/blender/compositor/COM_compile_state.hh @@ -10,6 +10,7 @@ #include "NOD_derived_node_tree.hh" +#include "COM_context.hh" #include "COM_domain.hh" #include "COM_node_operation.hh" #include "COM_pixel_operation.hh" @@ -119,6 +120,8 @@ using namespace nodes::derived_node_tree_types; * compiled. */ class CompileState { private: + /* A reference to the compositor context. */ + const Context &context_; /* A reference to the node execution schedule that is being compiled. */ const Schedule &schedule_; /* Those two maps associate each node with the operation it was compiled into. Each node is @@ -141,7 +144,7 @@ class CompileState { public: /* Construct a compile state from the node execution schedule being compiled. */ - CompileState(const Schedule &schedule); + CompileState(const Context &context, const Schedule &schedule); /* Get a reference to the node execution schedule being compiled. */ const Schedule &get_schedule(); diff --git a/source/blender/compositor/COM_implicit_input_operation.hh b/source/blender/compositor/COM_implicit_input_operation.hh new file mode 100644 index 00000000000..9dcd1df2c41 --- /dev/null +++ b/source/blender/compositor/COM_implicit_input_operation.hh @@ -0,0 +1,37 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_operation.hh" +#include "COM_result.hh" + +namespace blender::compositor { + +/* ------------------------------------------------------------------------------------------------ + * Implicit Input Operation + * + * An operation that outputs a result representing a specific implicit input. */ +class ImplicitInputOperation : public Operation { + private: + /* The identifier of the output. */ + static const StringRef output_identifier_; + /* The type of implicit input needed. */ + ImplicitInput implicit_input_; + + public: + ImplicitInputOperation(Context &context, const ImplicitInput implicit_input); + + void execute() override; + + /* Get a reference to the output result of the operation, this essentially calls the super + * get_result with the output identifier of the operation. */ + Result &get_result(); +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/COM_input_descriptor.hh b/source/blender/compositor/COM_input_descriptor.hh index 6e13be7247d..37fd0c1d61a 100644 --- a/source/blender/compositor/COM_input_descriptor.hh +++ b/source/blender/compositor/COM_input_descriptor.hh @@ -25,6 +25,18 @@ enum class InputRealizationMode : uint8_t { OperationDomain, }; +/* ------------------------------------------------------------------------------------------------ + * Implicit Input + * + * Specifies the implicit input that should be assigned to the input if it is unlinked. See the + * ImplicitInputOperation operation for more information on the individual types. */ +enum class ImplicitInput : uint8_t { + /* The input does not have an implicit input and its value should be used. */ + None, + /* The input should have the texture coordinates of the compositing space as an input. */ + TextureCoordinates, +}; + /* ------------------------------------------------------------------------------------------------ * Input Descriptor * @@ -37,6 +49,8 @@ class InputDescriptor { ResultType type; /* Specify how the input should be realized. */ InputRealizationMode realization_mode = InputRealizationMode::OperationDomain; + /* Specifies the type of implicit input in case the input in unlinked. */ + ImplicitInput implicit_input = ImplicitInput::None; /* The priority of the input for determining the operation domain. The non-single value input * with the highest priority will be used to infer the operation domain, the highest priority * being zero. See the discussion in COM_domain.hh for more information. */ diff --git a/source/blender/compositor/COM_multi_function_procedure_operation.hh b/source/blender/compositor/COM_multi_function_procedure_operation.hh index cff37f3a93c..abba7d3d489 100644 --- a/source/blender/compositor/COM_multi_function_procedure_operation.hh +++ b/source/blender/compositor/COM_multi_function_procedure_operation.hh @@ -18,6 +18,7 @@ #include "NOD_multi_function.hh" #include "COM_context.hh" +#include "COM_input_descriptor.hh" #include "COM_pixel_operation.hh" #include "COM_scheduler.hh" @@ -44,6 +45,8 @@ class MultiFunctionProcedureOperation : public PixelOperation { /* A map that associates the output sockets of each node to the variables that were created for * them. */ Map output_to_variable_map_; + /* A map that associates implicit inputs to the variables that were created for them. */ + Map implicit_input_to_variable_map_; /* A vector that stores the intermediate variables that were implicitly created for the procedure * but are not associated with a node output. Those variables are for such multi-functions like * constant inputs and implicit conversion. */ @@ -82,6 +85,12 @@ class MultiFunctionProcedureOperation : public PixelOperation { * of the given input socket. */ mf::Variable *get_constant_input_variable(DInputSocket input); + /* Given an unlinked input with an implicit input. Declare an input to the operation/procedure + * for that implicit input if not done already and return a variable that represent that implicit + * input. The implicit input and type are 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 */ + mf::Variable *get_implicit_input_variable(const DInputSocket input, const DInputSocket origin); + /* Given an input in a node that is part of the compile unit that is connected to an output that * is in a non that is not part of the compile unit. Declare an input to the operation/procedure * for that output if not done already and return a variable that represent that input. */ diff --git a/source/blender/compositor/COM_pixel_operation.hh b/source/blender/compositor/COM_pixel_operation.hh index 2dffec71929..f355ab20661 100644 --- a/source/blender/compositor/COM_pixel_operation.hh +++ b/source/blender/compositor/COM_pixel_operation.hh @@ -77,6 +77,9 @@ class PixelOperation : public Operation { /* A map that associates the output socket of a node that is not part of the pixel operation to * the identifier of the input of the operation that was declared for it. */ Map outputs_to_declared_inputs_map_; + /* A map that associates each of the needed implicit inputs with the identifiers of the inputs of + * the operation that were declared for them. */ + Map implicit_inputs_to_input_identifiers_map_; /* A map that associates the identifier of each input of the operation with the number of node * inputs that use it, that is, its reference count. This is needed to correct the reference * counts of results linked to the inputs of the operation, since the results that provide the @@ -121,6 +124,11 @@ class PixelOperation : public Operation { * input mapping. See inputs_to_linked_outputs_map_ for more information. */ Map &get_inputs_to_linked_outputs_map(); + /* Get a reference to the implicit inputs to input identifiers map of the operation. This is + * called by the compiler to link the operations inputs with their corresponding implicit input + * results. See implicit_inputs_to_input_identifiers_map_ for more information. */ + Map &get_implicit_inputs_to_input_identifiers_map(); + /* Returns the internal reference count of the operation input with the given identifier. See the * inputs_to_reference_counts_map_ member for more information. */ int get_internal_input_reference_count(const StringRef &identifier); diff --git a/source/blender/compositor/COM_shader_operation.hh b/source/blender/compositor/COM_shader_operation.hh index d98341cc2fb..c1f985b749e 100644 --- a/source/blender/compositor/COM_shader_operation.hh +++ b/source/blender/compositor/COM_shader_operation.hh @@ -16,6 +16,7 @@ #include "NOD_derived_node_tree.hh" #include "COM_context.hh" +#include "COM_input_descriptor.hh" #include "COM_pixel_operation.hh" #include "COM_scheduler.hh" #include "COM_shader_node.hh" @@ -64,6 +65,8 @@ class ShaderOperation : public PixelOperation { * the attribute that was created for it. This is used to share the same attribute with all * inputs that are linked to the same output socket. */ Map output_to_material_attribute_map_; + /* A map that associates implicit inputs to the attributes that were created for them. */ + Map implicit_input_to_material_attribute_map_; public: /* Construct and compile a GPU material from the given shader compile unit and execution schedule @@ -117,11 +120,17 @@ class ShaderOperation : public PixelOperation { 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, + * supplies the value of the unlinked input. The value is 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 */ + * node. */ void link_node_input_constant(const DInputSocket input, const DInputSocket origin); + /* Given an unlinked input with an implicit input. Declare a new input to the operation for that + * implicit input if not done already and link it to the input link of the GPU node stack of the + * input socket. The implicit input and type are 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_implicit(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/compile_state.cc b/source/blender/compositor/intern/compile_state.cc index e0c12162f68..dd3787924a9 100644 --- a/source/blender/compositor/intern/compile_state.cc +++ b/source/blender/compositor/intern/compile_state.cc @@ -11,6 +11,7 @@ #include "NOD_derived_node_tree.hh" #include "COM_compile_state.hh" +#include "COM_context.hh" #include "COM_domain.hh" #include "COM_input_descriptor.hh" #include "COM_node_operation.hh" @@ -23,7 +24,10 @@ namespace blender::compositor { using namespace nodes::derived_node_tree_types; -CompileState::CompileState(const Schedule &schedule) : schedule_(schedule) {} +CompileState::CompileState(const Context &context, const Schedule &schedule) + : context_(context), schedule_(schedule) +{ +} const Schedule &CompileState::get_schedule() { @@ -156,20 +160,31 @@ int CompileState::compute_pixel_node_operation_outputs_count(DNode node) bool CompileState::is_pixel_node_single_value(DNode node) { /* The pixel node is single value when all of its inputs are single values. */ - 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]}; if (!input->is_available()) { continue; } - /* Get the output linked to the input. If it is null, that means the input is unlinked, and is - * thus single value. */ - const DOutputSocket output = get_output_linked_to_input(dinput); - if (!output) { - continue; + /* The origin socket is an input, that means the input is unlinked. */ + const DSocket origin = get_input_origin_socket(input); + if (origin->is_input()) { + const InputDescriptor origin_descriptor = input_descriptor_from_input_socket( + origin.bsocket()); + + /* The input does not have an implicit input, so it is a single value. */ + if (origin_descriptor.implicit_input == ImplicitInput::None) { + continue; + } + + /* Otherwise, it has an implicit input, which is never a single value. */ + return false; } + /* Otherwise, the origin socket is an output, which means it is linked. */ + const DOutputSocket output = DOutputSocket(origin); + /* If the output belongs to a node that is part of the pixel compile unit and that compile unit * is not single value, then the node is not single value. */ if (pixel_compile_unit_.contains(output.node())) { @@ -197,21 +212,39 @@ Domain CompileState::compute_pixel_node_domain(DNode node) /* Go over the inputs and find the domain of the non single value input with the highest domain * priority. */ - 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]}; if (!input->is_available()) { continue; } - /* Get the output linked to the input. If it is null, that means the input is unlinked, so skip - * it. */ - const DOutputSocket output = get_output_linked_to_input(dinput); - if (!output) { + const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input.bsocket()); + + /* The origin socket is an input, that means the input is unlinked. */ + const DSocket origin = get_input_origin_socket(input); + if (origin->is_input()) { + const InputDescriptor origin_descriptor = input_descriptor_from_input_socket( + origin.bsocket()); + + /* The input does not have an implicit input, so it is a single that can't be a domain input + * and we skip it. */ + if (origin_descriptor.implicit_input == ImplicitInput::None) { + continue; + } + + /* Otherwise, the input has the domain of the implicit input, which is the domain of the + * compositing region. Notice that the lower the domain priority value is, the higher the + * priority is, hence the less than comparison. */ + if (input_descriptor.domain_priority < current_domain_priority) { + node_domain = Domain(context_.get_compositing_region_size()); + current_domain_priority = input_descriptor.domain_priority; + } continue; } - const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input); + /* Otherwise, the origin socket is an output, which means it is linked. */ + const DOutputSocket output = DOutputSocket(origin); /* If the output belongs to a node that is part of the pixel compile unit, then the domain of * the input is the domain of the compile unit itself. */ diff --git a/source/blender/compositor/intern/evaluator.cc b/source/blender/compositor/intern/evaluator.cc index 359b802bdfa..e0e854f600f 100644 --- a/source/blender/compositor/intern/evaluator.cc +++ b/source/blender/compositor/intern/evaluator.cc @@ -12,6 +12,7 @@ #include "COM_compile_state.hh" #include "COM_context.hh" #include "COM_evaluator.hh" +#include "COM_implicit_input_operation.hh" #include "COM_input_single_value_operation.hh" #include "COM_multi_function_procedure_operation.hh" #include "COM_node_operation.hh" @@ -50,7 +51,7 @@ void Evaluator::evaluate() const Schedule schedule = compute_schedule(context_, *derived_node_tree_); - CompileState compile_state(schedule); + CompileState compile_state(context_, schedule); for (const DNode &node : schedule) { if (context_.is_canceled()) { @@ -242,6 +243,15 @@ void Evaluator::map_pixel_operation_inputs_to_their_results(PixelOperation *oper const int internal_reference_count = operation->get_internal_input_reference_count(item.key); result.decrement_reference_count(internal_reference_count - 1); } + + for (const auto item : operation->get_implicit_inputs_to_input_identifiers_map().items()) { + ImplicitInputOperation *input_operation = new ImplicitInputOperation(context_, item.key); + operation->map_input_to_result(item.value, &input_operation->get_result()); + + operations_stream_.append(std::unique_ptr(input_operation)); + + input_operation->evaluate(); + } } void Evaluator::cancel_evaluation() diff --git a/source/blender/compositor/intern/implicit_input_operation.cc b/source/blender/compositor/intern/implicit_input_operation.cc new file mode 100644 index 00000000000..3267c7b893d --- /dev/null +++ b/source/blender/compositor/intern/implicit_input_operation.cc @@ -0,0 +1,57 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_assert.h" + +#include "COM_implicit_input_operation.hh" +#include "COM_input_descriptor.hh" +#include "COM_operation.hh" +#include "COM_result.hh" + +namespace blender::compositor { + +const StringRef ImplicitInputOperation::output_identifier_ = StringRef("Output"); + +static ResultType get_implicit_input_result_type(const ImplicitInput implicit_input) +{ + switch (implicit_input) { + case ImplicitInput::None: + break; + case ImplicitInput::TextureCoordinates: + return ResultType::Float3; + } + + BLI_assert_unreachable(); + return ResultType::Float3; +} + +ImplicitInputOperation::ImplicitInputOperation(Context &context, + const ImplicitInput implicit_input) + : Operation(context), implicit_input_(implicit_input) +{ + this->populate_result(output_identifier_, + context.create_result(get_implicit_input_result_type(implicit_input))); +} + +void ImplicitInputOperation::execute() +{ + Result &result = this->get_result(); + + switch (implicit_input_) { + case ImplicitInput::None: + BLI_assert_unreachable(); + break; + case ImplicitInput::TextureCoordinates: + const int2 size = this->context().get_compositing_region_size(); + result.wrap_external( + this->context().cache_manager().texture_coordinates.get(this->context(), size)); + } +} + +Result &ImplicitInputOperation::get_result() +{ + return Operation::get_result(output_identifier_); +} + +} // 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 bddfe226593..fcdcaabd904 100644 --- a/source/blender/compositor/intern/multi_function_procedure_operation.cc +++ b/source/blender/compositor/intern/multi_function_procedure_operation.cc @@ -140,6 +140,9 @@ void MultiFunctionProcedureOperation::build_procedure() procedure_builder_.add_destruct(*variable); } } + for (mf::Variable *variable : implicit_input_to_variable_map_.values()) { + procedure_builder_.add_destruct(*variable); + } mf::ReturnInstruction &return_instruction = procedure_builder_.add_return(); mf::procedure_optimization::move_destructs_up(procedure_, return_instruction); @@ -158,11 +161,19 @@ Vector MultiFunctionProcedureOperation::get_input_variables( continue; } - /* The origin socket is an input, that means the input is unlinked and we generate a constant - * variable for it. */ + /* The origin socket is an input, that means the input is unlinked. */ const DSocket origin = get_input_origin_socket(input); if (origin->is_input()) { - input_variables.append(this->get_constant_input_variable(DInputSocket(origin))); + const InputDescriptor origin_descriptor = input_descriptor_from_input_socket( + origin.bsocket()); + + if (origin_descriptor.implicit_input == ImplicitInput::None) { + /* No implicit input, so get a constant variable that holds the socket value. */ + input_variables.append(this->get_constant_input_variable(DInputSocket(origin))); + } + else { + input_variables.append(this->get_implicit_input_variable(input, DInputSocket(origin))); + } } else { /* Otherwise, the origin socket is an output, which means it is linked. */ @@ -244,6 +255,50 @@ mf::Variable *MultiFunctionProcedureOperation::get_constant_input_variable(DInpu return constant_variable; } +mf::Variable *MultiFunctionProcedureOperation::get_implicit_input_variable( + const DInputSocket input, const DInputSocket origin) +{ + const InputDescriptor origin_descriptor = input_descriptor_from_input_socket(origin.bsocket()); + const ImplicitInput implicit_input = origin_descriptor.implicit_input; + + /* Inherit the type and implicit input of the origin input since doing implicit conversion inside + * the multi-function operation is much cheaper. */ + InputDescriptor input_descriptor = input_descriptor_from_input_socket(input.bsocket()); + input_descriptor.type = origin_descriptor.type; + input_descriptor.implicit_input = implicit_input; + + /* An input was already declared for that implicit input, so no need to declare it again and we + * just return its variable. */ + if (implicit_input_to_variable_map_.contains(implicit_input)) { + /* But first we update the domain priority of the input descriptor to be the higher priority of + * the existing descriptor and the descriptor of the new input socket. That's because the same + * implicit input might be used in inputs inside the multi-function procedure operation which + * have different priorities. */ + InputDescriptor &existing_input_descriptor = this->get_input_descriptor( + implicit_inputs_to_input_identifiers_map_.lookup(implicit_input)); + existing_input_descriptor.domain_priority = math::min( + existing_input_descriptor.domain_priority, input_descriptor.domain_priority); + + return implicit_input_to_variable_map_.lookup(implicit_input); + } + + const int implicit_input_index = implicit_inputs_to_input_identifiers_map_.size(); + const std::string input_identifier = "implicit_input" + std::to_string(implicit_input_index); + declare_input_descriptor(input_identifier, input_descriptor); + + /* Map the implicit input to the identifier of the operation input that was declared for it. */ + implicit_inputs_to_input_identifiers_map_.add_new(implicit_input, input_identifier); + + mf::Variable &variable = procedure_builder_.add_input_parameter( + mf::DataType::ForSingle(Result::cpp_type(input_descriptor.type)), input_identifier); + parameter_identifiers_.append(input_identifier); + + /* Map the implicit input to the variable that was created for it. */ + implicit_input_to_variable_map_.add(implicit_input, &variable); + + return &variable; +} + mf::Variable *MultiFunctionProcedureOperation::get_multi_function_input_variable( DInputSocket input_socket, DOutputSocket output_socket) { diff --git a/source/blender/compositor/intern/pixel_operation.cc b/source/blender/compositor/intern/pixel_operation.cc index b17fa5de9eb..9996f4965bb 100644 --- a/source/blender/compositor/intern/pixel_operation.cc +++ b/source/blender/compositor/intern/pixel_operation.cc @@ -63,6 +63,11 @@ Map &PixelOperation::get_inputs_to_linked_outputs_ma return inputs_to_linked_outputs_map_; } +Map &PixelOperation::get_implicit_inputs_to_input_identifiers_map() +{ + return implicit_inputs_to_input_identifiers_map_; +} + int PixelOperation::get_internal_input_reference_count(const StringRef &identifier) { return inputs_to_reference_counts_map_.lookup(identifier); diff --git a/source/blender/compositor/intern/shader_operation.cc b/source/blender/compositor/intern/shader_operation.cc index c31aeb17468..eb77792bf98 100644 --- a/source/blender/compositor/intern/shader_operation.cc +++ b/source/blender/compositor/intern/shader_operation.cc @@ -135,11 +135,19 @@ void ShaderOperation::link_node_inputs(DNode node) continue; } - /* The origin socket is an input, that means the input is unlinked and we link a constant - * setter node for it. */ + /* The origin socket is an input, that means the input is unlinked and . */ const DSocket origin = get_input_origin_socket(input); if (origin->is_input()) { - this->link_node_input_constant(input, DInputSocket(origin)); + const InputDescriptor origin_descriptor = input_descriptor_from_input_socket( + origin.bsocket()); + + if (origin_descriptor.implicit_input == ImplicitInput::None) { + /* No implicit input, so link a constant setter node for it that holds the input value. */ + this->link_node_input_constant(input, DInputSocket(origin)); + } + else { + this->link_node_input_implicit(input, DInputSocket(origin)); + } continue; } @@ -241,6 +249,63 @@ void ShaderOperation::link_node_input_constant(const DInputSocket input, const D GPU_link(material_, function_name, link, &stack.link); } +void ShaderOperation::link_node_input_implicit(const DInputSocket input, const DInputSocket origin) +{ + ShaderNode &node = *shader_nodes_.lookup(input.node()); + GPUNodeStack &stack = node.get_input(input->identifier); + + const InputDescriptor origin_descriptor = input_descriptor_from_input_socket(origin.bsocket()); + const ImplicitInput implicit_input = origin_descriptor.implicit_input; + + /* Inherit the type and implicit input of the origin input since doing implicit conversion inside + * the shader operation is much cheaper. */ + InputDescriptor input_descriptor = input_descriptor_from_input_socket(input.bsocket()); + input_descriptor.type = origin_descriptor.type; + input_descriptor.implicit_input = implicit_input; + + /* An input was already declared for that implicit input, so no need to declare it again and we + * just link it. */ + if (implicit_input_to_material_attribute_map_.contains(implicit_input)) { + /* But first we update the domain priority of the input descriptor to be the higher priority of + * the existing descriptor and the descriptor of the new input socket. That's because the same + * implicit input might be used in inputs inside the shader operation which have different + * priorities. */ + InputDescriptor &existing_input_descriptor = this->get_input_descriptor( + implicit_inputs_to_input_identifiers_map_.lookup(implicit_input)); + existing_input_descriptor.domain_priority = math::min( + existing_input_descriptor.domain_priority, input_descriptor.domain_priority); + + /* Link the attribute representing the shader operation input corresponding to the implicit + * input. */ + stack.link = implicit_input_to_material_attribute_map_.lookup(implicit_input); + return; + } + + const int implicit_input_index = implicit_inputs_to_input_identifiers_map_.size(); + const std::string input_identifier = "implicit_input" + std::to_string(implicit_input_index); + declare_input_descriptor(input_identifier, input_descriptor); + + /* Map the implicit input to the identifier of the operation input that was declared for it. */ + implicit_inputs_to_input_identifiers_map_.add_new(implicit_input, input_identifier); + + /* Add a new GPU attribute representing an input to the GPU material. Instead of using the + * attribute directly, we link it to an appropriate set function and use its output link instead. + * This is needed because the `gputype` member of the attribute is only initialized if it is + * linked to a GPU node. */ + GPUNodeLink *attribute_link; + GPU_link(material_, + get_set_function_name(input_descriptor.type), + GPU_attribute(material_, CD_AUTO_FROM_NAME, input_identifier.c_str()), + &attribute_link); + + /* Map the implicit input to the attribute that was created for it. */ + implicit_input_to_material_attribute_map_.add(implicit_input, attribute_link); + + /* Link the attribute representing the shader operation input corresponding to the implicit + * input. */ + stack.link = attribute_link; +} + void ShaderOperation::link_node_input_internal(DInputSocket input_socket, DOutputSocket output_socket) { diff --git a/source/blender/compositor/intern/utilities.cc b/source/blender/compositor/intern/utilities.cc index b3e028eda1e..7992474bd2f 100644 --- a/source/blender/compositor/intern/utilities.cc +++ b/source/blender/compositor/intern/utilities.cc @@ -109,6 +109,15 @@ bool is_pixel_node(DNode node) return node->typeinfo->gpu_fn && node->typeinfo->build_multi_function; } +static ImplicitInput get_implicit_input(const nodes::SocketDeclaration *socket_declaration) +{ + /* We only support implicit textures coordinates, though this can be expanded in the future. */ + if (socket_declaration->input_field_type == nodes::InputSocketFieldType::Implicit) { + return ImplicitInput::TextureCoordinates; + } + return ImplicitInput::None; +} + InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket) { using namespace nodes; @@ -125,6 +134,7 @@ InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket) input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value(); input_descriptor.realization_mode = static_cast( socket_declaration->compositor_realization_mode()); + input_descriptor.implicit_input = get_implicit_input(socket_declaration); return input_descriptor; }