Blender  V3.3
multi_function_procedure_executor.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
4 
5 #include "BLI_stack.hh"
6 
7 namespace blender::fn {
8 
9 MFProcedureExecutor::MFProcedureExecutor(const MFProcedure &procedure) : procedure_(procedure)
10 {
11  MFSignatureBuilder signature("Procedure Executor");
12 
13  for (const ConstMFParameter &param : procedure.params()) {
14  signature.add("Parameter", MFParamType(param.type, param.variable->data_type()));
15  }
16 
17  signature_ = signature.build();
18  this->set_signature(&signature_);
19 }
20 
21 using IndicesSplitVectors = std::array<Vector<int64_t>, 2>;
22 
23 namespace {
24 enum class ValueType {
25  GVArray = 0,
26  Span = 1,
27  GVVectorArray = 2,
28  GVectorArray = 3,
29  OneSingle = 4,
30  OneVector = 5,
31 };
32 constexpr int tot_variable_value_types = 6;
33 } // namespace
34 
39 struct VariableValue {
40  ValueType type;
41 
42  VariableValue(ValueType type) : type(type)
43  {
44  }
45 };
46 
47 /* This variable is the unmodified virtual array from the caller. */
49  static inline constexpr ValueType static_type = ValueType::GVArray;
50  const GVArray &data;
51 
53  {
55  }
56 };
57 
58 /* This variable has a different value for every index. Some values may be uninitialized. The span
59  * may be owned by the caller. */
61  static inline constexpr ValueType static_type = ValueType::Span;
62  void *data;
63  bool owned;
64 
66  {
67  }
68 };
69 
70 /* This variable is the unmodified virtual vector array from the caller. */
72  static inline constexpr ValueType static_type = ValueType::GVVectorArray;
74 
76  {
77  }
78 };
79 
80 /* This variable has a different vector for every index. */
82  static inline constexpr ValueType static_type = ValueType::GVectorArray;
84  bool owned;
85 
88  {
89  }
90 };
91 
92 /* This variable has the same value for every index. */
94  static inline constexpr ValueType static_type = ValueType::OneSingle;
95  void *data;
96  bool is_initialized = false;
97 
99  {
100  }
101 };
102 
103 /* This variable has the same vector for every index. */
105  static inline constexpr ValueType static_type = ValueType::OneVector;
107 
109  {
110  }
111 };
112 
113 static_assert(std::is_trivially_destructible_v<VariableValue_GVArray>);
114 static_assert(std::is_trivially_destructible_v<VariableValue_Span>);
115 static_assert(std::is_trivially_destructible_v<VariableValue_GVVectorArray>);
116 static_assert(std::is_trivially_destructible_v<VariableValue_GVectorArray>);
117 static_assert(std::is_trivially_destructible_v<VariableValue_OneSingle>);
118 static_assert(std::is_trivially_destructible_v<VariableValue_OneVector>);
119 
120 class VariableState;
121 
127  private:
132  static constexpr inline int min_alignment = 64;
133 
135  LinearAllocator<> &linear_allocator_;
136 
141  std::array<Stack<VariableValue *>, tot_variable_value_types> variable_value_free_lists_;
142 
147  Stack<void *> small_span_buffers_free_list_;
148  Map<int, Stack<void *>> span_buffers_free_lists_;
149 
151  static constexpr inline int small_value_max_size = 16;
152  static constexpr inline int small_value_max_alignment = 8;
153  Stack<void *> small_single_value_free_list_;
154  Map<const CPPType *, Stack<void *>> single_value_free_lists_;
155 
156  public:
157  ValueAllocator(LinearAllocator<> &linear_allocator) : linear_allocator_(linear_allocator)
158  {
159  }
160 
162  {
163  return this->obtain<VariableValue_GVArray>(varray);
164  }
165 
167  {
168  return this->obtain<VariableValue_GVVectorArray>(varray);
169  }
170 
172  {
173  return this->obtain<VariableValue_Span>(buffer, false);
174  }
175 
177  {
178  void *buffer = nullptr;
179 
180  const int64_t element_size = type.size();
181  const int64_t alignment = type.alignment();
182 
183  if (alignment > min_alignment) {
184  /* In this rare case we fallback to not reusing existing buffers. */
185  buffer = linear_allocator_.allocate(element_size * size, alignment);
186  }
187  else {
188  Stack<void *> *stack = type.can_exist_in_buffer(small_value_max_size,
189  small_value_max_alignment) ?
190  &small_span_buffers_free_list_ :
191  span_buffers_free_lists_.lookup_ptr(element_size);
192  if (stack == nullptr || stack->is_empty()) {
193  buffer = linear_allocator_.allocate(
194  std::max<int64_t>(element_size, small_value_max_size) * size, min_alignment);
195  }
196  else {
197  /* Reuse existing buffer. */
198  buffer = stack->pop();
199  }
200  }
201 
202  return this->obtain<VariableValue_Span>(buffer, true);
203  }
204 
206  {
207  return this->obtain<VariableValue_GVectorArray>(data, false);
208  }
209 
211  {
212  GVectorArray *vector_array = new GVectorArray(type, size);
213  return this->obtain<VariableValue_GVectorArray>(*vector_array, true);
214  }
215 
217  {
218  const bool is_small = type.can_exist_in_buffer(small_value_max_size,
219  small_value_max_alignment);
220  Stack<void *> &stack = is_small ? small_single_value_free_list_ :
221  single_value_free_lists_.lookup_or_add_default(&type);
222  void *buffer;
223  if (stack.is_empty()) {
224  buffer = linear_allocator_.allocate(
225  std::max<int>(small_value_max_size, type.size()),
226  std::max<int>(small_value_max_alignment, type.alignment()));
227  }
228  else {
229  buffer = stack.pop();
230  }
231  return this->obtain<VariableValue_OneSingle>(buffer);
232  }
233 
235  {
236  GVectorArray *vector_array = new GVectorArray(type, 1);
237  return this->obtain<VariableValue_OneVector>(*vector_array);
238  }
239 
240  void release_value(VariableValue *value, const MFDataType &data_type)
241  {
242  switch (value->type) {
243  case ValueType::GVArray: {
244  break;
245  }
246  case ValueType::Span: {
247  auto *value_typed = static_cast<VariableValue_Span *>(value);
248  if (value_typed->owned) {
249  const CPPType &type = data_type.single_type();
250  /* Assumes all values in the buffer are uninitialized already. */
251  Stack<void *> &buffers = type.can_exist_in_buffer(small_value_max_size,
252  small_value_max_alignment) ?
253  small_span_buffers_free_list_ :
254  span_buffers_free_lists_.lookup_or_add_default(type.size());
255  buffers.push(value_typed->data);
256  }
257  break;
258  }
259  case ValueType::GVVectorArray: {
260  break;
261  }
262  case ValueType::GVectorArray: {
263  auto *value_typed = static_cast<VariableValue_GVectorArray *>(value);
264  if (value_typed->owned) {
265  delete &value_typed->data;
266  }
267  break;
268  }
269  case ValueType::OneSingle: {
270  auto *value_typed = static_cast<VariableValue_OneSingle *>(value);
271  const CPPType &type = data_type.single_type();
272  if (value_typed->is_initialized) {
273  type.destruct(value_typed->data);
274  }
275  const bool is_small = type.can_exist_in_buffer(small_value_max_size,
276  small_value_max_alignment);
277  if (is_small) {
278  small_single_value_free_list_.push(value_typed->data);
279  }
280  else {
281  single_value_free_lists_.lookup_or_add_default(&type).push(value_typed->data);
282  }
283  break;
284  }
285  case ValueType::OneVector: {
286  auto *value_typed = static_cast<VariableValue_OneVector *>(value);
287  delete &value_typed->data;
288  break;
289  }
290  }
291 
292  Stack<VariableValue *> &stack = variable_value_free_lists_[(int)value->type];
293  stack.push(value);
294  }
295 
296  private:
297  template<typename T, typename... Args> T *obtain(Args &&...args)
298  {
299  static_assert(std::is_base_of_v<VariableValue, T>);
300  Stack<VariableValue *> &stack = variable_value_free_lists_[(int)T::static_type];
301  if (stack.is_empty()) {
302  void *buffer = linear_allocator_.allocate(sizeof(T), alignof(T));
303  return new (buffer) T(std::forward<Args>(args)...);
304  }
305  return new (stack.pop()) T(std::forward<Args>(args)...);
306  }
307 };
308 
313  public:
315  VariableValue *value_ = nullptr;
318  /* This a non-owning pointer to either span buffer or #GVectorArray or null. */
319  void *caller_provided_storage_ = nullptr;
320 
321  void destruct_value(ValueAllocator &value_allocator, const MFDataType &data_type)
322  {
323  value_allocator.release_value(value_, data_type);
324  value_ = nullptr;
325  }
326 
327  /* True if this contains only one value for all indices, i.e. the value for all indices is
328  * the same. */
329  bool is_one() const
330  {
331  if (value_ == nullptr) {
332  return true;
333  }
334  switch (value_->type) {
335  case ValueType::GVArray:
336  return this->value_as<VariableValue_GVArray>()->data.is_single();
337  case ValueType::Span:
338  return tot_initialized_ == 0;
339  case ValueType::GVVectorArray:
340  return this->value_as<VariableValue_GVVectorArray>()->data.is_single_vector();
341  case ValueType::GVectorArray:
342  return tot_initialized_ == 0;
343  case ValueType::OneSingle:
344  return true;
345  case ValueType::OneVector:
346  return true;
347  }
349  return false;
350  }
351 
352  bool is_fully_initialized(const IndexMask full_mask)
353  {
354  return tot_initialized_ == full_mask.size();
355  }
356 
357  bool is_fully_uninitialized(const IndexMask full_mask)
358  {
359  UNUSED_VARS(full_mask);
360  return tot_initialized_ == 0;
361  }
362 
363  void add_as_input(MFParamsBuilder &params, IndexMask mask, const MFDataType &data_type) const
364  {
365  /* Sanity check to make sure that enough values are initialized. */
366  BLI_assert(mask.size() <= tot_initialized_);
367  BLI_assert(value_ != nullptr);
368 
369  switch (value_->type) {
370  case ValueType::GVArray: {
371  params.add_readonly_single_input(this->value_as<VariableValue_GVArray>()->data);
372  break;
373  }
374  case ValueType::Span: {
375  const void *data = this->value_as<VariableValue_Span>()->data;
376  const GSpan span{data_type.single_type(), data, mask.min_array_size()};
377  params.add_readonly_single_input(span);
378  break;
379  }
380  case ValueType::GVVectorArray: {
381  params.add_readonly_vector_input(this->value_as<VariableValue_GVVectorArray>()->data);
382  break;
383  }
384  case ValueType::GVectorArray: {
385  params.add_readonly_vector_input(this->value_as<VariableValue_GVectorArray>()->data);
386  break;
387  }
388  case ValueType::OneSingle: {
389  const auto *value_typed = this->value_as<VariableValue_OneSingle>();
390  BLI_assert(value_typed->is_initialized);
391  const GPointer gpointer{data_type.single_type(), value_typed->data};
392  params.add_readonly_single_input(gpointer);
393  break;
394  }
395  case ValueType::OneVector: {
396  params.add_readonly_vector_input(this->value_as<VariableValue_OneVector>()->data[0]);
397  break;
398  }
399  }
400  }
401 
402  void ensure_is_mutable(IndexMask full_mask,
403  const MFDataType &data_type,
404  ValueAllocator &value_allocator)
405  {
406  if (value_ != nullptr && ELEM(value_->type, ValueType::Span, ValueType::GVectorArray)) {
407  return;
408  }
409 
410  const int array_size = full_mask.min_array_size();
411 
412  switch (data_type.category()) {
413  case MFDataType::Single: {
414  const CPPType &type = data_type.single_type();
415  VariableValue_Span *new_value = nullptr;
416  if (caller_provided_storage_ == nullptr) {
417  new_value = value_allocator.obtain_Span(type, array_size);
418  }
419  else {
420  /* Reuse the storage provided caller when possible. */
421  new_value = value_allocator.obtain_Span_not_owned(caller_provided_storage_);
422  }
423  if (value_ != nullptr) {
424  if (value_->type == ValueType::GVArray) {
425  /* Fill new buffer with data from virtual array. */
426  this->value_as<VariableValue_GVArray>()->data.materialize_to_uninitialized(
427  full_mask, new_value->data);
428  }
429  else if (value_->type == ValueType::OneSingle) {
430  auto *old_value_typed_ = this->value_as<VariableValue_OneSingle>();
431  if (old_value_typed_->is_initialized) {
432  /* Fill the buffer with a single value. */
433  type.fill_construct_indices(old_value_typed_->data, new_value->data, full_mask);
434  }
435  }
436  else {
438  }
439  value_allocator.release_value(value_, data_type);
440  }
441  value_ = new_value;
442  break;
443  }
444  case MFDataType::Vector: {
445  const CPPType &type = data_type.vector_base_type();
446  VariableValue_GVectorArray *new_value = nullptr;
447  if (caller_provided_storage_ == nullptr) {
448  new_value = value_allocator.obtain_GVectorArray(type, array_size);
449  }
450  else {
451  new_value = value_allocator.obtain_GVectorArray_not_owned(
453  }
454  if (value_ != nullptr) {
455  if (value_->type == ValueType::GVVectorArray) {
456  /* Fill new vector array with data from virtual vector array. */
457  new_value->data.extend(full_mask, this->value_as<VariableValue_GVVectorArray>()->data);
458  }
459  else if (value_->type == ValueType::OneVector) {
460  /* Fill all indices with the same value. */
461  const GSpan vector = this->value_as<VariableValue_OneVector>()->data[0];
462  new_value->data.extend(full_mask, GVVectorArray_For_SingleGSpan{vector, array_size});
463  }
464  else {
466  }
467  value_allocator.release_value(value_, data_type);
468  }
469  value_ = new_value;
470  break;
471  }
472  }
473  }
474 
476  IndexMask mask,
477  IndexMask full_mask,
478  const MFDataType &data_type,
479  ValueAllocator &value_allocator)
480  {
481  /* Sanity check to make sure that enough values are initialized. */
482  BLI_assert(mask.size() <= tot_initialized_);
483 
484  this->ensure_is_mutable(full_mask, data_type, value_allocator);
485  BLI_assert(value_ != nullptr);
486 
487  switch (value_->type) {
488  case ValueType::Span: {
489  void *data = this->value_as<VariableValue_Span>()->data;
490  const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()};
491  params.add_single_mutable(span);
492  break;
493  }
494  case ValueType::GVectorArray: {
495  params.add_vector_mutable(this->value_as<VariableValue_GVectorArray>()->data);
496  break;
497  }
498  case ValueType::GVArray:
499  case ValueType::GVVectorArray:
500  case ValueType::OneSingle:
501  case ValueType::OneVector: {
503  break;
504  }
505  }
506  }
507 
509  IndexMask mask,
510  IndexMask full_mask,
511  const MFDataType &data_type,
512  ValueAllocator &value_allocator)
513  {
514  /* Sanity check to make sure that enough values are not initialized. */
515  BLI_assert(mask.size() <= full_mask.size() - tot_initialized_);
516  this->ensure_is_mutable(full_mask, data_type, value_allocator);
517  BLI_assert(value_ != nullptr);
518 
519  switch (value_->type) {
520  case ValueType::Span: {
521  void *data = this->value_as<VariableValue_Span>()->data;
522  const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()};
523  params.add_uninitialized_single_output(span);
524  break;
525  }
526  case ValueType::GVectorArray: {
527  params.add_vector_output(this->value_as<VariableValue_GVectorArray>()->data);
528  break;
529  }
530  case ValueType::GVArray:
531  case ValueType::GVVectorArray:
532  case ValueType::OneSingle:
533  case ValueType::OneVector: {
535  break;
536  }
537  }
538 
539  tot_initialized_ += mask.size();
540  }
541 
542  void add_as_input__one(MFParamsBuilder &params, const MFDataType &data_type) const
543  {
544  BLI_assert(this->is_one());
545  BLI_assert(value_ != nullptr);
546 
547  switch (value_->type) {
548  case ValueType::GVArray: {
549  params.add_readonly_single_input(this->value_as<VariableValue_GVArray>()->data);
550  break;
551  }
552  case ValueType::GVVectorArray: {
553  params.add_readonly_vector_input(this->value_as<VariableValue_GVVectorArray>()->data);
554  break;
555  }
556  case ValueType::OneSingle: {
557  const auto *value_typed = this->value_as<VariableValue_OneSingle>();
558  BLI_assert(value_typed->is_initialized);
559  GPointer ptr{data_type.single_type(), value_typed->data};
560  params.add_readonly_single_input(ptr);
561  break;
562  }
563  case ValueType::OneVector: {
564  params.add_readonly_vector_input(this->value_as<VariableValue_OneVector>()->data);
565  break;
566  }
567  case ValueType::Span:
568  case ValueType::GVectorArray: {
570  break;
571  }
572  }
573  }
574 
575  void ensure_is_mutable__one(const MFDataType &data_type, ValueAllocator &value_allocator)
576  {
577  BLI_assert(this->is_one());
578  if (value_ != nullptr && ELEM(value_->type, ValueType::OneSingle, ValueType::OneVector)) {
579  return;
580  }
581 
582  switch (data_type.category()) {
583  case MFDataType::Single: {
584  const CPPType &type = data_type.single_type();
585  VariableValue_OneSingle *new_value = value_allocator.obtain_OneSingle(type);
586  if (value_ != nullptr) {
587  if (value_->type == ValueType::GVArray) {
588  this->value_as<VariableValue_GVArray>()->data.get_internal_single_to_uninitialized(
589  new_value->data);
590  new_value->is_initialized = true;
591  }
592  else if (value_->type == ValueType::Span) {
594  /* Nothing to do, the single value is uninitialized already. */
595  }
596  else {
598  }
599  value_allocator.release_value(value_, data_type);
600  }
601  value_ = new_value;
602  break;
603  }
604  case MFDataType::Vector: {
605  const CPPType &type = data_type.vector_base_type();
606  VariableValue_OneVector *new_value = value_allocator.obtain_OneVector(type);
607  if (value_ != nullptr) {
608  if (value_->type == ValueType::GVVectorArray) {
609  const GVVectorArray &old_vector_array =
610  this->value_as<VariableValue_GVVectorArray>()->data;
611  new_value->data.extend(IndexRange(1), old_vector_array);
612  }
613  else if (value_->type == ValueType::GVectorArray) {
615  /* Nothing to do. */
616  }
617  else {
619  }
620  value_allocator.release_value(value_, data_type);
621  }
622  value_ = new_value;
623  break;
624  }
625  }
626  }
627 
629  const MFDataType &data_type,
630  ValueAllocator &value_allocator)
631  {
632  BLI_assert(this->is_one());
633  this->ensure_is_mutable__one(data_type, value_allocator);
634  BLI_assert(value_ != nullptr);
635 
636  switch (value_->type) {
637  case ValueType::OneSingle: {
638  auto *value_typed = this->value_as<VariableValue_OneSingle>();
639  BLI_assert(value_typed->is_initialized);
640  params.add_single_mutable(GMutableSpan{data_type.single_type(), value_typed->data, 1});
641  break;
642  }
643  case ValueType::OneVector: {
644  params.add_vector_mutable(this->value_as<VariableValue_OneVector>()->data);
645  break;
646  }
647  case ValueType::GVArray:
648  case ValueType::Span:
649  case ValueType::GVVectorArray:
650  case ValueType::GVectorArray: {
652  break;
653  }
654  }
655  }
656 
658  IndexMask mask,
659  const MFDataType &data_type,
660  ValueAllocator &value_allocator)
661  {
662  BLI_assert(this->is_one());
663  this->ensure_is_mutable__one(data_type, value_allocator);
664  BLI_assert(value_ != nullptr);
665 
666  switch (value_->type) {
667  case ValueType::OneSingle: {
668  auto *value_typed = this->value_as<VariableValue_OneSingle>();
669  BLI_assert(!value_typed->is_initialized);
670  params.add_uninitialized_single_output(
671  GMutableSpan{data_type.single_type(), value_typed->data, 1});
672  /* It becomes initialized when the multi-function is called. */
673  value_typed->is_initialized = true;
674  break;
675  }
676  case ValueType::OneVector: {
677  auto *value_typed = this->value_as<VariableValue_OneVector>();
678  BLI_assert(value_typed->data[0].is_empty());
679  params.add_vector_output(value_typed->data);
680  break;
681  }
682  case ValueType::GVArray:
683  case ValueType::Span:
684  case ValueType::GVVectorArray:
685  case ValueType::GVectorArray: {
687  break;
688  }
689  }
690 
691  tot_initialized_ += mask.size();
692  }
693 
700  IndexMask full_mask,
701  const MFDataType &data_type,
702  ValueAllocator &value_allocator)
703  {
704  BLI_assert(value_ != nullptr);
705  int new_tot_initialized = tot_initialized_ - mask.size();
706 
707  /* Sanity check to make sure that enough indices can be destructed. */
708  BLI_assert(new_tot_initialized >= 0);
709 
710  switch (value_->type) {
711  case ValueType::GVArray: {
712  if (mask.size() < full_mask.size()) {
713  /* Not all elements are destructed. Since we can't work on the original array, we have to
714  * create a copy first. */
715  this->ensure_is_mutable(full_mask, data_type, value_allocator);
716  BLI_assert(value_->type == ValueType::Span);
717  const CPPType &type = data_type.single_type();
718  type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask);
719  }
720  break;
721  }
722  case ValueType::Span: {
723  const CPPType &type = data_type.single_type();
724  type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask);
725  break;
726  }
727  case ValueType::GVVectorArray: {
728  if (mask.size() < full_mask.size()) {
729  /* Not all elements are cleared. Since we can't work on the original vector array, we
730  * have to create a copy first. A possible future optimization is to create the partial
731  * copy directly. */
732  this->ensure_is_mutable(full_mask, data_type, value_allocator);
733  BLI_assert(value_->type == ValueType::GVectorArray);
734  this->value_as<VariableValue_GVectorArray>()->data.clear(mask);
735  }
736  break;
737  }
738  case ValueType::GVectorArray: {
739  this->value_as<VariableValue_GVectorArray>()->data.clear(mask);
740  break;
741  }
742  case ValueType::OneSingle: {
743  auto *value_typed = this->value_as<VariableValue_OneSingle>();
744  BLI_assert(value_typed->is_initialized);
745  UNUSED_VARS_NDEBUG(value_typed);
746  if (mask.size() == tot_initialized_) {
747  const CPPType &type = data_type.single_type();
748  type.destruct(value_typed->data);
749  value_typed->is_initialized = false;
750  }
751  break;
752  }
753  case ValueType::OneVector: {
754  auto *value_typed = this->value_as<VariableValue_OneVector>();
755  if (mask.size() == tot_initialized_) {
756  value_typed->data.clear(IndexRange(1));
757  }
758  break;
759  }
760  }
761 
762  tot_initialized_ = new_tot_initialized;
763 
764  const bool should_self_destruct = new_tot_initialized == 0 &&
765  caller_provided_storage_ == nullptr;
766  return should_self_destruct;
767  }
768 
770  {
771  BLI_assert(mask.size() <= tot_initialized_);
772  BLI_assert(value_ != nullptr);
773 
774  switch (value_->type) {
775  case ValueType::GVArray: {
776  const VArray<bool> varray = this->value_as<VariableValue_GVArray>()->data.typed<bool>();
777  for (const int i : mask) {
778  r_indices[varray[i]].append(i);
779  }
780  break;
781  }
782  case ValueType::Span: {
783  const Span<bool> span((bool *)this->value_as<VariableValue_Span>()->data,
784  mask.min_array_size());
785  for (const int i : mask) {
786  r_indices[span[i]].append(i);
787  }
788  break;
789  }
790  case ValueType::OneSingle: {
791  auto *value_typed = this->value_as<VariableValue_OneSingle>();
792  BLI_assert(value_typed->is_initialized);
793  const bool condition = *(bool *)value_typed->data;
794  r_indices[condition].extend(mask);
795  break;
796  }
797  case ValueType::GVVectorArray:
798  case ValueType::GVectorArray:
799  case ValueType::OneVector: {
801  break;
802  }
803  }
804  }
805 
806  template<typename T> T *value_as()
807  {
808  BLI_assert(value_ != nullptr);
809  BLI_assert(value_->type == T::static_type);
810  return static_cast<T *>(value_);
811  }
812 
813  template<typename T> const T *value_as() const
814  {
815  BLI_assert(value_ != nullptr);
816  BLI_assert(value_->type == T::static_type);
817  return static_cast<T *>(value_);
818  }
819 };
820 
823  private:
824  ValueAllocator value_allocator_;
825  const MFProcedure &procedure_;
827  Array<VariableState> variable_states_;
828  IndexMask full_mask_;
829 
830  public:
832  const MFProcedure &procedure,
834  : value_allocator_(linear_allocator),
835  procedure_(procedure),
836  variable_states_(procedure.variables().size()),
837  full_mask_(full_mask)
838  {
839  }
840 
842  {
843  for (const int variable_i : procedure_.variables().index_range()) {
844  VariableState &state = variable_states_[variable_i];
845  if (state.value_ != nullptr) {
846  const MFVariable *variable = procedure_.variables()[variable_i];
847  state.destruct_value(value_allocator_, variable->data_type());
848  }
849  }
850  }
851 
853  {
854  return value_allocator_;
855  }
856 
857  const IndexMask &full_mask() const
858  {
859  return full_mask_;
860  }
861 
863  const MFProcedure &procedure,
864  MFParams &params)
865  {
866  for (const int param_index : fn.param_indices()) {
867  MFParamType param_type = fn.param_type(param_index);
868  const MFVariable *variable = procedure.params()[param_index].variable;
869 
870  auto add_state = [&](VariableValue *value,
871  bool input_is_initialized,
872  void *caller_provided_storage = nullptr) {
873  const int tot_initialized = input_is_initialized ? full_mask_.size() : 0;
874  const int variable_i = variable->index_in_procedure();
875  VariableState &variable_state = variable_states_[variable_i];
876  BLI_assert(variable_state.value_ == nullptr);
877  variable_state.value_ = value;
878  variable_state.tot_initialized_ = tot_initialized;
879  variable_state.caller_provided_storage_ = caller_provided_storage;
880  };
881 
882  switch (param_type.category()) {
884  const GVArray &data = params.readonly_single_input(param_index);
885  add_state(value_allocator_.obtain_GVArray(data), true);
886  break;
887  }
889  const GVVectorArray &data = params.readonly_vector_input(param_index);
890  add_state(value_allocator_.obtain_GVVectorArray(data), true);
891  break;
892  }
894  GMutableSpan data = params.uninitialized_single_output(param_index);
895  add_state(value_allocator_.obtain_Span_not_owned(data.data()), false, data.data());
896  break;
897  }
899  GVectorArray &data = params.vector_output(param_index);
900  add_state(value_allocator_.obtain_GVectorArray_not_owned(data), false, &data);
901  break;
902  }
904  GMutableSpan data = params.single_mutable(param_index);
905  add_state(value_allocator_.obtain_Span_not_owned(data.data()), true, data.data());
906  break;
907  }
909  GVectorArray &data = params.vector_mutable(param_index);
910  add_state(value_allocator_.obtain_GVectorArray_not_owned(data), true, &data);
911  break;
912  }
913  }
914  }
915  }
916 
917  void add_as_param(VariableState &variable_state,
919  const MFParamType &param_type,
920  const IndexMask &mask)
921  {
922  const MFDataType data_type = param_type.data_type();
923  switch (param_type.interface_type()) {
924  case MFParamType::Input: {
925  variable_state.add_as_input(params, mask, data_type);
926  break;
927  }
928  case MFParamType::Mutable: {
929  variable_state.add_as_mutable(params, mask, full_mask_, data_type, value_allocator_);
930  break;
931  }
932  case MFParamType::Output: {
933  variable_state.add_as_output(params, mask, full_mask_, data_type, value_allocator_);
934  break;
935  }
936  }
937  }
938 
939  void add_as_param__one(VariableState &variable_state,
941  const MFParamType &param_type,
942  const IndexMask &mask)
943  {
944  const MFDataType data_type = param_type.data_type();
945  switch (param_type.interface_type()) {
946  case MFParamType::Input: {
947  variable_state.add_as_input__one(params, data_type);
948  break;
949  }
950  case MFParamType::Mutable: {
951  variable_state.add_as_mutable__one(params, data_type, value_allocator_);
952  break;
953  }
954  case MFParamType::Output: {
955  variable_state.add_as_output__one(params, mask, data_type, value_allocator_);
956  break;
957  }
958  }
959  }
960 
961  void destruct(const MFVariable &variable, const IndexMask &mask)
962  {
963  VariableState &variable_state = this->get_variable_state(variable);
964  if (variable_state.destruct(mask, full_mask_, variable.data_type(), value_allocator_)) {
965  variable_state.destruct_value(value_allocator_, variable.data_type());
966  }
967  }
968 
970  {
971  const int variable_i = variable.index_in_procedure();
972  VariableState &variable_state = variable_states_[variable_i];
973  return variable_state;
974  }
975 };
976 
977 static bool evaluate_as_one(const MultiFunction &fn,
978  Span<VariableState *> param_variable_states,
979  const IndexMask &mask,
980  const IndexMask &full_mask)
981 {
982  if (fn.depends_on_context()) {
983  return false;
984  }
985  if (mask.size() < full_mask.size()) {
986  return false;
987  }
988  for (VariableState *state : param_variable_states) {
989  if (state != nullptr && state->value_ != nullptr && !state->is_one()) {
990  return false;
991  }
992  }
993  return true;
994 }
995 
997  const MFCallInstruction &instruction,
998  VariableStates &variable_states,
999  MutableSpan<VariableState *> r_param_variable_states)
1000 {
1001  for (const int param_index : fn.param_indices()) {
1002  const MFVariable *variable = instruction.params()[param_index];
1003  if (variable == nullptr) {
1004  r_param_variable_states[param_index] = nullptr;
1005  }
1006  else {
1007  VariableState &variable_state = variable_states.get_variable_state(*variable);
1008  r_param_variable_states[param_index] = &variable_state;
1009  }
1010  }
1011 }
1012 
1013 static void fill_params__one(const MultiFunction &fn,
1014  const IndexMask mask,
1016  VariableStates &variable_states,
1017  const Span<VariableState *> param_variable_states)
1018 {
1019  for (const int param_index : fn.param_indices()) {
1020  const MFParamType param_type = fn.param_type(param_index);
1021  VariableState *variable_state = param_variable_states[param_index];
1022  if (variable_state == nullptr) {
1023  params.add_ignored_single_output();
1024  }
1025  else {
1026  variable_states.add_as_param__one(*variable_state, params, param_type, mask);
1027  }
1028  }
1029 }
1030 
1031 static void fill_params(const MultiFunction &fn,
1032  const IndexMask mask,
1034  VariableStates &variable_states,
1035  const Span<VariableState *> param_variable_states)
1036 {
1037  for (const int param_index : fn.param_indices()) {
1038  const MFParamType param_type = fn.param_type(param_index);
1039  VariableState *variable_state = param_variable_states[param_index];
1040  if (variable_state == nullptr) {
1041  params.add_ignored_single_output();
1042  }
1043  else {
1044  variable_states.add_as_param(*variable_state, params, param_type, mask);
1045  }
1046  }
1047 }
1048 
1049 static void execute_call_instruction(const MFCallInstruction &instruction,
1050  const IndexMask mask,
1051  VariableStates &variable_states,
1052  const MFContext &context)
1053 {
1054  const MultiFunction &fn = instruction.fn();
1055 
1056  Vector<VariableState *> param_variable_states;
1057  param_variable_states.resize(fn.param_amount());
1058  gather_parameter_variable_states(fn, instruction, variable_states, param_variable_states);
1059 
1060  /* If all inputs to the function are constant, it's enough to call the function only once instead
1061  * of for every index. */
1062  if (evaluate_as_one(fn, param_variable_states, mask, variable_states.full_mask())) {
1063  MFParamsBuilder params(fn, 1);
1064  fill_params__one(fn, mask, params, variable_states, param_variable_states);
1065 
1066  try {
1067  fn.call(IndexRange(1), params, context);
1068  }
1069  catch (...) {
1070  /* Multi-functions must not throw exceptions. */
1072  }
1073  }
1074  else {
1075  MFParamsBuilder params(fn, &mask);
1076  fill_params(fn, mask, params, variable_states, param_variable_states);
1077 
1078  try {
1079  fn.call_auto(mask, params, context);
1080  }
1081  catch (...) {
1082  /* Multi-functions must not throw exceptions. */
1084  }
1085  }
1086 }
1087 
1090  bool is_owned;
1093 
1094  IndexMask mask() const
1095  {
1096  if (this->is_owned) {
1097  return this->owned_indices.as_span();
1098  }
1099  return this->referenced_indices;
1100  }
1101 };
1102 
1105  const MFInstruction *instruction = nullptr;
1107 
1108  IndexMask mask() const
1109  {
1110  return this->indices.mask();
1111  }
1112 
1113  operator bool() const
1114  {
1115  return this->instruction != nullptr;
1116  }
1117 };
1118 
1124  private:
1125  Stack<NextInstructionInfo> next_instructions_;
1126 
1127  public:
1129 
1131  {
1132  if (mask.is_empty()) {
1133  return;
1134  }
1135  InstructionIndices new_indices;
1136  new_indices.is_owned = false;
1137  new_indices.referenced_indices = mask;
1138  next_instructions_.push({&instruction, std::move(new_indices)});
1139  }
1140 
1142  {
1143  if (indices.is_empty()) {
1144  return;
1145  }
1147 
1148  InstructionIndices new_indices;
1149  new_indices.is_owned = true;
1150  new_indices.owned_indices = std::move(indices);
1151  next_instructions_.push({&instruction, std::move(new_indices)});
1152  }
1153 
1154  bool is_done() const
1155  {
1156  return next_instructions_.is_empty();
1157  }
1158 
1159  const NextInstructionInfo &peek() const
1160  {
1161  BLI_assert(!this->is_done());
1162  return next_instructions_.peek();
1163  }
1164 
1166  {
1167  next_instructions_.peek().instruction = &instruction;
1168  }
1169 
1171  {
1172  return next_instructions_.pop();
1173  }
1174 };
1175 
1177 {
1178  BLI_assert(procedure_.validate());
1179 
1180  AlignedBuffer<512, 64> local_buffer;
1181  LinearAllocator<> linear_allocator;
1182  linear_allocator.provide_buffer(local_buffer);
1183 
1184  VariableStates variable_states{linear_allocator, procedure_, full_mask};
1185  variable_states.add_initial_variable_states(*this, procedure_, params);
1186 
1187  InstructionScheduler scheduler;
1188  scheduler.add_referenced_indices(*procedure_.entry(), full_mask);
1189 
1190  /* Loop until all indices got to a return instruction. */
1191  while (!scheduler.is_done()) {
1192  const NextInstructionInfo &instr_info = scheduler.peek();
1193  const MFInstruction &instruction = *instr_info.instruction;
1194  switch (instruction.type()) {
1195  case MFInstructionType::Call: {
1196  const MFCallInstruction &call_instruction = static_cast<const MFCallInstruction &>(
1197  instruction);
1198  execute_call_instruction(call_instruction, instr_info.mask(), variable_states, context);
1199  scheduler.update_instruction_pointer(*call_instruction.next());
1200  break;
1201  }
1203  const MFBranchInstruction &branch_instruction = static_cast<const MFBranchInstruction &>(
1204  instruction);
1205  const MFVariable *condition_var = branch_instruction.condition();
1206  VariableState &variable_state = variable_states.get_variable_state(*condition_var);
1207 
1208  IndicesSplitVectors new_indices;
1209  variable_state.indices_split(instr_info.mask(), new_indices);
1210  scheduler.pop();
1211  scheduler.add_owned_indices(*branch_instruction.branch_false(), new_indices[false]);
1212  scheduler.add_owned_indices(*branch_instruction.branch_true(), new_indices[true]);
1213  break;
1214  }
1216  const MFDestructInstruction &destruct_instruction =
1217  static_cast<const MFDestructInstruction &>(instruction);
1218  const MFVariable *variable = destruct_instruction.variable();
1219  variable_states.destruct(*variable, instr_info.mask());
1220  scheduler.update_instruction_pointer(*destruct_instruction.next());
1221  break;
1222  }
1223  case MFInstructionType::Dummy: {
1224  const MFDummyInstruction &dummy_instruction = static_cast<const MFDummyInstruction &>(
1225  instruction);
1226  scheduler.update_instruction_pointer(*dummy_instruction.next());
1227  break;
1228  }
1230  /* Don't insert the indices back into the scheduler. */
1231  scheduler.pop();
1232  break;
1233  }
1234  }
1235  }
1236 
1237  for (const int param_index : this->param_indices()) {
1238  const MFParamType param_type = this->param_type(param_index);
1239  const MFVariable *variable = procedure_.params()[param_index].variable;
1240  VariableState &variable_state = variable_states.get_variable_state(*variable);
1241  switch (param_type.interface_type()) {
1242  case MFParamType::Input: {
1243  /* Input variables must be destructed in the end. */
1244  BLI_assert(variable_state.is_fully_uninitialized(full_mask));
1245  break;
1246  }
1247  case MFParamType::Mutable:
1248  case MFParamType::Output: {
1249  /* Mutable and output variables must be initialized in the end. */
1250  BLI_assert(variable_state.is_fully_initialized(full_mask));
1251  /* Make sure that the data is in the memory provided by the caller. */
1252  variable_state.ensure_is_mutable(
1253  full_mask, param_type.data_type(), variable_states.value_allocator());
1254  break;
1255  }
1256  }
1257  }
1258 }
1259 
1260 MultiFunction::ExecutionHints MFProcedureExecutor::get_execution_hints() const
1261 {
1262  ExecutionHints hints;
1263  hints.allocates_array = true;
1264  hints.min_grain_size = 10000;
1265  return hints;
1266 }
1267 
1268 } // namespace blender::fn
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define UNUSED_VARS(...)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a vector
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
void extend(int64_t index, const GVArray &values)
static bool indices_are_valid_index_mask(Span< int64_t > indices)
int64_t size() const
int64_t min_array_size() const
void provide_buffer(void *buffer, uint size)
void * allocate(const int64_t size, const int64_t alignment)
Value & lookup_or_add_default(const Key &key)
Definition: BLI_map.hh:580
const Value * lookup_ptr(const Key &key) const
Definition: BLI_map.hh:463
bool is_empty() const
Definition: BLI_stack.hh:308
void push(const T &value)
Definition: BLI_stack.hh:213
Span< T > as_span() const
Definition: BLI_vector.hh:325
void resize(const int64_t new_size)
Definition: BLI_vector.hh:353
void update_instruction_pointer(const MFInstruction &instruction)
void add_referenced_indices(const MFInstruction &instruction, IndexMask mask)
void add_owned_indices(const MFInstruction &instruction, Vector< int64_t > indices)
const CPPType & vector_base_type() const
const CPPType & single_type() const
MFProcedureExecutor(const MFProcedure &procedure)
void call(IndexMask mask, MFParams params, MFContext context) const override
Span< ConstMFParameter > params() const
MFParamType param_type(int param_index) const
IndexRange param_indices() const
void call_auto(IndexMask mask, MFParams params, MFContext context) const
void set_signature(const MFSignature *signature)
virtual void call(IndexMask mask, MFParams params, MFContext context) const =0
const MFSignature & signature() const
VariableValue_OneSingle * obtain_OneSingle(const CPPType &type)
ValueAllocator(LinearAllocator<> &linear_allocator)
VariableValue_GVectorArray * obtain_GVectorArray(const CPPType &type, int size)
VariableValue_GVectorArray * obtain_GVectorArray_not_owned(GVectorArray &data)
VariableValue_GVArray * obtain_GVArray(const GVArray &varray)
VariableValue_Span * obtain_Span_not_owned(void *buffer)
void release_value(VariableValue *value, const MFDataType &data_type)
VariableValue_OneVector * obtain_OneVector(const CPPType &type)
VariableValue_Span * obtain_Span(const CPPType &type, int size)
VariableValue_GVVectorArray * obtain_GVVectorArray(const GVVectorArray &varray)
void add_as_input(MFParamsBuilder &params, IndexMask mask, const MFDataType &data_type) const
void ensure_is_mutable(IndexMask full_mask, const MFDataType &data_type, ValueAllocator &value_allocator)
bool is_fully_uninitialized(const IndexMask full_mask)
void add_as_output(MFParamsBuilder &params, IndexMask mask, IndexMask full_mask, const MFDataType &data_type, ValueAllocator &value_allocator)
void add_as_mutable(MFParamsBuilder &params, IndexMask mask, IndexMask full_mask, const MFDataType &data_type, ValueAllocator &value_allocator)
bool destruct(IndexMask mask, IndexMask full_mask, const MFDataType &data_type, ValueAllocator &value_allocator)
void add_as_mutable__one(MFParamsBuilder &params, const MFDataType &data_type, ValueAllocator &value_allocator)
bool is_fully_initialized(const IndexMask full_mask)
void add_as_input__one(MFParamsBuilder &params, const MFDataType &data_type) const
void ensure_is_mutable__one(const MFDataType &data_type, ValueAllocator &value_allocator)
void add_as_output__one(MFParamsBuilder &params, IndexMask mask, const MFDataType &data_type, ValueAllocator &value_allocator)
void indices_split(IndexMask mask, IndicesSplitVectors &r_indices)
void destruct_value(ValueAllocator &value_allocator, const MFDataType &data_type)
void destruct(const MFVariable &variable, const IndexMask &mask)
void add_as_param__one(VariableState &variable_state, MFParamsBuilder &params, const MFParamType &param_type, const IndexMask &mask)
VariableStates(LinearAllocator<> &linear_allocator, const MFProcedure &procedure, IndexMask full_mask)
VariableState & get_variable_state(const MFVariable &variable)
void add_as_param(VariableState &variable_state, MFParamsBuilder &params, const MFParamType &param_type, const IndexMask &mask)
void add_initial_variable_states(const MFProcedureExecutor &fn, const MFProcedure &procedure, MFParams &params)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_global float * buffer
const int state
ccl_gpu_kernel_postfix int ccl_global int * indices
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
#define T
static void execute_call_instruction(const MFCallInstruction &instruction, const IndexMask mask, VariableStates &variable_states, const MFContext &context)
static void fill_params(const MultiFunction &fn, const IndexMask mask, MFParamsBuilder &params, VariableStates &variable_states, const Span< VariableState * > param_variable_states)
static void gather_parameter_variable_states(const MultiFunction &fn, const MFCallInstruction &instruction, VariableStates &variable_states, MutableSpan< VariableState * > r_param_variable_states)
std::array< Vector< int64_t >, 2 > IndicesSplitVectors
static void fill_params__one(const MultiFunction &fn, const IndexMask mask, MFParamsBuilder &params, VariableStates &variable_states, const Span< VariableState * > param_variable_states)
static bool evaluate_as_one(const MultiFunction &fn, Span< VariableState * > param_variable_states, const IndexMask &mask, const IndexMask &full_mask)
__int64 int64_t
Definition: stdint.h:89
PointerRNA * ptr
Definition: wm_files.c:3480