Blender V5.0
multi_function_procedure_executor.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BLI_stack.hh"
8
10
11ProcedureExecutor::ProcedureExecutor(const Procedure &procedure) : procedure_(procedure)
12{
13 SignatureBuilder builder("Procedure Executor", signature_);
14
15 for (const ConstParameter &param : procedure.params()) {
16 builder.add("Parameter", ParamType(param.type, param.variable->data_type()));
17 }
18
19 this->set_signature(&signature_);
20}
21
22using IndicesSplitVectors = std::array<Vector<int64_t>, 2>;
23
24namespace {
25enum class ValueType {
26 GVArray = 0,
27 Span = 1,
28 GVVectorArray = 2,
29 GVectorArray = 3,
30 OneSingle = 4,
31 OneVector = 5,
32};
33constexpr int tot_variable_value_types = 6;
34} // namespace
35
41 ValueType type;
42
43 VariableValue(ValueType type) : type(type) {}
44};
45
46/* This variable is the unmodified virtual array from the caller. */
48 static constexpr ValueType static_type = ValueType::GVArray;
49 const GVArray &data;
50
55};
56
57/* This variable has a different value for every index. Some values may be uninitialized. The span
58 * may be owned by the caller. */
60 static constexpr ValueType static_type = ValueType::Span;
61 void *data;
62 bool owned;
63
67};
68
69/* This variable is the unmodified virtual vector array from the caller. */
71 static constexpr ValueType static_type = ValueType::GVVectorArray;
73
77};
78
79/* This variable has a different vector for every index. */
81 static constexpr ValueType static_type = ValueType::GVectorArray;
83 bool owned;
84
89};
90
91/* This variable has the same value for every index. */
93 static constexpr ValueType static_type = ValueType::OneSingle;
94 void *data;
95 bool is_initialized = false;
96
98};
99
100/* This variable has the same vector for every index. */
102 static constexpr ValueType static_type = ValueType::OneVector;
104
106};
107
108static_assert(std::is_trivially_destructible_v<VariableValue_GVArray>);
109static_assert(std::is_trivially_destructible_v<VariableValue_Span>);
110static_assert(std::is_trivially_destructible_v<VariableValue_GVVectorArray>);
111static_assert(std::is_trivially_destructible_v<VariableValue_GVectorArray>);
112static_assert(std::is_trivially_destructible_v<VariableValue_OneSingle>);
113static_assert(std::is_trivially_destructible_v<VariableValue_OneVector>);
114
115class VariableState;
116
122 private:
127 static constexpr int min_alignment = 64;
128
130 LinearAllocator<> &linear_allocator_;
131
136 std::array<Stack<VariableValue *>, tot_variable_value_types> variable_value_free_lists_;
137
142 Stack<void *> small_span_buffers_free_list_;
143 Map<int, Stack<void *>> span_buffers_free_lists_;
144
146 static constexpr int small_value_max_size = 16;
147 static constexpr int small_value_max_alignment = 8;
148 Stack<void *> small_single_value_free_list_;
149 Map<const CPPType *, Stack<void *>> single_value_free_lists_;
150
151 public:
153
155 {
156 return this->obtain<VariableValue_GVArray>(varray);
157 }
158
160 {
161 return this->obtain<VariableValue_GVVectorArray>(varray);
162 }
163
165 {
166 return this->obtain<VariableValue_Span>(buffer, false);
167 }
168
170 {
171 void *buffer = nullptr;
172
173 const int64_t element_size = type.size;
174 const int64_t alignment = type.alignment;
175
176 if (alignment > min_alignment) {
177 /* In this rare case we fall back to not reusing existing buffers. */
178 buffer = linear_allocator_.allocate(element_size * size, alignment);
179 }
180 else {
181 Stack<void *> *stack = type.can_exist_in_buffer(small_value_max_size,
182 small_value_max_alignment) ?
183 &small_span_buffers_free_list_ :
184 span_buffers_free_lists_.lookup_ptr(element_size);
185 if (stack == nullptr || stack->is_empty()) {
186 buffer = linear_allocator_.allocate(
187 std::max<int64_t>(element_size, small_value_max_size) * size, min_alignment);
188 }
189 else {
190 /* Reuse existing buffer. */
191 buffer = stack->pop();
192 }
193 }
194
195 return this->obtain<VariableValue_Span>(buffer, true);
196 }
197
199 {
200 return this->obtain<VariableValue_GVectorArray>(data, false);
201 }
202
204 {
205 GVectorArray *vector_array = new GVectorArray(type, size);
206 return this->obtain<VariableValue_GVectorArray>(*vector_array, true);
207 }
208
210 {
211 const bool is_small = type.can_exist_in_buffer(small_value_max_size,
212 small_value_max_alignment);
213 Stack<void *> &stack = is_small ? small_single_value_free_list_ :
214 single_value_free_lists_.lookup_or_add_default(&type);
215 void *buffer;
216 if (stack.is_empty()) {
217 buffer = linear_allocator_.allocate(
218 std::max<int>(small_value_max_size, type.size),
219 std::max<int>(small_value_max_alignment, type.alignment));
220 }
221 else {
222 buffer = stack.pop();
223 }
224 return this->obtain<VariableValue_OneSingle>(buffer);
225 }
226
228 {
229 GVectorArray *vector_array = new GVectorArray(type, 1);
230 return this->obtain<VariableValue_OneVector>(*vector_array);
231 }
232
233 void release_value(VariableValue *value, const DataType &data_type)
234 {
235 switch (value->type) {
236 case ValueType::GVArray: {
237 break;
238 }
239 case ValueType::Span: {
240 auto *value_typed = static_cast<VariableValue_Span *>(value);
241 if (value_typed->owned) {
242 const CPPType &type = data_type.single_type();
243 /* Assumes all values in the buffer are uninitialized already. */
244 Stack<void *> &buffers = type.can_exist_in_buffer(small_value_max_size,
245 small_value_max_alignment) ?
246 small_span_buffers_free_list_ :
247 span_buffers_free_lists_.lookup_or_add_default(type.size);
248 buffers.push(value_typed->data);
249 }
250 break;
251 }
252 case ValueType::GVVectorArray: {
253 break;
254 }
255 case ValueType::GVectorArray: {
256 auto *value_typed = static_cast<VariableValue_GVectorArray *>(value);
257 if (value_typed->owned) {
258 delete &value_typed->data;
259 }
260 break;
261 }
262 case ValueType::OneSingle: {
263 auto *value_typed = static_cast<VariableValue_OneSingle *>(value);
264 const CPPType &type = data_type.single_type();
265 if (value_typed->is_initialized) {
266 type.destruct(value_typed->data);
267 }
268 const bool is_small = type.can_exist_in_buffer(small_value_max_size,
269 small_value_max_alignment);
270 if (is_small) {
271 small_single_value_free_list_.push(value_typed->data);
272 }
273 else {
274 single_value_free_lists_.lookup_or_add_default(&type).push(value_typed->data);
275 }
276 break;
277 }
278 case ValueType::OneVector: {
279 auto *value_typed = static_cast<VariableValue_OneVector *>(value);
280 delete &value_typed->data;
281 break;
282 }
283 }
284
285 Stack<VariableValue *> &stack = variable_value_free_lists_[int(value->type)];
286 stack.push(value);
287 }
288
289 private:
290 template<typename T, typename... Args> T *obtain(Args &&...args)
291 {
292 static_assert(std::is_base_of_v<VariableValue, T>);
293 Stack<VariableValue *> &stack = variable_value_free_lists_[int(T::static_type)];
294 if (stack.is_empty()) {
295 void *buffer = linear_allocator_.allocate(sizeof(T), alignof(T));
296 return new (buffer) T(std::forward<Args>(args)...);
297 }
298 return new (stack.pop()) T(std::forward<Args>(args)...);
299 }
300};
301
306 public:
311 /* This a non-owning pointer to either span buffer or #GVectorArray or null. */
313
314 void destruct_value(ValueAllocator &value_allocator, const DataType &data_type)
315 {
316 value_allocator.release_value(value_, data_type);
317 value_ = nullptr;
318 }
319
320 /* True if this contains only one value for all indices, i.e. the value for all indices is
321 * the same. */
322 bool is_one() const
323 {
324 if (value_ == nullptr) {
325 return true;
326 }
327 switch (value_->type) {
328 case ValueType::GVArray:
329 return this->value_as<VariableValue_GVArray>()->data.is_single();
330 case ValueType::Span:
331 return tot_initialized_ == 0;
332 case ValueType::GVVectorArray:
333 return this->value_as<VariableValue_GVVectorArray>()->data.is_single_vector();
334 case ValueType::GVectorArray:
335 return tot_initialized_ == 0;
336 case ValueType::OneSingle:
337 return true;
338 case ValueType::OneVector:
339 return true;
340 }
342 return false;
343 }
344
345 bool is_fully_initialized(const IndexMask &full_mask)
346 {
347 return tot_initialized_ == full_mask.size();
348 }
349
350 bool is_fully_uninitialized(const IndexMask &full_mask)
351 {
352 UNUSED_VARS(full_mask);
353 return tot_initialized_ == 0;
354 }
355
356 void add_as_input(ParamsBuilder &params, const IndexMask &mask, const DataType &data_type) const
357 {
358 /* Sanity check to make sure that enough values are initialized. */
360 BLI_assert(value_ != nullptr);
361
362 switch (value_->type) {
363 case ValueType::GVArray: {
364 params.add_readonly_single_input(this->value_as<VariableValue_GVArray>()->data);
365 break;
366 }
367 case ValueType::Span: {
368 const void *data = this->value_as<VariableValue_Span>()->data;
369 const GSpan span{data_type.single_type(), data, mask.min_array_size()};
370 params.add_readonly_single_input(span);
371 break;
372 }
373 case ValueType::GVVectorArray: {
374 params.add_readonly_vector_input(this->value_as<VariableValue_GVVectorArray>()->data);
375 break;
376 }
377 case ValueType::GVectorArray: {
378 params.add_readonly_vector_input(this->value_as<VariableValue_GVectorArray>()->data);
379 break;
380 }
381 case ValueType::OneSingle: {
382 const auto *value_typed = this->value_as<VariableValue_OneSingle>();
383 BLI_assert(value_typed->is_initialized);
384 const GPointer gpointer{data_type.single_type(), value_typed->data};
385 params.add_readonly_single_input(gpointer);
386 break;
387 }
388 case ValueType::OneVector: {
389 params.add_readonly_vector_input(this->value_as<VariableValue_OneVector>()->data[0]);
390 break;
391 }
392 }
393 }
394
395 void ensure_is_mutable(const IndexMask &full_mask,
396 const DataType &data_type,
397 ValueAllocator &value_allocator)
398 {
399 if (value_ != nullptr && ELEM(value_->type, ValueType::Span, ValueType::GVectorArray)) {
400 return;
401 }
402
403 const int array_size = full_mask.min_array_size();
404
405 switch (data_type.category()) {
406 case DataType::Single: {
407 const CPPType &type = data_type.single_type();
408 VariableValue_Span *new_value = nullptr;
409 if (caller_provided_storage_ == nullptr) {
410 new_value = value_allocator.obtain_Span(type, array_size);
411 }
412 else {
413 /* Reuse the storage provided caller when possible. */
414 new_value = value_allocator.obtain_Span_not_owned(caller_provided_storage_);
415 }
416 if (value_ != nullptr) {
417 if (value_->type == ValueType::GVArray) {
418 /* Fill new buffer with data from virtual array. */
419 this->value_as<VariableValue_GVArray>()->data.materialize_to_uninitialized(
420 full_mask, new_value->data);
421 }
422 else if (value_->type == ValueType::OneSingle) {
423 auto *old_value_typed_ = this->value_as<VariableValue_OneSingle>();
424 if (old_value_typed_->is_initialized) {
425 /* Fill the buffer with a single value. */
426 type.fill_construct_indices(old_value_typed_->data, new_value->data, full_mask);
427 }
428 }
429 else {
431 }
432 value_allocator.release_value(value_, data_type);
433 }
434 value_ = new_value;
435 break;
436 }
437 case DataType::Vector: {
438 const CPPType &type = data_type.vector_base_type();
439 VariableValue_GVectorArray *new_value = nullptr;
440 if (caller_provided_storage_ == nullptr) {
441 new_value = value_allocator.obtain_GVectorArray(type, array_size);
442 }
443 else {
444 new_value = value_allocator.obtain_GVectorArray_not_owned(
445 *static_cast<GVectorArray *>(caller_provided_storage_));
446 }
447 if (value_ != nullptr) {
448 if (value_->type == ValueType::GVVectorArray) {
449 /* Fill new vector array with data from virtual vector array. */
450 new_value->data.extend(full_mask, this->value_as<VariableValue_GVVectorArray>()->data);
451 }
452 else if (value_->type == ValueType::OneVector) {
453 /* Fill all indices with the same value. */
454 const GSpan vector = this->value_as<VariableValue_OneVector>()->data[0];
455 new_value->data.extend(full_mask, GVVectorArray_For_SingleGSpan{vector, array_size});
456 }
457 else {
459 }
460 value_allocator.release_value(value_, data_type);
461 }
462 value_ = new_value;
463 break;
464 }
465 }
466 }
467
469 const IndexMask &mask,
470 const IndexMask &full_mask,
471 const DataType &data_type,
472 ValueAllocator &value_allocator)
473 {
474 /* Sanity check to make sure that enough values are initialized. */
476
477 this->ensure_is_mutable(full_mask, data_type, value_allocator);
478 BLI_assert(value_ != nullptr);
479
480 switch (value_->type) {
481 case ValueType::Span: {
482 void *data = this->value_as<VariableValue_Span>()->data;
483 const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()};
484 params.add_single_mutable(span);
485 break;
486 }
487 case ValueType::GVectorArray: {
488 params.add_vector_mutable(this->value_as<VariableValue_GVectorArray>()->data);
489 break;
490 }
491 case ValueType::GVArray:
492 case ValueType::GVVectorArray:
493 case ValueType::OneSingle:
494 case ValueType::OneVector: {
496 break;
497 }
498 }
499 }
500
502 const IndexMask &mask,
503 const IndexMask &full_mask,
504 const DataType &data_type,
505 ValueAllocator &value_allocator)
506 {
507 /* Sanity check to make sure that enough values are not initialized. */
508 BLI_assert(mask.size() <= full_mask.size() - tot_initialized_);
509 this->ensure_is_mutable(full_mask, data_type, value_allocator);
510 BLI_assert(value_ != nullptr);
511
512 switch (value_->type) {
513 case ValueType::Span: {
514 void *data = this->value_as<VariableValue_Span>()->data;
515 const GMutableSpan span{data_type.single_type(), data, mask.min_array_size()};
516 params.add_uninitialized_single_output(span);
517 break;
518 }
519 case ValueType::GVectorArray: {
520 params.add_vector_output(this->value_as<VariableValue_GVectorArray>()->data);
521 break;
522 }
523 case ValueType::GVArray:
524 case ValueType::GVVectorArray:
525 case ValueType::OneSingle:
526 case ValueType::OneVector: {
528 break;
529 }
530 }
531
532 tot_initialized_ += mask.size();
533 }
534
535 void add_as_input__one(ParamsBuilder &params, const DataType &data_type) const
536 {
537 BLI_assert(this->is_one());
538 BLI_assert(value_ != nullptr);
539
540 switch (value_->type) {
541 case ValueType::GVArray: {
542 params.add_readonly_single_input(this->value_as<VariableValue_GVArray>()->data);
543 break;
544 }
545 case ValueType::GVVectorArray: {
546 params.add_readonly_vector_input(this->value_as<VariableValue_GVVectorArray>()->data);
547 break;
548 }
549 case ValueType::OneSingle: {
550 const auto *value_typed = this->value_as<VariableValue_OneSingle>();
551 BLI_assert(value_typed->is_initialized);
552 GPointer ptr{data_type.single_type(), value_typed->data};
553 params.add_readonly_single_input(ptr);
554 break;
555 }
556 case ValueType::OneVector: {
557 params.add_readonly_vector_input(this->value_as<VariableValue_OneVector>()->data);
558 break;
559 }
560 case ValueType::Span:
561 case ValueType::GVectorArray: {
563 break;
564 }
565 }
566 }
567
568 void ensure_is_mutable__one(const DataType &data_type, ValueAllocator &value_allocator)
569 {
570 BLI_assert(this->is_one());
571 if (value_ != nullptr && ELEM(value_->type, ValueType::OneSingle, ValueType::OneVector)) {
572 return;
573 }
574
575 switch (data_type.category()) {
576 case DataType::Single: {
577 const CPPType &type = data_type.single_type();
578 VariableValue_OneSingle *new_value = value_allocator.obtain_OneSingle(type);
579 if (value_ != nullptr) {
580 if (value_->type == ValueType::GVArray) {
581 this->value_as<VariableValue_GVArray>()->data.get_internal_single_to_uninitialized(
582 new_value->data);
583 new_value->is_initialized = true;
584 }
585 else if (value_->type == ValueType::Span) {
587 /* Nothing to do, the single value is uninitialized already. */
588 }
589 else {
591 }
592 value_allocator.release_value(value_, data_type);
593 }
594 value_ = new_value;
595 break;
596 }
597 case DataType::Vector: {
598 const CPPType &type = data_type.vector_base_type();
599 VariableValue_OneVector *new_value = value_allocator.obtain_OneVector(type);
600 if (value_ != nullptr) {
601 if (value_->type == ValueType::GVVectorArray) {
602 const GVVectorArray &old_vector_array =
604 new_value->data.extend(IndexRange(1), old_vector_array);
605 }
606 else if (value_->type == ValueType::GVectorArray) {
608 /* Nothing to do. */
609 }
610 else {
612 }
613 value_allocator.release_value(value_, data_type);
614 }
615 value_ = new_value;
616 break;
617 }
618 }
619 }
620
622 const DataType &data_type,
623 ValueAllocator &value_allocator)
624 {
625 BLI_assert(this->is_one());
626 this->ensure_is_mutable__one(data_type, value_allocator);
627 BLI_assert(value_ != nullptr);
628
629 switch (value_->type) {
630 case ValueType::OneSingle: {
631 auto *value_typed = this->value_as<VariableValue_OneSingle>();
632 BLI_assert(value_typed->is_initialized);
633 params.add_single_mutable(GMutableSpan{data_type.single_type(), value_typed->data, 1});
634 break;
635 }
636 case ValueType::OneVector: {
637 params.add_vector_mutable(this->value_as<VariableValue_OneVector>()->data);
638 break;
639 }
640 case ValueType::GVArray:
641 case ValueType::Span:
642 case ValueType::GVVectorArray:
643 case ValueType::GVectorArray: {
645 break;
646 }
647 }
648 }
649
651 const IndexMask &mask,
652 const DataType &data_type,
653 ValueAllocator &value_allocator)
654 {
655 BLI_assert(this->is_one());
656 this->ensure_is_mutable__one(data_type, value_allocator);
657 BLI_assert(value_ != nullptr);
658
659 switch (value_->type) {
660 case ValueType::OneSingle: {
661 auto *value_typed = this->value_as<VariableValue_OneSingle>();
662 BLI_assert(!value_typed->is_initialized);
663 params.add_uninitialized_single_output(
664 GMutableSpan{data_type.single_type(), value_typed->data, 1});
665 /* It becomes initialized when the multi-function is called. */
666 value_typed->is_initialized = true;
667 break;
668 }
669 case ValueType::OneVector: {
670 auto *value_typed = this->value_as<VariableValue_OneVector>();
671 BLI_assert(value_typed->data[0].is_empty());
672 params.add_vector_output(value_typed->data);
673 break;
674 }
675 case ValueType::GVArray:
676 case ValueType::Span:
677 case ValueType::GVVectorArray:
678 case ValueType::GVectorArray: {
680 break;
681 }
682 }
683
684 tot_initialized_ += mask.size();
685 }
686
693 const IndexMask &full_mask,
694 const DataType &data_type,
695 ValueAllocator &value_allocator)
696 {
697 BLI_assert(value_ != nullptr);
698 int new_tot_initialized = tot_initialized_ - mask.size();
699
700 /* Sanity check to make sure that enough indices can be destructed. */
701 BLI_assert(new_tot_initialized >= 0);
702
703 switch (value_->type) {
704 case ValueType::GVArray: {
705 if (mask.size() < full_mask.size()) {
706 /* Not all elements are destructed. Since we can't work on the original array, we have to
707 * create a copy first. */
708 this->ensure_is_mutable(full_mask, data_type, value_allocator);
709 BLI_assert(value_->type == ValueType::Span);
710 const CPPType &type = data_type.single_type();
712 }
713 break;
714 }
715 case ValueType::Span: {
716 const CPPType &type = data_type.single_type();
718 break;
719 }
720 case ValueType::GVVectorArray: {
721 if (mask.size() < full_mask.size()) {
722 /* Not all elements are cleared. Since we can't work on the original vector array, we
723 * have to create a copy first. A possible future optimization is to create the partial
724 * copy directly. */
725 this->ensure_is_mutable(full_mask, data_type, value_allocator);
726 BLI_assert(value_->type == ValueType::GVectorArray);
727 this->value_as<VariableValue_GVectorArray>()->data.clear(mask);
728 }
729 break;
730 }
731 case ValueType::GVectorArray: {
732 this->value_as<VariableValue_GVectorArray>()->data.clear(mask);
733 break;
734 }
735 case ValueType::OneSingle: {
736 auto *value_typed = this->value_as<VariableValue_OneSingle>();
737 BLI_assert(value_typed->is_initialized);
738 UNUSED_VARS_NDEBUG(value_typed);
739 if (mask.size() == tot_initialized_) {
740 const CPPType &type = data_type.single_type();
741 type.destruct(value_typed->data);
742 value_typed->is_initialized = false;
743 }
744 break;
745 }
746 case ValueType::OneVector: {
747 auto *value_typed = this->value_as<VariableValue_OneVector>();
748 if (mask.size() == tot_initialized_) {
749 value_typed->data.clear(IndexRange(1));
750 }
751 break;
752 }
753 }
754
755 tot_initialized_ = new_tot_initialized;
756
757 const bool should_self_destruct = new_tot_initialized == 0 &&
758 caller_provided_storage_ == nullptr;
759 return should_self_destruct;
760 }
761
763 {
765 BLI_assert(value_ != nullptr);
766
767 switch (value_->type) {
768 case ValueType::GVArray: {
769 const VArray<bool> varray = this->value_as<VariableValue_GVArray>()->data.typed<bool>();
770 mask.foreach_index([&](const int64_t i) { r_indices[varray[i]].append(i); });
771 break;
772 }
773 case ValueType::Span: {
774 const Span<bool> span(
775 static_cast<const bool *>(this->value_as<VariableValue_Span>()->data),
776 mask.min_array_size());
777 mask.foreach_index([&](const int64_t i) { r_indices[span[i]].append(i); });
778 break;
779 }
780 case ValueType::OneSingle: {
781 auto *value_typed = this->value_as<VariableValue_OneSingle>();
782 BLI_assert(value_typed->is_initialized);
783 const bool condition = *static_cast<const bool *>(value_typed->data);
784 Vector<int64_t> &indices = r_indices[condition];
785 indices.reserve(indices.size() + mask.size());
786 mask.foreach_index_optimized<int64_t>([&](const int64_t i) { indices.append(i); });
787 break;
788 }
789 case ValueType::GVVectorArray:
790 case ValueType::GVectorArray:
791 case ValueType::OneVector: {
793 break;
794 }
795 }
796 }
797
798 template<typename T> T *value_as()
799 {
800 BLI_assert(value_ != nullptr);
801 BLI_assert(value_->type == T::static_type);
802 return static_cast<T *>(value_);
803 }
804
805 template<typename T> const T *value_as() const
806 {
807 BLI_assert(value_ != nullptr);
808 BLI_assert(value_->type == T::static_type);
809 return static_cast<T *>(value_);
810 }
811};
812
815 private:
816 ValueAllocator value_allocator_;
817 const Procedure &procedure_;
819 Array<VariableState> variable_states_;
820 const IndexMask &full_mask_;
821
822 public:
824 const Procedure &procedure,
825 const IndexMask &full_mask)
826 : value_allocator_(linear_allocator),
827 procedure_(procedure),
828 variable_states_(procedure.variables().size()),
829 full_mask_(full_mask)
830 {
831 }
832
834 {
835 for (const int variable_i : procedure_.variables().index_range()) {
836 VariableState &state = variable_states_[variable_i];
837 if (state.value_ != nullptr) {
838 const Variable *variable = procedure_.variables()[variable_i];
839 state.destruct_value(value_allocator_, variable->data_type());
840 }
841 }
842 }
843
845 {
846 return value_allocator_;
847 }
848
849 const IndexMask &full_mask() const
850 {
851 return full_mask_;
852 }
853
855 const Procedure &procedure,
856 Params &params)
857 {
858 for (const int param_index : fn.param_indices()) {
859 ParamType param_type = fn.param_type(param_index);
860 const Variable *variable = procedure.params()[param_index].variable;
861
862 auto add_state = [&](VariableValue *value,
863 bool input_is_initialized,
864 void *caller_provided_storage = nullptr) {
865 const int tot_initialized = input_is_initialized ? full_mask_.size() : 0;
866 const int variable_i = variable->index_in_procedure();
867 VariableState &variable_state = variable_states_[variable_i];
868 BLI_assert(variable_state.value_ == nullptr);
869 variable_state.value_ = value;
870 variable_state.tot_initialized_ = tot_initialized;
871 variable_state.caller_provided_storage_ = caller_provided_storage;
872 };
873
874 switch (param_type.category()) {
876 const GVArray &data = params.readonly_single_input(param_index);
877 add_state(value_allocator_.obtain_GVArray(data), true);
878 break;
879 }
881 const GVVectorArray &data = params.readonly_vector_input(param_index);
882 add_state(value_allocator_.obtain_GVVectorArray(data), true);
883 break;
884 }
886 GMutableSpan data = params.uninitialized_single_output(param_index);
887 add_state(value_allocator_.obtain_Span_not_owned(data.data()), false, data.data());
888 break;
889 }
891 GVectorArray &data = params.vector_output(param_index);
892 add_state(value_allocator_.obtain_GVectorArray_not_owned(data), false, &data);
893 break;
894 }
896 GMutableSpan data = params.single_mutable(param_index);
897 add_state(value_allocator_.obtain_Span_not_owned(data.data()), true, data.data());
898 break;
899 }
901 GVectorArray &data = params.vector_mutable(param_index);
902 add_state(value_allocator_.obtain_GVectorArray_not_owned(data), true, &data);
903 break;
904 }
905 }
906 }
907 }
908
909 void add_as_param(VariableState &variable_state,
911 const ParamType &param_type,
912 const IndexMask &mask)
913 {
914 const DataType data_type = param_type.data_type();
915 switch (param_type.interface_type()) {
916 case ParamType::Input: {
917 variable_state.add_as_input(params, mask, data_type);
918 break;
919 }
920 case ParamType::Mutable: {
921 variable_state.add_as_mutable(params, mask, full_mask_, data_type, value_allocator_);
922 break;
923 }
924 case ParamType::Output: {
925 variable_state.add_as_output(params, mask, full_mask_, data_type, value_allocator_);
926 break;
927 }
928 }
929 }
930
931 void add_as_param__one(VariableState &variable_state,
933 const ParamType &param_type,
934 const IndexMask &mask)
935 {
936 const DataType data_type = param_type.data_type();
937 switch (param_type.interface_type()) {
938 case ParamType::Input: {
939 variable_state.add_as_input__one(params, data_type);
940 break;
941 }
942 case ParamType::Mutable: {
943 variable_state.add_as_mutable__one(params, data_type, value_allocator_);
944 break;
945 }
946 case ParamType::Output: {
947 variable_state.add_as_output__one(params, mask, data_type, value_allocator_);
948 break;
949 }
950 }
951 }
952
953 void destruct(const Variable &variable, const IndexMask &mask)
954 {
955 VariableState &variable_state = this->get_variable_state(variable);
956 if (variable_state.destruct(mask, full_mask_, variable.data_type(), value_allocator_)) {
957 variable_state.destruct_value(value_allocator_, variable.data_type());
958 }
959 }
960
962 {
963 const int variable_i = variable.index_in_procedure();
964 VariableState &variable_state = variable_states_[variable_i];
965 return variable_state;
966 }
967};
968
969static bool evaluate_as_one(Span<VariableState *> param_variable_states,
970 const IndexMask &mask,
971 const IndexMask &full_mask)
972{
973 if (mask.size() < full_mask.size()) {
974 return false;
975 }
976 for (VariableState *state : param_variable_states) {
977 if (state != nullptr && state->value_ != nullptr && !state->is_one()) {
978 return false;
979 }
980 }
981 return true;
982}
983
985 const CallInstruction &instruction,
986 VariableStates &variable_states,
987 MutableSpan<VariableState *> r_param_variable_states)
988{
989 for (const int param_index : fn.param_indices()) {
990 const Variable *variable = instruction.params()[param_index];
991 if (variable == nullptr) {
992 r_param_variable_states[param_index] = nullptr;
993 }
994 else {
995 VariableState &variable_state = variable_states.get_variable_state(*variable);
996 r_param_variable_states[param_index] = &variable_state;
997 }
998 }
999}
1000
1002 const IndexMask &mask,
1004 VariableStates &variable_states,
1005 const Span<VariableState *> param_variable_states)
1006{
1007 for (const int param_index : fn.param_indices()) {
1008 const ParamType param_type = fn.param_type(param_index);
1009 VariableState *variable_state = param_variable_states[param_index];
1010 if (variable_state == nullptr) {
1011 params.add_ignored_single_output();
1012 }
1013 else {
1014 variable_states.add_as_param__one(*variable_state, params, param_type, mask);
1015 }
1016 }
1017}
1018
1019static void fill_params(const MultiFunction &fn,
1020 const IndexMask &mask,
1022 VariableStates &variable_states,
1023 const Span<VariableState *> param_variable_states)
1024{
1025 for (const int param_index : fn.param_indices()) {
1026 const ParamType param_type = fn.param_type(param_index);
1027 VariableState *variable_state = param_variable_states[param_index];
1028 if (variable_state == nullptr) {
1029 params.add_ignored_single_output();
1030 }
1031 else {
1032 variable_states.add_as_param(*variable_state, params, param_type, mask);
1033 }
1034 }
1035}
1036
1037static void execute_call_instruction(const CallInstruction &instruction,
1038 const IndexMask &mask,
1039 VariableStates &variable_states,
1040 const Context &context)
1041{
1042 const MultiFunction &fn = instruction.fn();
1043
1044 Vector<VariableState *> param_variable_states;
1045 param_variable_states.resize(fn.param_amount());
1046 gather_parameter_variable_states(fn, instruction, variable_states, param_variable_states);
1047
1048 /* If all inputs to the function are constant, it's enough to call the function only once instead
1049 * of for every index. */
1050 if (evaluate_as_one(param_variable_states, mask, variable_states.full_mask())) {
1051 static const IndexMask one_mask(1);
1052 ParamsBuilder params(fn, &one_mask);
1053 fill_params__one(fn, mask, params, variable_states, param_variable_states);
1054
1055 try {
1056 fn.call(one_mask, params, context);
1057 }
1058 catch (...) {
1059 /* Multi-functions must not throw exceptions. */
1061 }
1062 }
1063 else {
1065 fill_params(fn, mask, params, variable_states, param_variable_states);
1066
1067 try {
1068 fn.call_auto(mask, params, context);
1069 }
1070 catch (...) {
1071 /* Multi-functions must not throw exceptions. */
1073 }
1074 }
1075}
1076
1079 std::unique_ptr<IndexMaskMemory> memory;
1081
1082 const IndexMask &mask() const
1083 {
1084 return this->referenced_indices;
1085 }
1086};
1087
1090 const Instruction *instruction = nullptr;
1092
1093 const IndexMask &mask() const
1094 {
1095 return this->indices.mask();
1096 }
1097
1098 operator bool() const
1099 {
1100 return this->instruction != nullptr;
1101 }
1102};
1103
1109 private:
1110 Stack<NextInstructionInfo> next_instructions_;
1111
1112 public:
1114
1115 void add_referenced_indices(const Instruction &instruction, const IndexMask &mask)
1116 {
1117 if (mask.is_empty()) {
1118 return;
1119 }
1120 InstructionIndices new_indices;
1121 new_indices.referenced_indices = mask;
1122 next_instructions_.push({&instruction, std::move(new_indices)});
1123 }
1124
1126 {
1127 if (indices.is_empty()) {
1128 return;
1129 }
1130
1131 InstructionIndices new_indices;
1132 new_indices.memory = std::make_unique<IndexMaskMemory>();
1134 *new_indices.memory);
1135 next_instructions_.push({&instruction, std::move(new_indices)});
1136 }
1137
1138 bool is_done() const
1139 {
1140 return next_instructions_.is_empty();
1141 }
1142
1144 {
1145 BLI_assert(!this->is_done());
1146 return next_instructions_.peek();
1147 }
1148
1150 {
1151 next_instructions_.peek().instruction = &instruction;
1152 }
1153
1155 {
1156 return next_instructions_.pop();
1157 }
1158};
1159
1160void ProcedureExecutor::call(const IndexMask &full_mask, Params params, Context context) const
1161{
1162 BLI_assert(procedure_.validate());
1163
1164 AlignedBuffer<512, 64> local_buffer;
1166 linear_allocator.provide_buffer(local_buffer);
1167
1168 VariableStates variable_states{linear_allocator, procedure_, full_mask};
1169 variable_states.add_initial_variable_states(*this, procedure_, params);
1170
1171 InstructionScheduler scheduler;
1172 scheduler.add_referenced_indices(*procedure_.entry(), full_mask);
1173
1174 /* Loop until all indices got to a return instruction. */
1175 while (!scheduler.is_done()) {
1176 const NextInstructionInfo &instr_info = scheduler.peek();
1177 const Instruction &instruction = *instr_info.instruction;
1178 switch (instruction.type()) {
1179 case InstructionType::Call: {
1180 const CallInstruction &call_instruction = static_cast<const CallInstruction &>(
1181 instruction);
1182 execute_call_instruction(call_instruction, instr_info.mask(), variable_states, context);
1183 scheduler.update_instruction_pointer(*call_instruction.next());
1184 break;
1185 }
1187 const BranchInstruction &branch_instruction = static_cast<const BranchInstruction &>(
1188 instruction);
1189 const Variable *condition_var = branch_instruction.condition();
1190 VariableState &variable_state = variable_states.get_variable_state(*condition_var);
1191
1192 IndicesSplitVectors new_indices;
1193 variable_state.indices_split(instr_info.mask(), new_indices);
1194 scheduler.pop();
1195 scheduler.add_owned_indices(*branch_instruction.branch_false(), new_indices[false]);
1196 scheduler.add_owned_indices(*branch_instruction.branch_true(), new_indices[true]);
1197 break;
1198 }
1200 const DestructInstruction &destruct_instruction = static_cast<const DestructInstruction &>(
1201 instruction);
1202 const Variable *variable = destruct_instruction.variable();
1203 variable_states.destruct(*variable, instr_info.mask());
1204 scheduler.update_instruction_pointer(*destruct_instruction.next());
1205 break;
1206 }
1208 const DummyInstruction &dummy_instruction = static_cast<const DummyInstruction &>(
1209 instruction);
1210 scheduler.update_instruction_pointer(*dummy_instruction.next());
1211 break;
1212 }
1214 /* Don't insert the indices back into the scheduler. */
1215 scheduler.pop();
1216 break;
1217 }
1218 }
1219 }
1220
1221 for (const int param_index : this->param_indices()) {
1222 const ParamType param_type = this->param_type(param_index);
1223 const Variable *variable = procedure_.params()[param_index].variable;
1224 VariableState &variable_state = variable_states.get_variable_state(*variable);
1225 switch (param_type.interface_type()) {
1226 case ParamType::Input: {
1227 /* Input variables must be destructed in the end. */
1228 BLI_assert(variable_state.is_fully_uninitialized(full_mask));
1229 break;
1230 }
1231 case ParamType::Mutable:
1232 case ParamType::Output: {
1233 /* Mutable and output variables must be initialized in the end. */
1234 BLI_assert(variable_state.is_fully_initialized(full_mask));
1235 /* Make sure that the data is in the memory provided by the caller. */
1236 variable_state.ensure_is_mutable(
1237 full_mask, param_type.data_type(), variable_states.value_allocator());
1238 break;
1239 }
1240 }
1241 }
1242}
1243
1245{
1246 ExecutionHints hints;
1247 hints.allocates_array = true;
1248 hints.min_grain_size = 10000;
1249 return hints;
1250}
1251
1252} // namespace blender::fn::multi_function
#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(...)
BMesh const char void * data
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void fill_construct_indices(const void *value, void *dst, const IndexMask &mask) const
void destruct_indices(void *ptr, const IndexMask &mask) const
void destruct(void *ptr) const
bool can_exist_in_buffer(const int64_t buffer_size, const int64_t buffer_alignment) const
void extend(int64_t index, const GVArray &values)
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
void * allocate(const int64_t size, const int64_t alignment)
NonCopyable(const NonCopyable &other)=delete
NonMovable(NonMovable &&other)=delete
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
void resize(const int64_t new_size)
void add_referenced_indices(const Instruction &instruction, const IndexMask &mask)
void add_owned_indices(const Instruction &instruction, Vector< int64_t > indices)
void set_signature(const Signature *signature)
ParamType param_type(int param_index) const
virtual ExecutionHints get_execution_hints() const
void call(const IndexMask &mask, Params params, Context context) const override
void add(ParamTag< Category, T >, const char *name)
VariableValue_GVectorArray * obtain_GVectorArray(const CPPType &type, int size)
VariableValue_GVectorArray * obtain_GVectorArray_not_owned(GVectorArray &data)
void release_value(VariableValue *value, const DataType &data_type)
VariableValue_OneSingle * obtain_OneSingle(const CPPType &type)
VariableValue_GVArray * obtain_GVArray(const GVArray &varray)
VariableValue_Span * obtain_Span(const CPPType &type, int size)
VariableValue_GVVectorArray * obtain_GVVectorArray(const GVVectorArray &varray)
VariableValue_OneVector * obtain_OneVector(const CPPType &type)
void indices_split(const IndexMask &mask, IndicesSplitVectors &r_indices)
void destruct_value(ValueAllocator &value_allocator, const DataType &data_type)
void ensure_is_mutable(const IndexMask &full_mask, const DataType &data_type, ValueAllocator &value_allocator)
void add_as_mutable(ParamsBuilder &params, const IndexMask &mask, const IndexMask &full_mask, const DataType &data_type, ValueAllocator &value_allocator)
void add_as_output(ParamsBuilder &params, const IndexMask &mask, const IndexMask &full_mask, const DataType &data_type, ValueAllocator &value_allocator)
void add_as_input__one(ParamsBuilder &params, const DataType &data_type) const
void ensure_is_mutable__one(const DataType &data_type, ValueAllocator &value_allocator)
void add_as_input(ParamsBuilder &params, const IndexMask &mask, const DataType &data_type) const
void add_as_output__one(ParamsBuilder &params, const IndexMask &mask, const DataType &data_type, ValueAllocator &value_allocator)
bool destruct(const IndexMask &mask, const IndexMask &full_mask, const DataType &data_type, ValueAllocator &value_allocator)
void add_as_mutable__one(ParamsBuilder &params, const DataType &data_type, ValueAllocator &value_allocator)
VariableState & get_variable_state(const Variable &variable)
void destruct(const Variable &variable, const IndexMask &mask)
void add_initial_variable_states(const ProcedureExecutor &fn, const Procedure &procedure, Params &params)
void add_as_param__one(VariableState &variable_state, ParamsBuilder &params, const ParamType &param_type, const IndexMask &mask)
void add_as_param(VariableState &variable_state, ParamsBuilder &params, const ParamType &param_type, const IndexMask &mask)
VariableStates(LinearAllocator<> &linear_allocator, const Procedure &procedure, const IndexMask &full_mask)
void foreach_index(Fn &&fn) const
static ushort indices[]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static ulong state[N]
#define T
MultiFunction::ExecutionHints ExecutionHints
static bool evaluate_as_one(Span< VariableState * > param_variable_states, const IndexMask &mask, const IndexMask &full_mask)
static void gather_parameter_variable_states(const MultiFunction &fn, const CallInstruction &instruction, VariableStates &variable_states, MutableSpan< VariableState * > r_param_variable_states)
static void fill_params(const MultiFunction &fn, const IndexMask &mask, ParamsBuilder &params, VariableStates &variable_states, const Span< VariableState * > param_variable_states)
static void execute_call_instruction(const CallInstruction &instruction, const IndexMask &mask, VariableStates &variable_states, const Context &context)
static void fill_params__one(const MultiFunction &fn, const IndexMask &mask, ParamsBuilder &params, VariableStates &variable_states, const Span< VariableState * > param_variable_states)
std::array< Vector< int64_t >, 2 > IndicesSplitVectors
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238
char * buffers[2]