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.
		
			
				
	
	
		
			1084 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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
 |