Blender V5.0
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 execute_array(TypeSequence<ParamTags...> /*param_tags*/,
135 std::index_sequence<I...> /*indices*/,
136 ElementFn element_fn,
137 MaskT mask,
138 /* Use restrict to tell the compiler that pointer inputs do not alias
139 * each other. This is important for some compiler optimizations. */
140 Args &&__restrict... args)
141{
142 if constexpr (std::is_same_v<std::decay_t<MaskT>, IndexRange>) {
143 /* Having this explicit loop is necessary for MSVC to be able to vectorize this. */
144 const int64_t start = mask.start();
145 const int64_t end = mask.one_after_last();
146 for (int64_t i = start; i < end; i++) {
147 element_fn(args[i]...);
148 }
149 }
150 else {
151 for (const int64_t i : mask) {
152 element_fn(args[i]...);
153 }
154 }
155}
156
163
168
172template<typename... ParamTags, typename ElementFn, typename... Chunks>
173#if (defined(__GNUC__) && !defined(__clang__))
174[[gnu::optimize("-funroll-loops")]] [[gnu::optimize("O3")]]
175#endif
177 const ElementFn element_fn,
178 const int64_t size,
179 Chunks &&__restrict... chunks)
180{
181 for (int64_t i = 0; i < size; i++) {
182 element_fn(chunks[i]...);
183 }
184}
185
191template<typename... ParamTags, size_t... I, typename ElementFn, typename... LoadedParams>
193 std::index_sequence<I...> /*indices*/,
194 const ElementFn element_fn,
196 const std::tuple<LoadedParams...> &loaded_params)
197{
198
199 /* In theory, all elements could be processed in one chunk. However, that has the disadvantage
200 * that large temporary arrays are needed. Using small chunks allows using small arrays, which
201 * are reused multiple times, which improves cache efficiency. The chunk size also shouldn't be
202 * too small, because then overhead of the outer loop over chunks becomes significant again. */
203 static constexpr int64_t MaxChunkSize = 64;
204 const int64_t mask_size = mask.size();
205 const int64_t tmp_buffer_size = std::min(mask_size, MaxChunkSize);
206
207 /* Local buffers that are used to temporarily store values for processing. */
208 std::tuple<TypedBuffer<typename ParamTags::base_type, MaxChunkSize>...> temporary_buffers;
209
210 /* Information about every parameter. */
211 std::tuple<MaterializeArgInfo<ParamTags>...> args_info;
212
213 (
214 /* Setup information for all parameters. */
215 [&] {
216 /* Use `typedef` instead of `using` to work around a compiler bug. */
217 using ParamTag = ParamTags;
218 using T = typename ParamTag::base_type;
219 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
221 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
222 const CommonVArrayInfo common_info = varray_impl.common_info();
223 if (common_info.type == CommonVArrayInfo::Type::Single) {
224 /* If an input #VArray is a single value, we have to fill the buffer with that value
225 * only once. The same unchanged buffer can then be reused in every chunk. */
226 const T &in_single = *static_cast<const T *>(common_info.data);
227 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
228 uninitialized_fill_n(tmp_buffer, tmp_buffer_size, in_single);
230 }
231 else if (common_info.type == CommonVArrayInfo::Type::Span) {
232 /* Remember the span so that it doesn't have to be retrieved in every iteration. */
233 arg_info.internal_span_data = static_cast<const T *>(common_info.data);
234 }
235 else {
236 arg_info.internal_span_data = nullptr;
237 }
238 }
239 }(),
240 ...);
241
242 IndexMaskFromSegment index_mask_from_segment;
243 const int64_t segment_offset = mask.offset();
244
245 /* Outer loop over all chunks. */
246 for (int64_t chunk_start = 0; chunk_start < mask_size; chunk_start += MaxChunkSize) {
247 const int64_t chunk_end = std::min<int64_t>(chunk_start + MaxChunkSize, mask_size);
248 const int64_t chunk_size = chunk_end - chunk_start;
249 const IndexMaskSegment sliced_mask = mask.slice(chunk_start, chunk_size);
250 const int64_t mask_start = sliced_mask[0];
251 const bool sliced_mask_is_range = unique_sorted_indices::non_empty_is_range(
252 sliced_mask.base_span());
253
254 /* Move mutable data into temporary array. */
255 if (!sliced_mask_is_range) {
256 (
257 [&] {
258 /* Use `typedef` instead of `using` to work around a compiler bug. */
259 using ParamTag = ParamTags;
260 using T = typename ParamTag::base_type;
262 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
263 T *param_buffer = std::get<I>(loaded_params);
264 for (int64_t i = 0; i < chunk_size; i++) {
265 new (tmp_buffer + i) T(std::move(param_buffer[sliced_mask[i]]));
266 }
267 }
268 }(),
269 ...);
270 }
271
272 const IndexMask *current_segment_mask = nullptr;
273
276 element_fn,
277 chunk_size,
278 /* Prepare every parameter for this chunk. */
279 [&] {
280 using ParamTag = ParamTags;
281 using T = typename ParamTag::base_type;
282 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
283 T *tmp_buffer = std::get<I>(temporary_buffers);
285 if (arg_info.mode == MaterializeArgMode::Single) {
286 /* The single value has been filled into a buffer already reused for every chunk. */
287 return const_cast<const T *>(tmp_buffer);
288 }
289 if (sliced_mask_is_range && arg_info.internal_span_data != nullptr) {
290 /* In this case we can just use an existing span instead of "compressing" it into
291 * a new temporary buffer. */
293 return arg_info.internal_span_data + mask_start;
294 }
295 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
296 if (current_segment_mask == nullptr) {
297 current_segment_mask = &index_mask_from_segment.update(
298 {segment_offset, sliced_mask.base_span()});
299 }
300 /* As a fallback, do a virtual function call to retrieve all elements in the current
301 * chunk. The elements are stored in a temporary buffer reused for every chunk. */
302 varray_impl.materialize_compressed(*current_segment_mask, tmp_buffer, true);
303 /* Remember that this parameter has been materialized, so that the values are
304 * destructed properly when the chunk is done. */
306 return const_cast<const T *>(tmp_buffer);
307 }
308 else if constexpr (ELEM(ParamTag::category,
311 {
312 /* For outputs, just pass a pointer. This is important so that `__restrict` works. */
313 if (sliced_mask_is_range) {
314 /* Can write into the caller-provided buffer directly. */
315 T *param_buffer = std::get<I>(loaded_params);
316 return param_buffer + mask_start;
317 }
318 /* Use the temporary buffer. The values will have to be copied out of that
319 * buffer into the caller-provided buffer afterwards. */
320 return tmp_buffer;
321 }
322 }()...);
323
324 /* Relocate outputs from temporary buffers to buffers provided by caller. */
325 if (!sliced_mask_is_range) {
326 (
327 [&] {
328 /* Use `typedef` instead of `using` to work around a compiler bug. */
329 using ParamTag = ParamTags;
330 using T = typename ParamTag::base_type;
331 if constexpr (ELEM(ParamTag::category,
334 {
335 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
336 T *param_buffer = std::get<I>(loaded_params);
337 for (int64_t i = 0; i < chunk_size; i++) {
338 new (param_buffer + sliced_mask[i]) T(std::move(tmp_buffer[i]));
339 std::destroy_at(tmp_buffer + i);
340 }
341 }
342 }(),
343 ...);
344 }
345
346 (
347 /* Destruct values that have been materialized before. */
348 [&] {
349 /* Use `typedef` instead of `using` to work around a compiler bug. */
350 using ParamTag = ParamTags;
351 using T = typename ParamTag::base_type;
352 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
354 if (arg_info.mode == MaterializeArgMode::Materialized) {
355 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
356 destruct_n(tmp_buffer, chunk_size);
357 }
358 }
359 }(),
360 ...);
361 }
362
363 (
364 /* Destruct buffers for single value inputs. */
365 [&] {
366 /* Use `typedef` instead of `using` to work around a compiler bug. */
367 using ParamTag = ParamTags;
368 using T = typename ParamTag::base_type;
369 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
371 if (arg_info.mode == MaterializeArgMode::Single) {
372 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
373 destruct_n(tmp_buffer, tmp_buffer_size);
374 }
375 }
376 }(),
377 ...);
378}
379
380template<typename ElementFn, typename ExecPreset, typename... ParamTags, size_t... I>
381inline void execute_element_fn_as_multi_function(const ElementFn element_fn,
382 const ExecPreset exec_preset,
383 const IndexMask &mask,
385 TypeSequence<ParamTags...> /*param_tags*/,
386 std::index_sequence<I...> /*indices*/)
387{
388
389 /* Load parameters from #Params. */
390 /* Contains `const GVArrayImpl *` for inputs and `T *` for outputs. */
391 const auto loaded_params = std::make_tuple([&]() {
392 /* Use `typedef` instead of `using` to work around a compiler bug. */
393 using ParamTag = ParamTags;
394 using T = typename ParamTag::base_type;
395
397 return params.readonly_single_input(I).get_implementation();
398 }
399 else if constexpr (ParamTag::category == ParamCategory::SingleOutput) {
400 return static_cast<T *>(params.uninitialized_single_output(I).data());
401 }
402 else if constexpr (ParamTag::category == ParamCategory::SingleMutable) {
403 return static_cast<T *>(params.single_mutable(I).data());
404 }
405 }()...);
406
407 /* Try execute devirtualized if enabled and the input types allow it. */
408 bool executed_devirtualized = false;
409 if constexpr (ExecPreset::use_devirtualization) {
410 /* Get segments before devirtualization to avoid generating this code multiple times. */
412 mask.to_spans_and_ranges<16>();
413
414 const auto devirtualizers = exec_preset.create_devirtualizers(
415 TypeSequence<ParamTags...>(), std::index_sequence<I...>(), loaded_params);
416 executed_devirtualized = call_with_devirtualized_parameters(
417 devirtualizers, [&](auto &&...args) {
418 for (const std::variant<IndexRange, IndexMaskSegment> &segment : mask_segments) {
419 if (std::holds_alternative<IndexRange>(segment)) {
420 const auto segment_range = std::get<IndexRange>(segment);
422 std::index_sequence<I...>(),
423 element_fn,
424 segment_range,
425 std::forward<decltype(args)>(args)...);
426 }
427 else {
428 const auto segment_indices = std::get<IndexMaskSegment>(segment);
430 std::index_sequence<I...>(),
431 element_fn,
432 segment_indices,
433 std::forward<decltype(args)>(args)...);
434 }
435 }
436 });
437 }
438 else {
439 UNUSED_VARS(exec_preset);
440 }
441
442 /* If devirtualized execution was disabled or not possible, use a fallback method which is
443 * slower but always works. */
444 if (!executed_devirtualized) {
445 /* The materialized method is most common because it avoids most virtual function overhead but
446 * still instantiates the function only once. */
447 if constexpr (ExecPreset::fallback_mode == exec_presets::FallbackMode::Materialized) {
448 mask.foreach_segment([&](const IndexMaskSegment segment) {
450 std::index_sequence<I...>(),
451 element_fn,
452 segment,
453 loaded_params);
454 });
455 }
456 else {
457 /* This fallback is slower because it uses virtual method calls for every element. */
458 mask.foreach_segment([&](const IndexMaskSegment segment) {
460 TypeSequence<ParamTags...>(), std::index_sequence<I...>(), element_fn, segment, [&]() {
461 /* Use `typedef` instead of `using` to work around a compiler bug. */
462 using ParamTag = ParamTags;
463 using T = typename ParamTag::base_type;
465 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
466 return GVArray(&varray_impl).typed<T>();
467 }
468 else if constexpr (ELEM(ParamTag::category,
471 {
472 T *ptr = std::get<I>(loaded_params);
473 return ptr;
474 }
475 }()...);
476 });
477 }
478 }
479}
480
487template<typename ElementFn, typename ExecPreset, typename... ParamTags>
488inline auto build_multi_function_call_from_element_fn(const ElementFn element_fn,
489 const ExecPreset exec_preset,
490 TypeSequence<ParamTags...> /*param_tags*/)
491{
492 return [element_fn, exec_preset](const IndexMask &mask, Params params) {
494 exec_preset,
495 mask,
496 params,
498 std::make_index_sequence<sizeof...(ParamTags)>());
499 };
500}
501
505template<typename CallFn, typename... ParamTags> class CustomMF : public MultiFunction {
506 private:
507 Signature signature_;
508 CallFn call_fn_;
509
510 public:
511 CustomMF(const char *name, CallFn call_fn, TypeSequence<ParamTags...> /*param_tags*/)
512 : call_fn_(std::move(call_fn))
513 {
514 SignatureBuilder builder{name, signature_};
515 /* Loop over all parameter types and add an entry for each in the signature. */
516 ([&] { builder.add(ParamTags(), ""); }(), ...);
517 this->set_signature(&signature_);
518 }
519
520 void call(const IndexMask &mask, Params params, Context /*context*/) const override
521 {
522 call_fn_(mask, params);
523 }
524};
525
526template<typename Out, typename... In, typename ElementFn, typename ExecPreset>
528 const ElementFn element_fn,
529 const ExecPreset exec_preset,
530 TypeSequence<In...> /*in_types*/)
531{
532 constexpr auto param_tags = TypeSequence<ParamTag<ParamCategory::SingleInput, In>...,
535 [element_fn](const In &...in, Out &out) { new (&out) Out(element_fn(in...)); },
536 exec_preset,
537 param_tags);
538 return CustomMF(name, call_fn, param_tags);
539}
540
541template<typename Out1, typename Out2, typename... In, typename ElementFn, typename ExecPreset>
543 const ElementFn element_fn,
544 const ExecPreset exec_preset,
545 TypeSequence<In...> /*in_types*/)
546{
547 constexpr auto param_tags = TypeSequence<ParamTag<ParamCategory::SingleInput, In>...,
550 auto call_fn = build_multi_function_call_from_element_fn(element_fn, exec_preset, param_tags);
551 return CustomMF(name, call_fn, param_tags);
552}
553
554} // namespace detail
555
557template<typename In1,
558 typename Out1,
559 typename ElementFn,
560 typename ExecPreset = exec_presets::Materialized>
561inline auto SI1_SO(const char *name,
562 const ElementFn element_fn,
563 const ExecPreset exec_preset = exec_presets::Materialized())
564{
566 name, element_fn, exec_preset, TypeSequence<In1>());
567}
568
570template<typename In1,
571 typename In2,
572 typename Out1,
573 typename ElementFn,
574 typename ExecPreset = exec_presets::Materialized>
575inline auto SI2_SO(const char *name,
576 const ElementFn element_fn,
577 const ExecPreset exec_preset = exec_presets::Materialized())
578{
580 name, element_fn, exec_preset, TypeSequence<In1, In2>());
581}
582
584template<typename In1,
585 typename In2,
586 typename In3,
587 typename Out1,
588 typename ElementFn,
589 typename ExecPreset = exec_presets::Materialized>
590inline auto SI3_SO(const char *name,
591 const ElementFn element_fn,
592 const ExecPreset exec_preset = exec_presets::Materialized())
593{
595 name, element_fn, exec_preset, TypeSequence<In1, In2, In3>());
596}
597
599template<typename In1,
600 typename In2,
601 typename In3,
602 typename In4,
603 typename Out1,
604 typename ElementFn,
605 typename ExecPreset = exec_presets::Materialized>
606inline auto SI4_SO(const char *name,
607 const ElementFn element_fn,
608 const ExecPreset exec_preset = exec_presets::Materialized())
609{
611 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4>());
612}
613
615template<typename In1,
616 typename In2,
617 typename In3,
618 typename In4,
619 typename In5,
620 typename Out1,
621 typename ElementFn,
622 typename ExecPreset = exec_presets::Materialized>
623inline auto SI5_SO(const char *name,
624 const ElementFn element_fn,
625 const ExecPreset exec_preset = exec_presets::Materialized())
626{
628 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5>());
629}
630
632template<typename In1,
633 typename In2,
634 typename In3,
635 typename In4,
636 typename In5,
637 typename In6,
638 typename Out1,
639 typename ElementFn,
640 typename ExecPreset = exec_presets::Materialized>
641inline auto SI6_SO(const char *name,
642 const ElementFn element_fn,
643 const ExecPreset exec_preset = exec_presets::Materialized())
644{
646 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5, In6>());
647}
648
650template<typename In1,
651 typename In2,
652 typename In3,
653 typename In4,
654 typename In5,
655 typename In6,
656 typename In7,
657 typename In8,
658 typename Out1,
659 typename ElementFn,
660 typename ExecPreset = exec_presets::Materialized>
661inline auto SI8_SO(const char *name,
662 const ElementFn element_fn,
663 const ExecPreset exec_preset = exec_presets::Materialized())
664{
667}
668
670template<typename Mut1, typename ElementFn, typename ExecPreset = exec_presets::AllSpanOrSingle>
671inline auto SM(const char *name,
672 const ElementFn element_fn,
673 const ExecPreset exec_preset = exec_presets::AllSpanOrSingle())
674{
677 element_fn, exec_preset, param_tags);
678 return detail::CustomMF(name, call_fn, param_tags);
679}
680
682template<typename In1,
683 typename Out1,
684 typename Out2,
685 typename ElementFn,
686 typename ExecPreset = exec_presets::Materialized>
687inline auto SI1_SO2(const char *name,
688 const ElementFn element_fn,
689 const ExecPreset exec_preset = exec_presets::Materialized())
690{
692 name, element_fn, exec_preset, TypeSequence<In1>());
693}
694
696template<typename In1,
697 typename In2,
698 typename Out1,
699 typename Out2,
700 typename ElementFn,
701 typename ExecPreset = exec_presets::Materialized>
702inline auto SI2_SO2(const char *name,
703 const ElementFn element_fn,
704 const ExecPreset exec_preset = exec_presets::Materialized())
705{
707 name, element_fn, exec_preset, TypeSequence<In1, In2>());
708}
709
711template<typename In1,
712 typename In2,
713 typename In3,
714 typename Out1,
715 typename Out2,
716 typename ElementFn,
717 typename ExecPreset = exec_presets::Materialized>
718inline auto SI3_SO2(const char *name,
719 const ElementFn element_fn,
720 const ExecPreset exec_preset = exec_presets::Materialized())
721{
723 name, element_fn, exec_preset, TypeSequence<In1, In2, In3>());
724}
725
727template<typename In1,
728 typename In2,
729 typename In3,
730 typename In4,
731 typename Out1,
732 typename Out2,
733 typename ElementFn,
734 typename ExecPreset = exec_presets::Materialized>
735inline auto SI4_SO2(const char *name,
736 const ElementFn element_fn,
737 const ExecPreset exec_preset = exec_presets::Materialized())
738{
740 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4>());
741}
742
744template<typename In1,
745 typename In2,
746 typename In3,
747 typename In4,
748 typename In5,
749 typename Out1,
750 typename Out2,
751 typename ElementFn,
752 typename ExecPreset = exec_presets::Materialized>
753inline auto SI5_SO2(const char *name,
754 const ElementFn element_fn,
755 const ExecPreset exec_preset = exec_presets::Materialized())
756{
758 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5>());
759}
760
762template<typename In1,
763 typename Out1,
764 typename Out2,
765 typename Out3,
766 typename ElementFn,
767 typename ExecPreset = exec_presets::Materialized>
768inline auto SI1_SO3(const char *name,
769 const ElementFn element_fn,
770 const ExecPreset exec_preset = exec_presets::Materialized())
771{
777 element_fn, exec_preset, param_tags);
778 return detail::CustomMF(name, call_fn, param_tags);
779}
780
782template<typename In1,
783 typename Out1,
784 typename Out2,
785 typename Out3,
786 typename Out4,
787 typename ElementFn,
788 typename ExecPreset = exec_presets::Materialized>
789inline auto SI1_SO4(const char *name,
790 const ElementFn element_fn,
791 const ExecPreset exec_preset = exec_presets::Materialized())
792{
799 element_fn, exec_preset, param_tags);
800 return detail::CustomMF(name, call_fn, param_tags);
801}
802
803} // namespace blender::fn::multi_function::build
804
806
813 private:
814 const CPPType &type_;
815 const void *value_;
816 Signature signature_;
817 bool owns_value_;
818
819 template<typename T> friend class CustomMF_Constant;
820
821 public:
822 CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy);
823 ~CustomMF_GenericConstant() override;
824 void call(const IndexMask &mask, Params params, Context context) const override;
825 uint64_t hash() const override;
826 bool equals(const MultiFunction &other) const override;
827};
828
834 private:
835 GSpan array_;
836 Signature signature_;
837
838 public:
840 void call(const IndexMask &mask, Params params, Context context) const override;
841};
842
846template<typename T> class CustomMF_Constant : public MultiFunction {
847 private:
848 T value_;
849 Signature signature_;
850
851 public:
852 template<typename U> CustomMF_Constant(U &&value) : value_(std::forward<U>(value))
853 {
854 SignatureBuilder builder{"Constant", signature_};
855 builder.single_output<T>("Value");
856 this->set_signature(&signature_);
857 }
858
859 void call(const IndexMask &mask, Params params, Context /*context*/) const override
860 {
861 MutableSpan<T> output = params.uninitialized_single_output<T>(0);
862 mask.foreach_index_optimized<int64_t>([&](const int64_t i) { new (&output[i]) T(value_); });
863 }
864
865 uint64_t hash() const override
866 {
867 return get_default_hash(value_);
868 }
869
870 bool equals(const MultiFunction &other) const override
871 {
872 const CustomMF_Constant *other1 = dynamic_cast<const CustomMF_Constant *>(&other);
873 if (other1 != nullptr) {
874 return value_ == other1->value_;
875 }
876 const CustomMF_GenericConstant *other2 = dynamic_cast<const CustomMF_GenericConstant *>(
877 &other);
878 if (other2 != nullptr) {
879 const CPPType &type = CPPType::get<T>();
880 if (type == other2->type_) {
881 return type.is_equal_or_false(static_cast<const void *>(&value_), other2->value_);
882 }
883 }
884 return false;
885 }
886};
887
889 private:
890 int output_amount_;
891 Signature signature_;
892
893 public:
894 CustomMF_DefaultOutput(Span<DataType> input_types, Span<DataType> output_types);
895 void call(const IndexMask &mask, Params params, Context context) const override;
896};
897
899 private:
900 Signature signature_;
901
902 public:
904 void call(const IndexMask &mask, Params params, Context context) const override;
905};
906
907} // 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(const IndexMask &mask, void *dst, bool dst_is_uninitialized) 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
const char * name
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:4238