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 4fe8d0419c Functions: refactor virtual array data structures
When a function is executed for many elements (e.g. per point) it is often the case
that some parameters are different for every element and other parameters are
the same (there are some more less common cases). To simplify writing such
functions one can use a "virtual array". This is a data structure that has a value
for every index, but might not be stored as an actual array internally. Instead, it
might be just a single value or is computed on the fly. There are various tradeoffs
involved when using this data structure which are mentioned in `BLI_virtual_array.hh`.
It is called "virtual", because it uses inheritance and virtual methods.

Furthermore, there is a new virtual vector array data structure, which is an array
of vectors. Both these types have corresponding generic variants, which can be used
when the data type is not known at compile time. This is typically the case when
building a somewhat generic execution system. The function system used these virtual
data structures before, but now they are more versatile.

I've done this refactor in preparation for the attribute processor and other features of
geometry nodes. I moved the typed virtual arrays to blenlib, so that they can be used
independent of the function system.

One open question for me is whether all the generic data structures (and `CPPType`)
should be moved to blenlib as well. They are well isolated and don't really contain
any business logic. That can be done later if necessary.
2021-03-21 19:33:13 +01:00

1086 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_collector.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, ResourceCollector &resources);
const GVArray &get_single_input__single(const MFInputSocket &socket,
ResourceCollector &resources);
const GVVectorArray &get_vector_input__full(const MFInputSocket &socket,
ResourceCollector &resources);
const GVVectorArray &get_vector_input__single(const MFInputSocket &socket,
ResourceCollector &resources);
/* 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,
ResourceCollector &resources);
GMutableSpan get_mutable_single__single(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceCollector &resources);
GVectorArray &get_mutable_vector__full(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceCollector &resources);
GVectorArray &get_mutable_vector__single(const MFInputSocket &input,
const MFOutputSocket &output,
ResourceCollector &resources);
/* 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 = this->get_builder("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;
}
}
}
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};
ResourceCollector &resources = params.resources();
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, resources);
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, resources);
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, resources);
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, resources);
params.add_vector_mutable(values);
break;
}
}
}
function.call(IndexRange(1), params, global_context);
}
else {
MFParamsBuilder params{function, storage.mask().min_array_size()};
ResourceCollector &resources = params.resources();
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, resources);
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, resources);
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, resources);
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, resources);
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
{
ResourceCollector resources;
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, resources);
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, resources);
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,
ResourceCollector &resources)
{
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, resources);
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, resources);
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,
ResourceCollector &resources)
{
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, resources);
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, resources);
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,
ResourceCollector &resources)
{
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, resources);
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, resources);
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,
ResourceCollector &resources)
{
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, resources);
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, resources);
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,
ResourceCollector &resources)
{
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 resources.construct<GVArrayForSingleValueRef>(
__func__, value->span.type(), min_array_size_, value->span.data());
}
return resources.construct<GVArrayForGSpan>(__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 resources.construct<GVArrayForGSpan>(__func__, value->span);
}
BLI_assert(false);
return resources.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
}
const GVArray &MFNetworkEvaluationStorage::get_single_input__single(const MFInputSocket &socket,
ResourceCollector &resources)
{
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 resources.construct<GVArrayForGSpan>(__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 resources.construct<GVArrayForGSpan>(__func__, value->span);
}
BLI_assert(false);
return resources.construct<GVArrayForEmpty>(__func__, CPPType::get<float>());
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__full(
const MFInputSocket &socket, ResourceCollector &resources)
{
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 resources.construct<GVVectorArrayForSingleGSpan>(__func__, span, min_array_size_);
}
return resources.construct<GVVectorArrayForGVectorArray>(__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 resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
return resources.construct<GVVectorArrayForSingleGSpan>(
__func__, GSpan(CPPType::get<float>()), 0);
}
const GVVectorArray &MFNetworkEvaluationStorage::get_vector_input__single(
const MFInputSocket &socket, ResourceCollector &resources)
{
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 resources.construct<GVVectorArrayForGVectorArray>(__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 resources.construct<GVVectorArrayForGVectorArray>(__func__, *value->vector_array);
}
BLI_assert(false);
return resources.construct<GVVectorArrayForSingleGSpan>(
__func__, GSpan(CPPType::get<float>()), 0);
}
/** \} */
} // namespace blender::fn