Blender V4.5
FN_multi_function_builder.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
12
13#include "FN_multi_function.hh"
14
16
21namespace exec_presets {
22
30
36struct Simple {
37 static constexpr bool use_devirtualization = false;
39};
40
48 static constexpr bool use_devirtualization = false;
50};
51
59 static constexpr bool use_devirtualization = true;
61
62 template<typename... ParamTags, typename... LoadedParams, size_t... I>
64 std::index_sequence<I...> /*indices*/,
65 const std::tuple<LoadedParams...> &loaded_params) const
66 {
67 return std::make_tuple([&]() {
68 using ParamTag = ParamTags;
69 using T = typename ParamTag::base_type;
71 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
72 return GVArrayDevirtualizer<T, true, true>{varray_impl};
73 }
74 else if constexpr (ELEM(ParamTag::category,
77 {
78 T *ptr = std::get<I>(loaded_params);
80 }
81 }()...);
82 }
83};
84
90template<size_t... Indices> struct SomeSpanOrSingle {
91 static constexpr bool use_devirtualization = true;
93
94 template<typename... ParamTags, typename... LoadedParams, size_t... I>
96 std::index_sequence<I...> /*indices*/,
97 const std::tuple<LoadedParams...> &loaded_params) const
98 {
99 return std::make_tuple([&]() {
100 using ParamTag = ParamTags;
101 using T = typename ParamTag::base_type;
102
104 constexpr bool UseSpan = ValueSequence<size_t, Indices...>::template contains<I>();
105 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
106 return GVArrayDevirtualizer<T, true, UseSpan>{varray_impl};
107 }
108 else if constexpr (ELEM(ParamTag::category,
111 {
112 T *ptr = std::get<I>(loaded_params);
114 }
115 }()...);
116 }
117};
118
119} // namespace exec_presets
120
121namespace detail {
122
128template<typename MaskT, typename... Args, typename... ParamTags, size_t... I, typename ElementFn>
129/* Perform additional optimizations on this loop because it is a very hot loop. For example, the
130 * math node in geometry nodes is processed here. */
131#if (defined(__GNUC__) && !defined(__clang__))
132[[gnu::optimize("-funroll-loops")]] [[gnu::optimize("O3")]]
133#endif
134inline void
136 std::index_sequence<I...> /*indices*/,
137 ElementFn element_fn,
138 MaskT mask,
139 /* Use restrict to tell the compiler that pointer inputs do not alias
140 * each other. This is important for some compiler optimizations. */
141 Args &&__restrict... args)
142{
143 if constexpr (std::is_same_v<std::decay_t<MaskT>, IndexRange>) {
144 /* Having this explicit loop is necessary for MSVC to be able to vectorize this. */
145 const int64_t start = mask.start();
146 const int64_t end = mask.one_after_last();
147 for (int64_t i = start; i < end; i++) {
148 element_fn(args[i]...);
149 }
150 }
151 else {
152 for (const int64_t i : mask) {
153 element_fn(args[i]...);
154 }
155 }
156}
157
164
169
173template<typename... ParamTags, typename ElementFn, typename... Chunks>
174#if (defined(__GNUC__) && !defined(__clang__))
175[[gnu::optimize("-funroll-loops")]] [[gnu::optimize("O3")]]
176#endif
177inline void
179 const ElementFn element_fn,
180 const int64_t size,
181 Chunks &&__restrict... chunks)
182{
183 for (int64_t i = 0; i < size; i++) {
184 element_fn(chunks[i]...);
185 }
186}
187
193template<typename... ParamTags, size_t... I, typename ElementFn, typename... LoadedParams>
195 std::index_sequence<I...> /*indices*/,
196 const ElementFn element_fn,
198 const std::tuple<LoadedParams...> &loaded_params)
199{
200
201 /* In theory, all elements could be processed in one chunk. However, that has the disadvantage
202 * that large temporary arrays are needed. Using small chunks allows using small arrays, which
203 * are reused multiple times, which improves cache efficiency. The chunk size also shouldn't be
204 * too small, because then overhead of the outer loop over chunks becomes significant again. */
205 static constexpr int64_t MaxChunkSize = 64;
206 const int64_t mask_size = mask.size();
207 const int64_t tmp_buffer_size = std::min(mask_size, MaxChunkSize);
208
209 /* Local buffers that are used to temporarily store values for processing. */
210 std::tuple<TypedBuffer<typename ParamTags::base_type, MaxChunkSize>...> temporary_buffers;
211
212 /* Information about every parameter. */
213 std::tuple<MaterializeArgInfo<ParamTags>...> args_info;
214
215 (
216 /* Setup information for all parameters. */
217 [&] {
218 /* Use `typedef` instead of `using` to work around a compiler bug. */
219 using ParamTag = ParamTags;
220 using T = typename ParamTag::base_type;
221 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
223 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
224 const CommonVArrayInfo common_info = varray_impl.common_info();
225 if (common_info.type == CommonVArrayInfo::Type::Single) {
226 /* If an input #VArray is a single value, we have to fill the buffer with that value
227 * only once. The same unchanged buffer can then be reused in every chunk. */
228 const T &in_single = *static_cast<const T *>(common_info.data);
229 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
230 uninitialized_fill_n(tmp_buffer, tmp_buffer_size, in_single);
232 }
233 else if (common_info.type == CommonVArrayInfo::Type::Span) {
234 /* Remember the span so that it doesn't have to be retrieved in every iteration. */
235 arg_info.internal_span_data = static_cast<const T *>(common_info.data);
236 }
237 else {
238 arg_info.internal_span_data = nullptr;
239 }
240 }
241 }(),
242 ...);
243
244 IndexMaskFromSegment index_mask_from_segment;
245 const int64_t segment_offset = mask.offset();
246
247 /* Outer loop over all chunks. */
248 for (int64_t chunk_start = 0; chunk_start < mask_size; chunk_start += MaxChunkSize) {
249 const int64_t chunk_end = std::min<int64_t>(chunk_start + MaxChunkSize, mask_size);
250 const int64_t chunk_size = chunk_end - chunk_start;
251 const IndexMaskSegment sliced_mask = mask.slice(chunk_start, chunk_size);
252 const int64_t mask_start = sliced_mask[0];
253 const bool sliced_mask_is_range = unique_sorted_indices::non_empty_is_range(
254 sliced_mask.base_span());
255
256 /* Move mutable data into temporary array. */
257 if (!sliced_mask_is_range) {
258 (
259 [&] {
260 /* Use `typedef` instead of `using` to work around a compiler bug. */
261 using ParamTag = ParamTags;
262 using T = typename ParamTag::base_type;
264 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
265 T *param_buffer = std::get<I>(loaded_params);
266 for (int64_t i = 0; i < chunk_size; i++) {
267 new (tmp_buffer + i) T(std::move(param_buffer[sliced_mask[i]]));
268 }
269 }
270 }(),
271 ...);
272 }
273
274 const IndexMask *current_segment_mask = nullptr;
275
278 element_fn,
279 chunk_size,
280 /* Prepare every parameter for this chunk. */
281 [&] {
282 using ParamTag = ParamTags;
283 using T = typename ParamTag::base_type;
284 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
285 T *tmp_buffer = std::get<I>(temporary_buffers);
287 if (arg_info.mode == MaterializeArgMode::Single) {
288 /* The single value has been filled into a buffer already reused for every chunk. */
289 return const_cast<const T *>(tmp_buffer);
290 }
291 if (sliced_mask_is_range && arg_info.internal_span_data != nullptr) {
292 /* In this case we can just use an existing span instead of "compressing" it into
293 * a new temporary buffer. */
295 return arg_info.internal_span_data + mask_start;
296 }
297 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
298 if (current_segment_mask == nullptr) {
299 current_segment_mask = &index_mask_from_segment.update(
300 {segment_offset, sliced_mask.base_span()});
301 }
302 /* As a fallback, do a virtual function call to retrieve all elements in the current
303 * chunk. The elements are stored in a temporary buffer reused for every chunk. */
304 varray_impl.materialize_compressed_to_uninitialized(*current_segment_mask, tmp_buffer);
305 /* Remember that this parameter has been materialized, so that the values are
306 * destructed properly when the chunk is done. */
308 return const_cast<const T *>(tmp_buffer);
309 }
310 else if constexpr (ELEM(ParamTag::category,
313 {
314 /* For outputs, just pass a pointer. This is important so that `__restrict` works. */
315 if (sliced_mask_is_range) {
316 /* Can write into the caller-provided buffer directly. */
317 T *param_buffer = std::get<I>(loaded_params);
318 return param_buffer + mask_start;
319 }
320 /* Use the temporary buffer. The values will have to be copied out of that
321 * buffer into the caller-provided buffer afterwards. */
322 return tmp_buffer;
323 }
324 }()...);
325
326 /* Relocate outputs from temporary buffers to buffers provided by caller. */
327 if (!sliced_mask_is_range) {
328 (
329 [&] {
330 /* Use `typedef` instead of `using` to work around a compiler bug. */
331 using ParamTag = ParamTags;
332 using T = typename ParamTag::base_type;
333 if constexpr (ELEM(ParamTag::category,
336 {
337 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
338 T *param_buffer = std::get<I>(loaded_params);
339 for (int64_t i = 0; i < chunk_size; i++) {
340 new (param_buffer + sliced_mask[i]) T(std::move(tmp_buffer[i]));
341 std::destroy_at(tmp_buffer + i);
342 }
343 }
344 }(),
345 ...);
346 }
347
348 (
349 /* Destruct values that have been materialized before. */
350 [&] {
351 /* Use `typedef` instead of `using` to work around a compiler bug. */
352 using ParamTag = ParamTags;
353 using T = typename ParamTag::base_type;
354 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
356 if (arg_info.mode == MaterializeArgMode::Materialized) {
357 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
358 destruct_n(tmp_buffer, chunk_size);
359 }
360 }
361 }(),
362 ...);
363 }
364
365 (
366 /* Destruct buffers for single value inputs. */
367 [&] {
368 /* Use `typedef` instead of `using` to work around a compiler bug. */
369 using ParamTag = ParamTags;
370 using T = typename ParamTag::base_type;
371 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
373 if (arg_info.mode == MaterializeArgMode::Single) {
374 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
375 destruct_n(tmp_buffer, tmp_buffer_size);
376 }
377 }
378 }(),
379 ...);
380}
381
382template<typename ElementFn, typename ExecPreset, typename... ParamTags, size_t... I>
383inline void execute_element_fn_as_multi_function(const ElementFn element_fn,
384 const ExecPreset exec_preset,
385 const IndexMask &mask,
387 TypeSequence<ParamTags...> /*param_tags*/,
388 std::index_sequence<I...> /*indices*/)
389{
390
391 /* Load parameters from #Params. */
392 /* Contains `const GVArrayImpl *` for inputs and `T *` for outputs. */
393 const auto loaded_params = std::make_tuple([&]() {
394 /* Use `typedef` instead of `using` to work around a compiler bug. */
395 using ParamTag = ParamTags;
396 using T = typename ParamTag::base_type;
397
399 return params.readonly_single_input(I).get_implementation();
400 }
401 else if constexpr (ParamTag::category == ParamCategory::SingleOutput) {
402 return static_cast<T *>(params.uninitialized_single_output(I).data());
403 }
404 else if constexpr (ParamTag::category == ParamCategory::SingleMutable) {
405 return static_cast<T *>(params.single_mutable(I).data());
406 }
407 }()...);
408
409 /* Try execute devirtualized if enabled and the input types allow it. */
410 bool executed_devirtualized = false;
411 if constexpr (ExecPreset::use_devirtualization) {
412 /* Get segments before devirtualization to avoid generating this code multiple times. */
414 mask.to_spans_and_ranges<16>();
415
416 const auto devirtualizers = exec_preset.create_devirtualizers(
417 TypeSequence<ParamTags...>(), std::index_sequence<I...>(), loaded_params);
418 executed_devirtualized = call_with_devirtualized_parameters(
419 devirtualizers, [&](auto &&...args) {
420 for (const std::variant<IndexRange, IndexMaskSegment> &segment : mask_segments) {
421 if (std::holds_alternative<IndexRange>(segment)) {
422 const auto segment_range = std::get<IndexRange>(segment);
424 std::index_sequence<I...>(),
425 element_fn,
426 segment_range,
427 std::forward<decltype(args)>(args)...);
428 }
429 else {
430 const auto segment_indices = std::get<IndexMaskSegment>(segment);
432 std::index_sequence<I...>(),
433 element_fn,
434 segment_indices,
435 std::forward<decltype(args)>(args)...);
436 }
437 }
438 });
439 }
440 else {
441 UNUSED_VARS(exec_preset);
442 }
443
444 /* If devirtualized execution was disabled or not possible, use a fallback method which is
445 * slower but always works. */
446 if (!executed_devirtualized) {
447 /* The materialized method is most common because it avoids most virtual function overhead but
448 * still instantiates the function only once. */
449 if constexpr (ExecPreset::fallback_mode == exec_presets::FallbackMode::Materialized) {
450 mask.foreach_segment([&](const IndexMaskSegment segment) {
452 std::index_sequence<I...>(),
453 element_fn,
454 segment,
455 loaded_params);
456 });
457 }
458 else {
459 /* This fallback is slower because it uses virtual method calls for every element. */
460 mask.foreach_segment([&](const IndexMaskSegment segment) {
462 TypeSequence<ParamTags...>(), std::index_sequence<I...>(), element_fn, segment, [&]() {
463 /* Use `typedef` instead of `using` to work around a compiler bug. */
464 using ParamTag = ParamTags;
465 using T = typename ParamTag::base_type;
467 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
468 return GVArray(&varray_impl).typed<T>();
469 }
470 else if constexpr (ELEM(ParamTag::category,
473 {
474 T *ptr = std::get<I>(loaded_params);
475 return ptr;
476 }
477 }()...);
478 });
479 }
480 }
481}
482
489template<typename ElementFn, typename ExecPreset, typename... ParamTags>
490inline auto build_multi_function_call_from_element_fn(const ElementFn element_fn,
491 const ExecPreset exec_preset,
492 TypeSequence<ParamTags...> /*param_tags*/)
493{
494 return [element_fn, exec_preset](const IndexMask &mask, Params params) {
496 exec_preset,
497 mask,
498 params,
500 std::make_index_sequence<sizeof...(ParamTags)>());
501 };
502}
503
507template<typename CallFn, typename... ParamTags> class CustomMF : public MultiFunction {
508 private:
509 Signature signature_;
510 CallFn call_fn_;
511
512 public:
513 CustomMF(const char *name, CallFn call_fn, TypeSequence<ParamTags...> /*param_tags*/)
514 : call_fn_(std::move(call_fn))
515 {
516 SignatureBuilder builder{name, signature_};
517 /* Loop over all parameter types and add an entry for each in the signature. */
518 ([&] { builder.add(ParamTags(), ""); }(), ...);
519 this->set_signature(&signature_);
520 }
521
522 void call(const IndexMask &mask, Params params, Context /*context*/) const override
523 {
524 call_fn_(mask, params);
525 }
526};
527
528template<typename Out, typename... In, typename ElementFn, typename ExecPreset>
530 const ElementFn element_fn,
531 const ExecPreset exec_preset,
532 TypeSequence<In...> /*in_types*/)
533{
534 constexpr auto param_tags = TypeSequence<ParamTag<ParamCategory::SingleInput, In>...,
537 [element_fn](const In &...in, Out &out) { new (&out) Out(element_fn(in...)); },
538 exec_preset,
539 param_tags);
540 return CustomMF(name, call_fn, param_tags);
541}
542
543template<typename Out1, typename Out2, typename... In, typename ElementFn, typename ExecPreset>
545 const ElementFn element_fn,
546 const ExecPreset exec_preset,
547 TypeSequence<In...> /*in_types*/)
548{
549 constexpr auto param_tags = TypeSequence<ParamTag<ParamCategory::SingleInput, In>...,
552 auto call_fn = build_multi_function_call_from_element_fn(element_fn, exec_preset, param_tags);
553 return CustomMF(name, call_fn, param_tags);
554}
555
556} // namespace detail
557
559template<typename In1,
560 typename Out1,
561 typename ElementFn,
562 typename ExecPreset = exec_presets::Materialized>
563inline auto SI1_SO(const char *name,
564 const ElementFn element_fn,
565 const ExecPreset exec_preset = exec_presets::Materialized())
566{
568 name, element_fn, exec_preset, TypeSequence<In1>());
569}
570
572template<typename In1,
573 typename In2,
574 typename Out1,
575 typename ElementFn,
576 typename ExecPreset = exec_presets::Materialized>
577inline auto SI2_SO(const char *name,
578 const ElementFn element_fn,
579 const ExecPreset exec_preset = exec_presets::Materialized())
580{
582 name, element_fn, exec_preset, TypeSequence<In1, In2>());
583}
584
586template<typename In1,
587 typename In2,
588 typename In3,
589 typename Out1,
590 typename ElementFn,
591 typename ExecPreset = exec_presets::Materialized>
592inline auto SI3_SO(const char *name,
593 const ElementFn element_fn,
594 const ExecPreset exec_preset = exec_presets::Materialized())
595{
597 name, element_fn, exec_preset, TypeSequence<In1, In2, In3>());
598}
599
601template<typename In1,
602 typename In2,
603 typename In3,
604 typename In4,
605 typename Out1,
606 typename ElementFn,
607 typename ExecPreset = exec_presets::Materialized>
608inline auto SI4_SO(const char *name,
609 const ElementFn element_fn,
610 const ExecPreset exec_preset = exec_presets::Materialized())
611{
613 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4>());
614}
615
617template<typename In1,
618 typename In2,
619 typename In3,
620 typename In4,
621 typename In5,
622 typename Out1,
623 typename ElementFn,
624 typename ExecPreset = exec_presets::Materialized>
625inline auto SI5_SO(const char *name,
626 const ElementFn element_fn,
627 const ExecPreset exec_preset = exec_presets::Materialized())
628{
630 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5>());
631}
632
634template<typename In1,
635 typename In2,
636 typename In3,
637 typename In4,
638 typename In5,
639 typename In6,
640 typename Out1,
641 typename ElementFn,
642 typename ExecPreset = exec_presets::Materialized>
643inline auto SI6_SO(const char *name,
644 const ElementFn element_fn,
645 const ExecPreset exec_preset = exec_presets::Materialized())
646{
648 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5, In6>());
649}
650
652template<typename In1,
653 typename In2,
654 typename In3,
655 typename In4,
656 typename In5,
657 typename In6,
658 typename In7,
659 typename In8,
660 typename Out1,
661 typename ElementFn,
662 typename ExecPreset = exec_presets::Materialized>
663inline auto SI8_SO(const char *name,
664 const ElementFn element_fn,
665 const ExecPreset exec_preset = exec_presets::Materialized())
666{
668 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5, In6, In7, In8>());
669}
670
672template<typename Mut1, typename ElementFn, typename ExecPreset = exec_presets::AllSpanOrSingle>
673inline auto SM(const char *name,
674 const ElementFn element_fn,
675 const ExecPreset exec_preset = exec_presets::AllSpanOrSingle())
676{
679 element_fn, exec_preset, param_tags);
680 return detail::CustomMF(name, call_fn, param_tags);
681}
682
684template<typename In1,
685 typename Out1,
686 typename Out2,
687 typename ElementFn,
688 typename ExecPreset = exec_presets::Materialized>
689inline auto SI1_SO2(const char *name,
690 const ElementFn element_fn,
691 const ExecPreset exec_preset = exec_presets::Materialized())
692{
694 name, element_fn, exec_preset, TypeSequence<In1>());
695}
696
698template<typename In1,
699 typename In2,
700 typename Out1,
701 typename Out2,
702 typename ElementFn,
703 typename ExecPreset = exec_presets::Materialized>
704inline auto SI2_SO2(const char *name,
705 const ElementFn element_fn,
706 const ExecPreset exec_preset = exec_presets::Materialized())
707{
709 name, element_fn, exec_preset, TypeSequence<In1, In2>());
710}
711
713template<typename In1,
714 typename In2,
715 typename In3,
716 typename Out1,
717 typename Out2,
718 typename ElementFn,
719 typename ExecPreset = exec_presets::Materialized>
720inline auto SI3_SO2(const char *name,
721 const ElementFn element_fn,
722 const ExecPreset exec_preset = exec_presets::Materialized())
723{
725 name, element_fn, exec_preset, TypeSequence<In1, In2, In3>());
726}
727
729template<typename In1,
730 typename In2,
731 typename In3,
732 typename In4,
733 typename Out1,
734 typename Out2,
735 typename ElementFn,
736 typename ExecPreset = exec_presets::Materialized>
737inline auto SI4_SO2(const char *name,
738 const ElementFn element_fn,
739 const ExecPreset exec_preset = exec_presets::Materialized())
740{
742 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4>());
743}
744
746template<typename In1,
747 typename In2,
748 typename In3,
749 typename In4,
750 typename In5,
751 typename Out1,
752 typename Out2,
753 typename ElementFn,
754 typename ExecPreset = exec_presets::Materialized>
755inline auto SI5_SO2(const char *name,
756 const ElementFn element_fn,
757 const ExecPreset exec_preset = exec_presets::Materialized())
758{
760 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5>());
761}
762
764template<typename In1,
765 typename Out1,
766 typename Out2,
767 typename Out3,
768 typename ElementFn,
769 typename ExecPreset = exec_presets::Materialized>
770inline auto SI1_SO3(const char *name,
771 const ElementFn element_fn,
772 const ExecPreset exec_preset = exec_presets::Materialized())
773{
779 element_fn, exec_preset, param_tags);
780 return detail::CustomMF(name, call_fn, param_tags);
781}
782
784template<typename In1,
785 typename Out1,
786 typename Out2,
787 typename Out3,
788 typename Out4,
789 typename ElementFn,
790 typename ExecPreset = exec_presets::Materialized>
791inline auto SI1_SO4(const char *name,
792 const ElementFn element_fn,
793 const ExecPreset exec_preset = exec_presets::Materialized())
794{
801 element_fn, exec_preset, param_tags);
802 return detail::CustomMF(name, call_fn, param_tags);
803}
804
805} // namespace blender::fn::multi_function::build
806
808
815 private:
816 const CPPType &type_;
817 const void *value_;
818 Signature signature_;
819 bool owns_value_;
820
821 template<typename T> friend class CustomMF_Constant;
822
823 public:
824 CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy);
825 ~CustomMF_GenericConstant() override;
826 void call(const IndexMask &mask, Params params, Context context) const override;
827 uint64_t hash() const override;
828 bool equals(const MultiFunction &other) const override;
829};
830
836 private:
837 GSpan array_;
838 Signature signature_;
839
840 public:
842 void call(const IndexMask &mask, Params params, Context context) const override;
843};
844
848template<typename T> class CustomMF_Constant : public MultiFunction {
849 private:
850 T value_;
851 Signature signature_;
852
853 public:
854 template<typename U> CustomMF_Constant(U &&value) : value_(std::forward<U>(value))
855 {
856 SignatureBuilder builder{"Constant", signature_};
857 builder.single_output<T>("Value");
858 this->set_signature(&signature_);
859 }
860
861 void call(const IndexMask &mask, Params params, Context /*context*/) const override
862 {
863 MutableSpan<T> output = params.uninitialized_single_output<T>(0);
864 mask.foreach_index_optimized<int64_t>([&](const int64_t i) { new (&output[i]) T(value_); });
865 }
866
867 uint64_t hash() const override
868 {
869 return get_default_hash(value_);
870 }
871
872 bool equals(const MultiFunction &other) const override
873 {
874 const CustomMF_Constant *other1 = dynamic_cast<const CustomMF_Constant *>(&other);
875 if (other1 != nullptr) {
876 return value_ == other1->value_;
877 }
878 const CustomMF_GenericConstant *other2 = dynamic_cast<const CustomMF_GenericConstant *>(
879 &other);
880 if (other2 != nullptr) {
881 const CPPType &type = CPPType::get<T>();
882 if (type == other2->type_) {
883 return type.is_equal_or_false(static_cast<const void *>(&value_), other2->value_);
884 }
885 }
886 return false;
887 }
888};
889
891 private:
892 int output_amount_;
893 Signature signature_;
894
895 public:
896 CustomMF_DefaultOutput(Span<DataType> input_types, Span<DataType> output_types);
897 void call(const IndexMask &mask, Params params, Context context) const override;
898};
899
901 private:
902 Signature signature_;
903
904 public:
906 void call(const IndexMask &mask, Params params, Context context) const override;
907};
908
909} // namespace blender::fn::multi_function
#define UNUSED_VARS(...)
#define ELEM(...)
#define U
BMesh const char void * data
long long int int64_t
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static const CPPType & get()
bool is_equal_or_false(const void *a, const void *b) const
virtual void materialize_compressed_to_uninitialized(const IndexMask &mask, void *dst) const
virtual CommonVArrayInfo common_info() const
Span< BaseT > base_span() const
bool equals(const MultiFunction &other) const override
void call(const IndexMask &mask, Params params, Context) const override
CustomMF_DefaultOutput(Span< DataType > input_types, Span< DataType > output_types)
void call(const IndexMask &mask, Params params, Context context) const override
void call(const IndexMask &mask, Params params, Context context) const override
CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy)
bool equals(const MultiFunction &other) const override
void call(const IndexMask &mask, Params params, Context context) const override
void call(const IndexMask &mask, Params params, Context context) const override
void set_signature(const Signature *signature)
void add(ParamTag< Category, T >, const char *name)
void single_output(const char *name, const ParamFlag flag=ParamFlag::None)
CustomMF(const char *name, CallFn call_fn, TypeSequence< ParamTags... >)
void call(const IndexMask &mask, Params params, Context) const override
const IndexMask & update(IndexMaskSegment segment)
#define in
#define out
#define output
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define T
auto build_multi_function_with_n_inputs_two_outputs(const char *name, const ElementFn element_fn, const ExecPreset exec_preset, TypeSequence< In... >)
auto build_multi_function_with_n_inputs_one_output(const char *name, const ElementFn element_fn, const ExecPreset exec_preset, TypeSequence< In... >)
void execute_element_fn_as_multi_function(const ElementFn element_fn, const ExecPreset exec_preset, const IndexMask &mask, Params params, TypeSequence< ParamTags... >, std::index_sequence< I... >)
auto build_multi_function_call_from_element_fn(const ElementFn element_fn, const ExecPreset exec_preset, TypeSequence< ParamTags... >)
void execute_array(TypeSequence< ParamTags... >, std::index_sequence< I... >, ElementFn element_fn, MaskT mask, Args &&__restrict... args)
void execute_materialized_impl(TypeSequence< ParamTags... >, const ElementFn element_fn, const int64_t size, Chunks &&__restrict... chunks)
void execute_materialized(TypeSequence< ParamTags... >, std::index_sequence< I... >, const ElementFn element_fn, const IndexMaskSegment mask, const std::tuple< LoadedParams... > &loaded_params)
auto SI1_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI3_SO2(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI5_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI4_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI1_SO2(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SM(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::AllSpanOrSingle())
auto SI3_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI4_SO2(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI2_SO2(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI1_SO3(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI1_SO4(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI6_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI2_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI5_SO2(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
auto SI8_SO(const char *name, const ElementFn element_fn, const ExecPreset exec_preset=exec_presets::Materialized())
bool non_empty_is_range(const Span< T > indices)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
bool call_with_devirtualized_parameters(const std::tuple< Devirtualizers... > &devis, const Fn &fn)
void uninitialized_fill_n(T *dst, int64_t n, const T &value)
void destruct_n(T *ptr, int64_t n)
#define I
auto create_devirtualizers(TypeSequence< ParamTags... >, std::index_sequence< I... >, const std::tuple< LoadedParams... > &loaded_params) const
auto create_devirtualizers(TypeSequence< ParamTags... >, std::index_sequence< I... >, const std::tuple< LoadedParams... > &loaded_params) const
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4227