216 lines
6.3 KiB
C++
216 lines
6.3 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include <limits>
|
|
#include <memory>
|
|
|
|
#include "BLI_map.hh"
|
|
#include "BLI_string_ref.hh"
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "COM_context.hh"
|
|
#include "COM_conversion_operation.hh"
|
|
#include "COM_domain.hh"
|
|
#include "COM_input_descriptor.hh"
|
|
#include "COM_operation.hh"
|
|
#include "COM_realize_on_domain_operation.hh"
|
|
#include "COM_reduce_to_single_value_operation.hh"
|
|
#include "COM_result.hh"
|
|
#include "COM_simple_operation.hh"
|
|
#include "COM_texture_pool.hh"
|
|
|
|
namespace blender::realtime_compositor {
|
|
|
|
Operation::Operation(Context &context) : context_(context) {}
|
|
|
|
Operation::~Operation() = default;
|
|
|
|
void Operation::evaluate()
|
|
{
|
|
evaluate_input_processors();
|
|
|
|
reset_results();
|
|
|
|
execute();
|
|
|
|
compute_preview();
|
|
|
|
release_inputs();
|
|
|
|
release_unneeded_results();
|
|
}
|
|
|
|
Result &Operation::get_result(StringRef identifier)
|
|
{
|
|
return results_.lookup(identifier);
|
|
}
|
|
|
|
void Operation::map_input_to_result(StringRef identifier, Result *result)
|
|
{
|
|
results_mapped_to_inputs_.add_new(identifier, result);
|
|
}
|
|
|
|
Domain Operation::compute_domain()
|
|
{
|
|
/* Default to an identity domain in case no domain input was found, most likely because all
|
|
* inputs are single values. */
|
|
Domain operation_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 (StringRef identifier : input_descriptors_.keys()) {
|
|
const Result &result = get_input(identifier);
|
|
const InputDescriptor &descriptor = get_input_descriptor(identifier);
|
|
|
|
/* A single value input can't be a domain input. */
|
|
if (result.is_single_value() || descriptor.expects_single_value) {
|
|
continue;
|
|
}
|
|
|
|
/* An input that skips operation domain realization can't be a domain input. */
|
|
if (!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 (descriptor.domain_priority < current_domain_priority) {
|
|
operation_domain = result.domain();
|
|
current_domain_priority = descriptor.domain_priority;
|
|
}
|
|
}
|
|
|
|
return operation_domain;
|
|
}
|
|
|
|
void Operation::add_and_evaluate_input_processors()
|
|
{
|
|
/* Each input processor type is added to all inputs entirely before the next type. This is done
|
|
* because the construction of the input processors may depend on the result of previous input
|
|
* processors for all inputs. For instance, the realize on domain input processor considers the
|
|
* value of all inputs, so previous input processors for all inputs needs to be added and
|
|
* evaluated first. */
|
|
|
|
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
|
|
SimpleOperation *single_value = ReduceToSingleValueOperation::construct_if_needed(
|
|
context(), get_input(identifier));
|
|
add_and_evaluate_input_processor(identifier, single_value);
|
|
}
|
|
|
|
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
|
|
SimpleOperation *conversion = ConversionOperation::construct_if_needed(
|
|
context(), get_input(identifier), get_input_descriptor(identifier));
|
|
add_and_evaluate_input_processor(identifier, conversion);
|
|
}
|
|
|
|
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
|
|
SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed(
|
|
context(), get_input(identifier), get_input_descriptor(identifier), compute_domain());
|
|
add_and_evaluate_input_processor(identifier, realize_on_domain);
|
|
}
|
|
}
|
|
|
|
void Operation::add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor)
|
|
{
|
|
/* Allow null inputs to facilitate construct_if_needed pattern of addition. For instance, see the
|
|
* implementation of the add_and_evaluate_input_processors method. */
|
|
if (!processor) {
|
|
return;
|
|
}
|
|
|
|
ProcessorsVector &processors = input_processors_.lookup_or_add_default(identifier);
|
|
|
|
/* Get the result that should serve as the input for the processor. This is either the result
|
|
* mapped to the input or the result of the last processor depending on whether this is the first
|
|
* processor or not. */
|
|
Result &result = processors.is_empty() ? get_input(identifier) : processors.last()->get_result();
|
|
|
|
/* Map the input result of the processor and add it to the processors vector. */
|
|
processor->map_input_to_result(&result);
|
|
processors.append(std::unique_ptr<SimpleOperation>(processor));
|
|
|
|
/* Switch the result mapped to the input to be the output result of the processor. */
|
|
switch_result_mapped_to_input(identifier, &processor->get_result());
|
|
|
|
processor->evaluate();
|
|
}
|
|
|
|
void Operation::compute_preview(){};
|
|
|
|
Result &Operation::get_input(StringRef identifier) const
|
|
{
|
|
return *results_mapped_to_inputs_.lookup(identifier);
|
|
}
|
|
|
|
void Operation::switch_result_mapped_to_input(StringRef identifier, Result *result)
|
|
{
|
|
results_mapped_to_inputs_.lookup(identifier) = result;
|
|
}
|
|
|
|
void Operation::populate_result(StringRef identifier, Result result)
|
|
{
|
|
results_.add_new(identifier, result);
|
|
}
|
|
|
|
void Operation::declare_input_descriptor(StringRef identifier, InputDescriptor descriptor)
|
|
{
|
|
input_descriptors_.add_new(identifier, descriptor);
|
|
}
|
|
|
|
InputDescriptor &Operation::get_input_descriptor(StringRef identifier)
|
|
{
|
|
return input_descriptors_.lookup(identifier);
|
|
}
|
|
|
|
Context &Operation::context() const
|
|
{
|
|
return context_;
|
|
}
|
|
|
|
TexturePool &Operation::texture_pool() const
|
|
{
|
|
return context_.texture_pool();
|
|
}
|
|
|
|
void Operation::evaluate_input_processors()
|
|
{
|
|
if (!input_processors_added_) {
|
|
add_and_evaluate_input_processors();
|
|
input_processors_added_ = true;
|
|
return;
|
|
}
|
|
|
|
for (const ProcessorsVector &processors : input_processors_.values()) {
|
|
for (const std::unique_ptr<SimpleOperation> &processor : processors) {
|
|
processor->evaluate();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Operation::reset_results()
|
|
{
|
|
for (Result &result : results_.values()) {
|
|
result.reset();
|
|
}
|
|
}
|
|
|
|
void Operation::release_inputs()
|
|
{
|
|
for (Result *result : results_mapped_to_inputs_.values()) {
|
|
result->release();
|
|
}
|
|
}
|
|
|
|
void Operation::release_unneeded_results()
|
|
{
|
|
for (Result &result : results_.values()) {
|
|
if (!result.should_compute() && result.is_allocated()) {
|
|
result.release();
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace blender::realtime_compositor
|