Compositor: Initial support for implicit inputs
This patch adds initial support for implicit inputs in pixel operations. This is currently a non-functional change but will be used in the future to support implicit inputs in texture nodes or so. This works by exposing extra inputs to pixel operation for each of the supported implicit input types if needed, and linking those inputs to instances of the ImplicitInputOperation operation. Only a single implicit input exist for now and we do not differentiate between any of the implicit inputs type. In order to do that, we need to refactor how input declarations for implicit inputs happen, since they are now tied to the Geometry Nodes specifically.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
37
source/blender/compositor/COM_implicit_input_operation.hh
Normal file
37
source/blender/compositor/COM_implicit_input_operation.hh
Normal file
@@ -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
|
||||
@@ -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. */
|
||||
|
||||
@@ -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<DOutputSocket, mf::Variable *> output_to_variable_map_;
|
||||
/* A map that associates implicit inputs to the variables that were created for them. */
|
||||
Map<ImplicitInput, mf::Variable *> 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. */
|
||||
|
||||
@@ -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<DOutputSocket, std::string> 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<ImplicitInput, std::string> 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<std::string, DOutputSocket> &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<ImplicitInput, std::string> &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);
|
||||
|
||||
@@ -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<DOutputSocket, GPUNodeLink *> output_to_material_attribute_map_;
|
||||
/* A map that associates implicit inputs to the attributes that were created for them. */
|
||||
Map<ImplicitInput, GPUNodeLink *> 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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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<ImplicitInputOperation>(input_operation));
|
||||
|
||||
input_operation->evaluate();
|
||||
}
|
||||
}
|
||||
|
||||
void Evaluator::cancel_evaluation()
|
||||
|
||||
57
source/blender/compositor/intern/implicit_input_operation.cc
Normal file
57
source/blender/compositor/intern/implicit_input_operation.cc
Normal file
@@ -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
|
||||
@@ -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<mf::Variable *> 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)
|
||||
{
|
||||
|
||||
@@ -63,6 +63,11 @@ Map<std::string, DOutputSocket> &PixelOperation::get_inputs_to_linked_outputs_ma
|
||||
return inputs_to_linked_outputs_map_;
|
||||
}
|
||||
|
||||
Map<ImplicitInput, std::string> &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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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<InputRealizationMode>(
|
||||
socket_declaration->compositor_realization_mode());
|
||||
input_descriptor.implicit_input = get_implicit_input(socket_declaration);
|
||||
|
||||
return input_descriptor;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user