This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/functions/intern/multi_function_network_evaluation.cc
Jacques Lucke 3608891282 Functions: extend virtual array functionality
This adds support for mutable virtual arrays and provides many utilities
for creating virtual arrays for various kinds of data. This commit is
preparation for D10994.
2021-04-17 15:13:20 +02:00

1084 lines
40 KiB
C++

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup fn
*
* The `MFNetworkEvaluator` class is a multi-function that consists of potentially many smaller
* multi-functions. When called, it traverses the underlying MFNetwork and executes the required
* function nodes.
*
* There are many possible approaches to evaluate a function network. The approach implemented
* below has the following features:
* - It does not use recursion. Those could become problematic with long node chains.
* - It can handle all existing parameter types (including mutable parameters).
* - Avoids data copies in many cases.
* - Every node is executed at most once.
* - Can compute sub-functions on a single element, when the result is the same for all elements.
*
* Possible improvements:
* - Cache and reuse buffers.
* - Use "deepest depth first" heuristic to decide which order the inputs of a node should be
* computed. This reduces the number of required temporary buffers when they are reused.
*/
#include "FN_multi_function_network_evaluation.hh"
#include "BLI_resource_scope.hh"
#include "BLI_stack.hh"
namespace blender::fn {
struct Value;
/**
* This keeps track of all the values that flow through the multi-function network. Therefore it
* maintains a mapping between output sockets and their corresponding values. Every `value`
* references some memory, that is owned either by the caller or this storage.
*
* A value can be owned by different sockets over time to avoid unnecessary copies.
*/
class MFNetworkEvaluationStorage {
private:
LinearAllocator<> allocator_;
IndexMask mask_;
Array<Value *> value_per_output_id_;
int64_t min_array_size_;
public:
MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount);
~MFNetworkEvaluationStorage();
/* Add the values that have been provided by the caller of the multi-function network. */
void add_single_input_from_caller(const MFOutputSocket &socket, const GVArray &virtual_array);
void add_vector_input_from_caller(const MFOutputSocket &socket,
const GVVectorArray &virtual_vector_array);
void add_single_output_from_caller(const MFOutputSocket &socket, GMutableSpan span);
void add_vector_output_from_caller(const MFOutputSocket &socket, GVectorArray &vector_array);
/* Get input buffers for function node evaluations. */
const GVArray &get_single_input__full(const MFInputSocket &socket, ResourceScope &scope);
const GVArray &get_single_input__single(const MFInputSocket &socket, ResourceScope &scope);
const GVVectorArray &get_vector_input__full(const MFInputSocket &socket, ResourceScope &scope);
const GVVectorArray &get_vector_input__single(const MFInputSocket &socket, ResourceScope &scope);
/* Get output buffers for function node evaluations. */
GMutableSpan get_single_output__full(const MFOutputSocket &socket);
GMutableSpan get_single_output__single(const MFOutputSocket &socket);
GVectorArray &get_vector_output__full(const MFOutputSocket &socket);
GVectorArray &get_vector_output__single(const MFOutputSocket &socket);
/* Get mutable buffers for function node evaluations. */
GMutableSpan get_mutable_single__full(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceScope &scope);
GMutableSpan get_mutable_single__single(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceScope &scope);
GVectorArray &get_mutable_vector__full(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceScope &scope);
GVectorArray &get_mutable_vector__single(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceScope &scope);
/* Mark a node as being done with evaluation. This might free temporary buffers that are no
* longer needed. */
void finish_node(const MFFunctionNode &node);
void finish_output_socket(const MFOutputSocket &socket);
void finish_input_socket(const MFInputSocket &socket);
IndexMask mask() const;
bool socket_is_computed(const MFOutputSocket &socket);
bool is_same_value_for_every_index(const MFOutputSocket &socket);
bool socket_has_buffer_for_output(const MFOutputSocket &socket);
};
MFNetworkEvaluator::MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs,
Vector<const MFInputSocket *> outputs)
: inputs_(std::move(inputs)), outputs_(std::move(outputs))
{
BLI_assert(outputs_.size() > 0);
MFSignatureBuilder signature{"Function Tree"};
for (const MFOutputSocket *socket : inputs_) {
BLI_assert(socket->node().is_dummy());
MFDataType type = socket->data_type();
switch (type.category()) {
case MFDataType::Single:
signature.single_input(socket->name(), type.single_type());
break;
case MFDataType::Vector:
signature.vector_input(socket->name(), type.vector_base_type());
break;
}
}
for (const MFInputSocket *socket : outputs_) {
BLI_assert(socket->node().is_dummy());
MFDataType type = socket->data_type();
switch (type.category()) {
case MFDataType::Single:
signature.single_output(socket->name(), type.single_type());
break;
case MFDataType::Vector:
signature.vector_output(socket->name(), type.vector_base_type());
break;
}
}
signature_ = signature.build();
this->set_signature(&signature_);
}
void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context) const
{
if (mask.size() == 0) {
return;
}
const MFNetwork &network = outputs_[0]->node().network();
Storage storage(mask, network.socket_id_amount());
Vector<const MFInputSocket *> outputs_to_initialize_in_the_end;
this->copy_inputs_to_storage(params, storage);
this->copy_outputs_to_storage(params, storage, outputs_to_initialize_in_the_end);
this->evaluate_network_to_compute_outputs(context, storage);
this->initialize_remaining_outputs(params, storage, outputs_to_initialize_in_the_end);
}
BLI_NOINLINE void MFNetworkEvaluator::copy_inputs_to_storage(MFParams params,
Storage &storage) const
{
for (int input_index : inputs_.index_range()) {
int param_index = input_index + 0;
const MFOutputSocket &socket = *inputs_[input_index];
switch (socket.data_type().category()) {
case MFDataType::Single: {
const GVArray &input_list = params.readonly_single_input(param_index);
storage.add_single_input_from_caller(socket, input_list);
break;
}
case MFDataType::Vector: {
const GVVectorArray &input_list_list = params.readonly_vector_input(param_index);
storage.add_vector_input_from_caller(socket, input_list_list);
break;
}
}
}
}
BLI_NOINLINE void MFNetworkEvaluator::copy_outputs_to_storage(
MFParams params,
Storage &storage,
Vector<const MFInputSocket *> &outputs_to_initialize_in_the_end) const
{
for (int output_index : outputs_.index_range()) {
int param_index = output_index + inputs_.size();
const MFInputSocket &socket = *outputs_[output_index];
const MFOutputSocket &origin = *socket.origin();
if (origin.node().is_dummy()) {
BLI_assert(inputs_.contains(&origin));
/* Don't overwrite input buffers. */
outputs_to_initialize_in_the_end.append(&socket);
continue;
}
if (storage.socket_has_buffer_for_output(origin)) {
/* When two outputs will be initialized to the same values. */
outputs_to_initialize_in_the_end.append(&socket);
continue;
}
switch (socket.data_type().category()) {
case MFDataType::Single: {
GMutableSpan span = params.uninitialized_single_output(param_index);
storage.add_single_output_from_caller(origin, span);
break;
}
case MFDataType::Vector: {
GVectorArray &vector_array = params.vector_output(param_index);
storage.add_vector_output_from_caller(origin, vector_array);
break;
}
}
}
}
BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs(
MFContext &global_context, Storage &storage) const
{
Stack<const MFOutputSocket *, 32> sockets_to_compute;
for (const MFInputSocket *socket : outputs_) {
sockets_to_compute.push(socket->origin());
}
/* This is the main loop that traverses the MFNetwork. */
while (!sockets_to_compute.is_empty()) {
const MFOutputSocket &socket = *sockets_to_compute.peek();
const MFNode &node = socket.node();
if (storage.socket_is_computed(socket)) {
sockets_to_compute.pop();
continue;
}
BLI_assert(node.is_function());
BLI_assert(!node.has_unlinked_inputs());
const MFFunctionNode &function_node = node.as_function();
bool all_origins_are_computed = true;
for (const MFInputSocket *input_socket : function_node.inputs()) {
const MFOutputSocket *origin = input_socket->origin();
if (origin != nullptr) {
if (!storage.socket_is_computed(*origin)) {
sockets_to_compute.push(origin);
all_origins_are_computed = false;
}
}
}
if (all_origins_are_computed) {
this->evaluate_function(global_context, function_node, storage);
sockets_to_compute.pop();
}
}
}
BLI_NOINLINE void MFNetworkEvaluator::evaluate_function(MFContext &global_context,
const MFFunctionNode &function_node,
Storage &storage) const
{
const MultiFunction &function = function_node.function();
// std::cout << "Function: " << function.name() << "\n";
if (this->can_do_single_value_evaluation(function_node, storage)) {
/* The function output would be the same for all elements. Therefore, it is enough to call the
* function only on a single element. This can avoid many duplicate computations. */
MFParamsBuilder params{function, 1};
ResourceScope &scope = params.resource_scope();
for (int param_index : function.param_indices()) {
MFParamType param_type = function.param_type(param_index);
switch (param_type.category()) {
case MFParamType::SingleInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
const GVArray &values = storage.get_single_input__single(socket, scope);
params.add_readonly_single_input(values);
break;
}
case MFParamType::VectorInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
const GVVectorArray &values = storage.get_vector_input__single(socket, scope);
params.add_readonly_vector_input(values);
break;
}
case MFParamType::SingleOutput: {
const MFOutputSocket &socket = function_node.output_for_param(param_index);
GMutableSpan values = storage.get_single_output__single(socket);
params.add_uninitialized_single_output(values);
break;
}
case MFParamType::VectorOutput: {
const MFOutputSocket &socket = function_node.output_for_param(param_index);
GVectorArray &values = storage.get_vector_output__single(socket);
params.add_vector_output(values);
break;
}
case MFParamType::SingleMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
GMutableSpan values = storage.get_mutable_single__single(input, output, scope);
params.add_single_mutable(values);
break;
}
case MFParamType::VectorMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
GVectorArray &values = storage.get_mutable_vector__single(input, output, scope);
params.add_vector_mutable(values);
break;
}
}
}
function.call(IndexRange(1), params, global_context);
}
else {
MFParamsBuilder params{function, storage.mask().min_array_size()};
ResourceScope &scope = params.resource_scope();
for (int param_index : function.param_indices()) {
MFParamType param_type = function.param_type(param_index);
switch (param_type.category()) {
case MFParamType::SingleInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
const GVArray &values = storage.get_single_input__full(socket, scope);
params.add_readonly_single_input(values);
break;
}
case MFParamType::VectorInput: {
const MFInputSocket &socket = function_node.input_for_param(param_index);
const GVVectorArray &values = storage.get_vector_input__full(socket, scope);
params.add_readonly_vector_input(values);
break;
}
case MFParamType::SingleOutput: {
const MFOutputSocket &socket = function_node.output_for_param(param_index);
GMutableSpan values = storage.get_single_output__full(socket);
params.add_uninitialized_single_output(values);
break;
}
case MFParamType::VectorOutput: {
const MFOutputSocket &socket = function_node.output_for_param(param_index);
GVectorArray &values = storage.get_vector_output__full(socket);
params.add_vector_output(values);
break;
}
case MFParamType::SingleMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
GMutableSpan values = storage.get_mutable_single__full(input, output, scope);
params.add_single_mutable(values);
break;
}
case MFParamType::VectorMutable: {
const MFInputSocket &input = function_node.input_for_param(param_index);
const MFOutputSocket &output = function_node.output_for_param(param_index);
GVectorArray &values = storage.get_mutable_vector__full(input, output, scope);
params.add_vector_mutable(values);
break;
}
}
}
function.call(storage.mask(), params, global_context);
}
storage.finish_node(function_node);
}
bool MFNetworkEvaluator::can_do_single_value_evaluation(const MFFunctionNode &function_node,
Storage &storage) const
{
for (const MFInputSocket *socket : function_node.inputs()) {
if (!storage.is_same_value_for_every_index(*socket->origin())) {
return false;
}
}
if (storage.mask().min_array_size() >= 1) {
for (const MFOutputSocket *socket : function_node.outputs()) {
if (storage.socket_has_buffer_for_output(*socket)) {
return false;
}
}
}
return true;
}
BLI_NOINLINE void MFNetworkEvaluator::initialize_remaining_outputs(
MFParams params, Storage &storage, Span<const MFInputSocket *> remaining_outputs) const
{
ResourceScope scope;
for (const MFInputSocket *socket : remaining_outputs) {
int param_index = inputs_.size() + outputs_.first_index_of(socket);
switch (socket->data_type().category()) {
case MFDataType::Single: {
const GVArray &values = storage.get_single_input__full(*socket, scope);
GMutableSpan output_values = params.uninitialized_single_output(param_index);
values.materialize_to_uninitialized(storage.mask(), output_values.data());
break;
}
case MFDataType::Vector: {
const GVVectorArray &values = storage.get_vector_input__full(*socket, scope);
GVectorArray &output_values = params.vector_output(param_index);
output_values.extend(storage.mask(), values);
break;
}
}
}
}
/* -------------------------------------------------------------------- */
/** \name Value Types
* \{ */
enum class ValueType {
InputSingle,
InputVector,
OutputSingle,
OutputVector,
OwnSingle,
OwnVector,
};
struct Value {
ValueType type;
Value(ValueType type) : type(type)
{
}
};
struct InputSingleValue : public Value {
/** This virtual array has been provided by the code that called the multi-function network. */
const GVArray &virtual_array;
InputSingleValue(const GVArray &virtual_array)
: Value(ValueType::InputSingle), virtual_array(virtual_array)
{
}
};
struct InputVectorValue : public Value {
/** This virtual vector has been provided by the code that called the multi-function network. */
const GVVectorArray &virtual_vector_array;
InputVectorValue(const GVVectorArray &virtual_vector_array)
: Value(ValueType::InputVector), virtual_vector_array(virtual_vector_array)
{
}
};
struct OutputValue : public Value {
bool is_computed = false;
OutputValue(ValueType type) : Value(type)
{
}
};
struct OutputSingleValue : public OutputValue {
/** This span has been provided by the code that called the multi-function network. */
GMutableSpan span;
OutputSingleValue(GMutableSpan span) : OutputValue(ValueType::OutputSingle), span(span)
{
}
};
struct OutputVectorValue : public OutputValue {
/** This vector array has been provided by the code that called the multi-function network. */
GVectorArray *vector_array;
OutputVectorValue(GVectorArray &vector_array)
: OutputValue(ValueType::OutputVector), vector_array(&vector_array)
{
}
};
struct OwnSingleValue : public Value {
/** This span has been allocated during the evaluation of the multi-function network and contains
* intermediate data. It has to be freed once the network evaluation is finished. */
GMutableSpan span;
int max_remaining_users;
bool is_single_allocated;
OwnSingleValue(GMutableSpan span, int max_remaining_users, bool is_single_allocated)
: Value(ValueType::OwnSingle),
span(span),
max_remaining_users(max_remaining_users),
is_single_allocated(is_single_allocated)
{
}
};
struct OwnVectorValue : public Value {
/** This vector array has been allocated during the evaluation of the multi-function network and
* contains intermediate data. It has to be freed once the network evaluation is finished. */
GVectorArray *vector_array;
int max_remaining_users;
OwnVectorValue(GVectorArray &vector_array, int max_remaining_users)
: Value(ValueType::OwnVector),
vector_array(&vector_array),
max_remaining_users(max_remaining_users)
{
}
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Storage methods
* \{ */
MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, int socket_id_amount)
: mask_(mask),
value_per_output_id_(socket_id_amount, nullptr),
min_array_size_(mask.min_array_size())
{
}
MFNetworkEvaluationStorage::~MFNetworkEvaluationStorage()
{
for (Value *any_value : value_per_output_id_) {
if (any_value == nullptr) {
continue;
}
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
GMutableSpan span = value->span;
const CPPType &type = span.type();
if (value->is_single_allocated) {
type.destruct(span.data());
}
else {
type.destruct_indices(span.data(), mask_);
MEM_freeN(span.data());
}
}
else if (any_value->type == ValueType::OwnVector) {
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
delete value->vector_array;
}
}
}
IndexMask MFNetworkEvaluationStorage::mask() const
{
return mask_;
}
bool MFNetworkEvaluationStorage::socket_is_computed(const MFOutputSocket &socket)
{
Value *any_value = value_per_output_id_[socket.id()];
if (any_value == nullptr) {
return false;
}
if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
return static_cast<OutputValue *>(any_value)->is_computed;
}
return true;
}
bool MFNetworkEvaluationStorage::is_same_value_for_every_index(const MFOutputSocket &socket)
{
Value *any_value = value_per_output_id_[socket.id()];
switch (any_value->type) {
case ValueType::OwnSingle:
return static_cast<OwnSingleValue *>(any_value)->span.size() == 1;
case ValueType::OwnVector:
return static_cast<OwnVectorValue *>(any_value)->vector_array->size() == 1;
case ValueType::InputSingle:
return static_cast<InputSingleValue *>(any_value)->virtual_array.is_single();
case ValueType::InputVector:
return static_cast<InputVectorValue *>(any_value)->virtual_vector_array.is_single_vector();
case ValueType::OutputSingle:
return static_cast<OutputSingleValue *>(any_value)->span.size() == 1;
case ValueType::OutputVector:
return static_cast<OutputVectorValue *>(any_value)->vector_array->size() == 1;
}
BLI_assert(false);
return false;
}
bool MFNetworkEvaluationStorage::socket_has_buffer_for_output(const MFOutputSocket &socket)
{
Value *any_value = value_per_output_id_[socket.id()];
if (any_value == nullptr) {
return false;
}
BLI_assert(ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector));
return true;
}
void MFNetworkEvaluationStorage::finish_node(const MFFunctionNode &node)
{
for (const MFInputSocket *socket : node.inputs()) {
this->finish_input_socket(*socket);
}
for (const MFOutputSocket *socket : node.outputs()) {
this->finish_output_socket(*socket);
}
}
void MFNetworkEvaluationStorage::finish_output_socket(const MFOutputSocket &socket)
{
Value *any_value = value_per_output_id_[socket.id()];
if (any_value == nullptr) {
return;
}
if (ELEM(any_value->type, ValueType::OutputSingle, ValueType::OutputVector)) {
static_cast<OutputValue *>(any_value)->is_computed = true;
}
}
void MFNetworkEvaluationStorage::finish_input_socket(const MFInputSocket &socket)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
if (any_value == nullptr) {
/* Can happen when a value has been forward to the next node. */
return;
}
switch (any_value->type) {
case ValueType::InputSingle:
case ValueType::OutputSingle:
case ValueType::InputVector:
case ValueType::OutputVector: {
break;
}
case ValueType::OwnSingle: {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
BLI_assert(value->max_remaining_users >= 1);
value->max_remaining_users--;
if (value->max_remaining_users == 0) {
GMutableSpan span = value->span;
const CPPType &type = span.type();
if (value->is_single_allocated) {
type.destruct(span.data());
}
else {
type.destruct_indices(span.data(), mask_);
MEM_freeN(span.data());
}
value_per_output_id_[origin.id()] = nullptr;
}
break;
}
case ValueType::OwnVector: {
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
BLI_assert(value->max_remaining_users >= 1);
value->max_remaining_users--;
if (value->max_remaining_users == 0) {
delete value->vector_array;
value_per_output_id_[origin.id()] = nullptr;
}
break;
}
}
}
void MFNetworkEvaluationStorage::add_single_input_from_caller(const MFOutputSocket &socket,
const GVArray &virtual_array)
{
BLI_assert(value_per_output_id_[socket.id()] == nullptr);
BLI_assert(virtual_array.size() >= min_array_size_);
auto *value = allocator_.construct<InputSingleValue>(virtual_array).release();
value_per_output_id_[socket.id()] = value;
}
void MFNetworkEvaluationStorage::add_vector_input_from_caller(
const MFOutputSocket &socket, const GVVectorArray &virtual_vector_array)
{
BLI_assert(value_per_output_id_[socket.id()] == nullptr);
BLI_assert(virtual_vector_array.size() >= min_array_size_);
auto *value = allocator_.construct<InputVectorValue>(virtual_vector_array).release();
value_per_output_id_[socket.id()] = value;
}
void MFNetworkEvaluationStorage::add_single_output_from_caller(const MFOutputSocket &socket,
GMutableSpan span)
{
BLI_assert(value_per_output_id_[socket.id()] == nullptr);
BLI_assert(span.size() >= min_array_size_);
auto *value = allocator_.construct<OutputSingleValue>(span).release();
value_per_output_id_[socket.id()] = value;
}
void MFNetworkEvaluationStorage::add_vector_output_from_caller(const MFOutputSocket &socket,
GVectorArray &vector_array)
{
BLI_assert(value_per_output_id_[socket.id()] == nullptr);
BLI_assert(vector_array.size() >= min_array_size_);
auto *value = allocator_.construct<OutputVectorValue>(vector_array).release();
value_per_output_id_[socket.id()] = value;
}
GMutableSpan MFNetworkEvaluationStorage::get_single_output__full(const MFOutputSocket &socket)
{
Value *any_value = value_per_output_id_[socket.id()];
if (any_value == nullptr) {
const CPPType &type = socket.data_type().single_type();
void *buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
GMutableSpan span(type, buffer, min_array_size_);
auto *value =
allocator_.construct<OwnSingleValue>(span, socket.targets().size(), false).release();
value_per_output_id_[socket.id()] = value;
return span;
}
BLI_assert(any_value->type == ValueType::OutputSingle);
return static_cast<OutputSingleValue *>(any_value)->span;
}
GMutableSpan MFNetworkEvaluationStorage::get_single_output__single(const MFOutputSocket &socket)
{
Value *any_value = value_per_output_id_[socket.id()];
if (any_value == nullptr) {
const CPPType &type = socket.data_type().single_type();
void *buffer = allocator_.allocate(type.size(), type.alignment());
GMutableSpan span(type, buffer, 1);
auto *value =
allocator_.construct<OwnSingleValue>(span, socket.targets().size(), true).release();
value_per_output_id_[socket.id()] = value;
return value->span;
}
BLI_assert(any_value->type == ValueType::OutputSingle);
GMutableSpan span = static_cast<OutputSingleValue *>(any_value)->span;
BLI_assert(span.size() == 1);
return span;
}
GVectorArray &MFNetworkEvaluationStorage::get_vector_output__full(const MFOutputSocket &socket)
{
Value *any_value = value_per_output_id_[socket.id()];
if (any_value == nullptr) {
const CPPType &type = socket.data_type().vector_base_type();
GVectorArray *vector_array = new GVectorArray(type, min_array_size_);
auto *value =
allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release();
value_per_output_id_[socket.id()] = value;
return *value->vector_array;
}
BLI_assert(any_value->type == ValueType::OutputVector);
return *static_cast<OutputVectorValue *>(any_value)->vector_array;
}
GVectorArray &MFNetworkEvaluationStorage::get_vector_output__single(const MFOutputSocket &socket)
{
Value *any_value = value_per_output_id_[socket.id()];
if (any_value == nullptr) {
const CPPType &type = socket.data_type().vector_base_type();
GVectorArray *vector_array = new GVectorArray(type, 1);
auto *value =
allocator_.construct<OwnVectorValue>(*vector_array, socket.targets().size()).release();
value_per_output_id_[socket.id()] = value;
return *value->vector_array;
}
BLI_assert(any_value->type == ValueType::OutputVector);
GVectorArray &vector_array = *static_cast<OutputVectorValue *>(any_value)->vector_array;
BLI_assert(vector_array.size() == 1);
return vector_array;
}
GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__full(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
const CPPType &type = from.data_type().single_type();
Value *from_any_value = value_per_output_id_[from.id()];
Value *to_any_value = value_per_output_id_[to.id()];
BLI_assert(from_any_value != nullptr);
BLI_assert(type == to.data_type().single_type());
if (to_any_value != nullptr) {
BLI_assert(to_any_value->type == ValueType::OutputSingle);
GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
const GVArray &virtual_array = this->get_single_input__full(input, scope);
virtual_array.materialize_to_uninitialized(mask_, span.data());
return span;
}
if (from_any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value);
if (value->max_remaining_users == 1 && !value->is_single_allocated) {
value_per_output_id_[to.id()] = value;
value_per_output_id_[from.id()] = nullptr;
value->max_remaining_users = to.targets().size();
return value->span;
}
}
const GVArray &virtual_array = this->get_single_input__full(input, scope);
void *new_buffer = MEM_mallocN_aligned(min_array_size_ * type.size(), type.alignment(), AT);
GMutableSpan new_array_ref(type, new_buffer, min_array_size_);
virtual_array.materialize_to_uninitialized(mask_, new_array_ref.data());
OwnSingleValue *new_value =
allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), false).release();
value_per_output_id_[to.id()] = new_value;
return new_array_ref;
}
GMutableSpan MFNetworkEvaluationStorage::get_mutable_single__single(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
const CPPType &type = from.data_type().single_type();
Value *from_any_value = value_per_output_id_[from.id()];
Value *to_any_value = value_per_output_id_[to.id()];
BLI_assert(from_any_value != nullptr);
BLI_assert(type == to.data_type().single_type());
if (to_any_value != nullptr) {
BLI_assert(to_any_value->type == ValueType::OutputSingle);
GMutableSpan span = static_cast<OutputSingleValue *>(to_any_value)->span;
BLI_assert(span.size() == 1);
const GVArray &virtual_array = this->get_single_input__single(input, scope);
virtual_array.get_single_to_uninitialized(span[0]);
return span;
}
if (from_any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(from_any_value);
if (value->max_remaining_users == 1) {
value_per_output_id_[to.id()] = value;
value_per_output_id_[from.id()] = nullptr;
value->max_remaining_users = to.targets().size();
BLI_assert(value->span.size() == 1);
return value->span;
}
}
const GVArray &virtual_array = this->get_single_input__single(input, scope);
void *new_buffer = allocator_.allocate(type.size(), type.alignment());
virtual_array.get_single_to_uninitialized(new_buffer);
GMutableSpan new_array_ref(type, new_buffer, 1);
OwnSingleValue *new_value =
allocator_.construct<OwnSingleValue>(new_array_ref, to.targets().size(), true).release();
value_per_output_id_[to.id()] = new_value;
return new_array_ref;
}
GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__full(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
const CPPType &base_type = from.data_type().vector_base_type();
Value *from_any_value = value_per_output_id_[from.id()];
Value *to_any_value = value_per_output_id_[to.id()];
BLI_assert(from_any_value != nullptr);
BLI_assert(base_type == to.data_type().vector_base_type());
if (to_any_value != nullptr) {
BLI_assert(to_any_value->type == ValueType::OutputVector);
GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
vector_array.extend(mask_, virtual_vector_array);
return vector_array;
}
if (from_any_value->type == ValueType::OwnVector) {
OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value);
if (value->max_remaining_users == 1) {
value_per_output_id_[to.id()] = value;
value_per_output_id_[from.id()] = nullptr;
value->max_remaining_users = to.targets().size();
return *value->vector_array;
}
}
const GVVectorArray &virtual_vector_array = this->get_vector_input__full(input, scope);
GVectorArray *new_vector_array = new GVectorArray(base_type, min_array_size_);
new_vector_array->extend(mask_, virtual_vector_array);
OwnVectorValue *new_value =
allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release();
value_per_output_id_[to.id()] = new_value;
return *new_vector_array;
}
GVectorArray &MFNetworkEvaluationStorage::get_mutable_vector__single(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceScope &scope)
{
const MFOutputSocket &from = *input.origin();
const MFOutputSocket &to = output;
const CPPType &base_type = from.data_type().vector_base_type();
Value *from_any_value = value_per_output_id_[from.id()];
Value *to_any_value = value_per_output_id_[to.id()];
BLI_assert(from_any_value != nullptr);
BLI_assert(base_type == to.data_type().vector_base_type());
if (to_any_value != nullptr) {
BLI_assert(to_any_value->type == ValueType::OutputVector);
GVectorArray &vector_array = *static_cast<OutputVectorValue *>(to_any_value)->vector_array;
BLI_assert(vector_array.size() == 1);
const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
vector_array.extend({0}, virtual_vector_array);
return vector_array;
}
if (from_any_value->type == ValueType::OwnVector) {
OwnVectorValue *value = static_cast<OwnVectorValue *>(from_any_value);
if (value->max_remaining_users == 1) {
value_per_output_id_[to.id()] = value;
value_per_output_id_[from.id()] = nullptr;
value->max_remaining_users = to.targets().size();
return *value->vector_array;
}
}
const GVVectorArray &virtual_vector_array = this->get_vector_input__single(input, scope);
GVectorArray *new_vector_array = new GVectorArray(base_type, 1);
new_vector_array->extend({0}, virtual_vector_array);
OwnVectorValue *new_value =
allocator_.construct<OwnVectorValue>(*new_vector_array, to.targets().size()).release();
value_per_output_id_[to.id()] = new_value;
return *new_vector_array;
}
const GVArray &MFNetworkEvaluationStorage::get_single_input__full(const MFInputSocket &socket,
ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
BLI_assert(any_value != nullptr);
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
if (value->is_single_allocated) {
return scope.construct<GVArray_For_SingleValueRef>(
__func__, value->span.type(), min_array_size_, value->span.data());
}
return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
if (any_value->type == ValueType::InputSingle) {
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
return value->virtual_array;
}
if (any_value->type == ValueType::OutputSingle) {
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
BLI_assert(value->is_computed);
return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
BLI_assert(false);
return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
}
const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
BLI_assert(any_value != nullptr);
if (any_value->type == ValueType::OwnSingle) {
OwnSingleValue *value = static_cast<OwnSingleValue *>(any_value);
BLI_assert(value->span.size() == 1);
return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
if (any_value->type == ValueType::InputSingle) {
InputSingleValue *value = static_cast<InputSingleValue *>(any_value);
BLI_assert(value->virtual_array.is_single());
return value->virtual_array;
}
if (any_value->type == ValueType::OutputSingle) {
OutputSingleValue *value = static_cast<OutputSingleValue *>(any_value);
BLI_assert(value->is_computed);
BLI_assert(value->span.size() == 1);
return scope.construct<GVArray_For_GSpan>(__func__, value->span);
}
BLI_assert(false);
return scope.construct<GVArray_For_Empty>(__func__, CPPType::get<float>());
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
const MFInputSocket &socket, ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
BLI_assert(any_value != nullptr);
if (any_value->type == ValueType::OwnVector) {
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
if (value->vector_array->size() == 1) {
GSpan span = (*value->vector_array)[0];
return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, span, min_array_size_);
}
return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
if (any_value->type == ValueType::InputVector) {
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
return value->virtual_vector_array;
}
if (any_value->type == ValueType::OutputVector) {
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
const MFInputSocket &socket, ResourceScope &scope)
{
const MFOutputSocket &origin = *socket.origin();
Value *any_value = value_per_output_id_[origin.id()];
BLI_assert(any_value != nullptr);
if (any_value->type == ValueType::OwnVector) {
OwnVectorValue *value = static_cast<OwnVectorValue *>(any_value);
BLI_assert(value->vector_array->size() == 1);
return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
if (any_value->type == ValueType::InputVector) {
InputVectorValue *value = static_cast<InputVectorValue *>(any_value);
BLI_assert(value->virtual_vector_array.is_single_vector());
return value->virtual_vector_array;
}
if (any_value->type == ValueType::OutputVector) {
OutputVectorValue *value = static_cast<OutputVectorValue *>(any_value);
BLI_assert(value->vector_array->size() == 1);
return scope.construct<GVVectorArray_For_GVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
return scope.construct<GVVectorArray_For_SingleGSpan>(__func__, GSpan(CPPType::get<float>()), 0);
}
/** \} */
} // namespace blender::fn