Blender V4.3
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 inline constexpr ValueType static_type = ValueType::GVArray;
49 const GVArray &data;
50
52 {
53 BLI_assert(data);
54 }
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 inline constexpr ValueType static_type = ValueType::Span;
61 void *data;
62 bool owned;
63
64 VariableValue_Span(void *data, bool owned) : VariableValue(static_type), data(data), owned(owned)
65 {
66 }
67};
68
69/* This variable is the unmodified virtual vector array from the caller. */
71 static inline constexpr ValueType static_type = ValueType::GVVectorArray;
73
77};
78
79/* This variable has a different vector for every index. */
81 static inline constexpr ValueType static_type = ValueType::GVectorArray;
83 bool owned;
84
89};
90
91/* This variable has the same value for every index. */
93 static inline 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 inline 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 inline 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 inline int small_value_max_size = 16;
147 static constexpr inline 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:
152 ValueAllocator(LinearAllocator<> &linear_allocator) : linear_allocator_(linear_allocator) {}
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
169 VariableValue_Span *obtain_Span(const CPPType &type, int size)
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 fallback 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. */
359 BLI_assert(mask.size() <= tot_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. */
475 BLI_assert(mask.size() <= tot_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
692 bool destruct(const IndexMask &mask,
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();
711 type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask);
712 }
713 break;
714 }
715 case ValueType::Span: {
716 const CPPType &type = data_type.single_type();
717 type.destruct_indices(this->value_as<VariableValue_Span>()->data, mask);
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
762 void indices_split(const IndexMask &mask, IndicesSplitVectors &r_indices)
763 {
764 BLI_assert(mask.size() <= tot_initialized_);
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
1001static void fill_params__one(const MultiFunction &fn,
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 {
1064 ParamsBuilder params(fn, &mask);
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
1125 void add_owned_indices(const Instruction &instruction, Vector<int64_t> indices)
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;
1165 LinearAllocator<> linear_allocator;
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
1244MultiFunction::ExecutionHints ProcedureExecutor::get_execution_hints() const
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:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define UNUSED_VARS(...)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
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 Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture 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
void destruct_indices(void *ptr, const IndexMask &mask) const
void destruct(void *ptr) const
void extend(int64_t index, const GVArray &values)
void provide_buffer(void *buffer, const int64_t size)
void * allocate(const int64_t size, const int64_t alignment)
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:484
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:601
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 reserve(const int64_t min_capacity)
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 void call(const IndexMask &mask, Params params, Context context) const =0
void call_auto(const IndexMask &mask, Params params, Context context) 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)
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
void foreach_index(Fn &&fn) const
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float4 mask(const int4 mask, const float4 a)
static ulong state[N]
#define T
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
__int64 int64_t
Definition stdint.h:89
PointerRNA * ptr
Definition wm_files.cc:4126
char * buffers[2]