Files
test2/source/blender/compositor/intern/compile_state.cc

197 lines
6.6 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <limits>
#include "BLI_math_vector_types.hh"
#include "DNA_node_types.h"
#include "NOD_derived_node_tree.hh"
#include "COM_compile_state.hh"
#include "COM_domain.hh"
#include "COM_input_descriptor.hh"
#include "COM_node_operation.hh"
#include "COM_pixel_operation.hh"
#include "COM_result.hh"
#include "COM_scheduler.hh"
#include "COM_utilities.hh"
namespace blender::compositor {
using namespace nodes::derived_node_tree_types;
CompileState::CompileState(const Schedule &schedule) : schedule_(schedule) {}
const Schedule &CompileState::get_schedule()
{
return schedule_;
}
void CompileState::map_node_to_node_operation(DNode node, NodeOperation *operations)
{
return node_operations_.add_new(node, operations);
}
void CompileState::map_node_to_pixel_operation(DNode node, PixelOperation *operations)
{
return pixel_operations_.add_new(node, operations);
}
Result &CompileState::get_result_from_output_socket(DOutputSocket output)
{
/* The output belongs to a node that was compiled into a standard node operation, so return a
* reference to the result from that operation using the output identifier. */
if (node_operations_.contains(output.node())) {
NodeOperation *operation = node_operations_.lookup(output.node());
return operation->get_result(output->identifier);
}
/* Otherwise, the output belongs to a node that was compiled into a pixel operation, so retrieve
* the internal identifier of that output and return a reference to the result from that
* operation using the retrieved identifier. */
PixelOperation *operation = pixel_operations_.lookup(output.node());
return operation->get_result(operation->get_output_identifier_from_output_socket(output));
}
void CompileState::add_node_to_pixel_compile_unit(DNode node)
{
pixel_compile_unit_.add_new(node);
/* If the domain of the pixel compile unit is not yet determined or was determined to be
* an identity domain, update it to be the computed domain of the node. */
if (pixel_compile_unit_domain_ == Domain::identity()) {
pixel_compile_unit_domain_ = compute_pixel_node_domain(node);
}
}
PixelCompileUnit &CompileState::get_pixel_compile_unit()
{
return pixel_compile_unit_;
}
void CompileState::reset_pixel_compile_unit()
{
pixel_compile_unit_.clear();
pixel_compile_unit_domain_ = Domain::identity();
}
bool CompileState::should_compile_pixel_compile_unit(DNode node)
{
/* If the pixel compile unit is empty, then it can't be compiled yet. */
if (pixel_compile_unit_.is_empty()) {
return false;
}
/* If the node is not a pixel node, then it can't be added to the pixel compile unit and the
* pixel compile unit is considered complete and should be compiled. */
if (!is_pixel_node(node)) {
return true;
}
/* If the computed domain of the node doesn't matches the domain of the pixel compile unit, then
* it can't be added to the pixel compile unit and the pixel compile unit is considered
* complete and should be compiled. Identity domains are an exception as they are always
* compatible because they represents single values. */
if (pixel_compile_unit_domain_ != Domain::identity() &&
pixel_compile_unit_domain_ != compute_pixel_node_domain(node))
{
return true;
}
/* Otherwise, the node is compatible and can be added to the compile unit and it shouldn't be
* compiled just yet. */
return false;
}
int CompileState::compute_pixel_node_operation_outputs_count(DNode node)
{
const DOutputSocket preview_output = find_preview_output_socket(node);
int outputs_count = 0;
for (const bNodeSocket *output : node->output_sockets()) {
const DOutputSocket doutput{node.context(), output};
/* If the output is used as the node preview, then an operation output will exist for it. */
const bool is_preview_output = doutput == preview_output;
/* If any of the nodes linked to the output are not part of the pixel compile unit but are
* part of the execution schedule, then an operation output will exist for it. */
const bool is_operation_output = is_output_linked_to_node_conditioned(
doutput, [&](DNode node) {
return schedule_.contains(node) && !pixel_compile_unit_.contains(node);
});
if (is_operation_output || is_preview_output) {
outputs_count += 1;
}
}
return outputs_count;
}
Domain CompileState::compute_pixel_node_domain(DNode node)
{
/* Default to an identity domain in case no domain input was found, most likely because all
* inputs are single values. */
Domain node_domain = Domain::identity();
int current_domain_priority = std::numeric_limits<int>::max();
/* 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};
/* 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) {
continue;
}
const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input);
/* 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. */
if (pixel_compile_unit_.contains(output.node())) {
/* Single value inputs can't be domain inputs. */
if (pixel_compile_unit_domain_.size == int2(1)) {
continue;
}
/* 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 = pixel_compile_unit_domain_;
current_domain_priority = input_descriptor.domain_priority;
}
continue;
}
const Result &result = get_result_from_output_socket(output);
/* A single value input can't be a domain input. */
if (result.is_single_value() || input_descriptor.expects_single_value) {
continue;
}
/* An input that skips operation domain realization can't be a domain input. */
if (!input_descriptor.realization_options.realize_on_operation_domain) {
continue;
}
/* 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 = result.domain();
current_domain_priority = input_descriptor.domain_priority;
}
}
return node_domain;
}
} // namespace blender::compositor