Blender V4.3
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
13#include "FN_multi_function.hh"
14
16
21namespace exec_presets {
22
24enum class FallbackMode {
26 Simple,
29};
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 typedef ParamTags ParamTag;
69 typedef typename ParamTag::base_type T;
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 typedef ParamTags ParamTag;
101 typedef typename ParamTag::base_type T;
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
159 Unknown,
160 Single,
161 Span,
163};
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,
197 const IndexMaskSegment mask,
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 typedef ParamTags ParamTag;
220 typedef typename ParamTag::base_type T;
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 typedef ParamTags ParamTag;
262 typedef typename ParamTag::base_type T;
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 else {
321 /* Use the temporary buffer. The values will have to be copied out of that
322 * buffer into the caller-provided buffer afterwards. */
323 return const_cast<T *>(tmp_buffer);
324 }
325 }
326 }()...);
327
328 /* Relocate outputs from temporary buffers to buffers provided by caller. */
329 if (!sliced_mask_is_range) {
330 (
331 [&] {
332 /* Use `typedef` instead of `using` to work around a compiler bug. */
333 typedef ParamTags ParamTag;
334 typedef typename ParamTag::base_type T;
335 if constexpr (ELEM(ParamTag::category,
338 {
339 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
340 T *param_buffer = std::get<I>(loaded_params);
341 for (int64_t i = 0; i < chunk_size; i++) {
342 new (param_buffer + sliced_mask[i]) T(std::move(tmp_buffer[i]));
343 std::destroy_at(tmp_buffer + i);
344 }
345 }
346 }(),
347 ...);
348 }
349
350 (
351 /* Destruct values that have been materialized before. */
352 [&] {
353 /* Use `typedef` instead of `using` to work around a compiler bug. */
354 typedef ParamTags ParamTag;
355 typedef typename ParamTag::base_type T;
356 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
358 if (arg_info.mode == MaterializeArgMode::Materialized) {
359 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
360 destruct_n(tmp_buffer, chunk_size);
361 }
362 }
363 }(),
364 ...);
365 }
366
367 (
368 /* Destruct buffers for single value inputs. */
369 [&] {
370 /* Use `typedef` instead of `using` to work around a compiler bug. */
371 typedef ParamTags ParamTag;
372 typedef typename ParamTag::base_type T;
373 [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
375 if (arg_info.mode == MaterializeArgMode::Single) {
376 T *tmp_buffer = std::get<I>(temporary_buffers).ptr();
377 destruct_n(tmp_buffer, tmp_buffer_size);
378 }
379 }
380 }(),
381 ...);
382}
383
384template<typename ElementFn, typename ExecPreset, typename... ParamTags, size_t... I>
385inline void execute_element_fn_as_multi_function(const ElementFn element_fn,
386 const ExecPreset exec_preset,
387 const IndexMask &mask,
389 TypeSequence<ParamTags...> /*param_tags*/,
390 std::index_sequence<I...> /*indices*/)
391{
392
393 /* Load parameters from #Params. */
394 /* Contains `const GVArrayImpl *` for inputs and `T *` for outputs. */
395 const auto loaded_params = std::make_tuple([&]() {
396 /* Use `typedef` instead of `using` to work around a compiler bug. */
397 typedef ParamTags ParamTag;
398 typedef typename ParamTag::base_type T;
399
401 return params.readonly_single_input(I).get_implementation();
402 }
403 else if constexpr (ParamTag::category == ParamCategory::SingleOutput) {
404 return static_cast<T *>(params.uninitialized_single_output(I).data());
405 }
406 else if constexpr (ParamTag::category == ParamCategory::SingleMutable) {
407 return static_cast<T *>(params.single_mutable(I).data());
408 }
409 }()...);
410
411 /* Try execute devirtualized if enabled and the input types allow it. */
412 bool executed_devirtualized = false;
413 if constexpr (ExecPreset::use_devirtualization) {
414 /* Get segments before devirtualization to avoid generating this code multiple times. */
416 mask.to_spans_and_ranges<16>();
417
418 const auto devirtualizers = exec_preset.create_devirtualizers(
419 TypeSequence<ParamTags...>(), std::index_sequence<I...>(), loaded_params);
420 executed_devirtualized = call_with_devirtualized_parameters(
421 devirtualizers, [&](auto &&...args) {
422 for (const std::variant<IndexRange, IndexMaskSegment> &segment : mask_segments) {
423 if (std::holds_alternative<IndexRange>(segment)) {
424 const auto segment_range = std::get<IndexRange>(segment);
426 std::index_sequence<I...>(),
427 element_fn,
428 segment_range,
429 std::forward<decltype(args)>(args)...);
430 }
431 else {
432 const auto segment_indices = std::get<IndexMaskSegment>(segment);
434 std::index_sequence<I...>(),
435 element_fn,
436 segment_indices,
437 std::forward<decltype(args)>(args)...);
438 }
439 }
440 });
441 }
442 else {
443 UNUSED_VARS(exec_preset);
444 }
445
446 /* If devirtualized execution was disabled or not possible, use a fallback method which is
447 * slower but always works. */
448 if (!executed_devirtualized) {
449 /* The materialized method is most common because it avoids most virtual function overhead but
450 * still instantiates the function only once. */
451 if constexpr (ExecPreset::fallback_mode == exec_presets::FallbackMode::Materialized) {
452 mask.foreach_segment([&](const IndexMaskSegment segment) {
454 std::index_sequence<I...>(),
455 element_fn,
456 segment,
457 loaded_params);
458 });
459 }
460 else {
461 /* This fallback is slower because it uses virtual method calls for every element. */
463 TypeSequence<ParamTags...>(), std::index_sequence<I...>(), element_fn, mask, [&]() {
464 /* Use `typedef` instead of `using` to work around a compiler bug. */
465 typedef ParamTags ParamTag;
466 typedef typename ParamTag::base_type T;
468 const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
469 return GVArray(&varray_impl).typed<T>();
470 }
471 else if constexpr (ELEM(ParamTag::category,
474 {
475 T *ptr = std::get<I>(loaded_params);
476 return ptr;
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
543} // namespace detail
544
546template<typename In1,
547 typename Out1,
548 typename ElementFn,
549 typename ExecPreset = exec_presets::Materialized>
550inline auto SI1_SO(const char *name,
551 const ElementFn element_fn,
552 const ExecPreset exec_preset = exec_presets::Materialized())
553{
555 name, element_fn, exec_preset, TypeSequence<In1>());
556}
557
559template<typename In1,
560 typename In2,
561 typename Out1,
562 typename ElementFn,
563 typename ExecPreset = exec_presets::Materialized>
564inline auto SI2_SO(const char *name,
565 const ElementFn element_fn,
566 const ExecPreset exec_preset = exec_presets::Materialized())
567{
569 name, element_fn, exec_preset, TypeSequence<In1, In2>());
570}
571
573template<typename In1,
574 typename In2,
575 typename In3,
576 typename Out1,
577 typename ElementFn,
578 typename ExecPreset = exec_presets::Materialized>
579inline auto SI3_SO(const char *name,
580 const ElementFn element_fn,
581 const ExecPreset exec_preset = exec_presets::Materialized())
582{
584 name, element_fn, exec_preset, TypeSequence<In1, In2, In3>());
585}
586
588template<typename In1,
589 typename In2,
590 typename In3,
591 typename In4,
592 typename Out1,
593 typename ElementFn,
594 typename ExecPreset = exec_presets::Materialized>
595inline auto SI4_SO(const char *name,
596 const ElementFn element_fn,
597 const ExecPreset exec_preset = exec_presets::Materialized())
598{
600 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4>());
601}
602
604template<typename In1,
605 typename In2,
606 typename In3,
607 typename In4,
608 typename In5,
609 typename Out1,
610 typename ElementFn,
611 typename ExecPreset = exec_presets::Materialized>
612inline auto SI5_SO(const char *name,
613 const ElementFn element_fn,
614 const ExecPreset exec_preset = exec_presets::Materialized())
615{
617 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5>());
618}
619
621template<typename In1,
622 typename In2,
623 typename In3,
624 typename In4,
625 typename In5,
626 typename In6,
627 typename Out1,
628 typename ElementFn,
629 typename ExecPreset = exec_presets::Materialized>
630inline auto SI6_SO(const char *name,
631 const ElementFn element_fn,
632 const ExecPreset exec_preset = exec_presets::Materialized())
633{
635 name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5, In6>());
636}
637
639template<typename Mut1, typename ElementFn, typename ExecPreset = exec_presets::AllSpanOrSingle>
640inline auto SM(const char *name,
641 const ElementFn element_fn,
642 const ExecPreset exec_preset = exec_presets::AllSpanOrSingle())
643{
646 element_fn, exec_preset, param_tags);
647 return detail::CustomMF(name, call_fn, param_tags);
648}
649
650} // namespace blender::fn::multi_function::build
651
653
660 private:
661 const CPPType &type_;
662 const void *value_;
663 Signature signature_;
664 bool owns_value_;
665
666 template<typename T> friend class CustomMF_Constant;
667
668 public:
669 CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy);
671 void call(const IndexMask &mask, Params params, Context context) const override;
672 uint64_t hash() const override;
673 bool equals(const MultiFunction &other) const override;
674};
675
681 private:
682 GSpan array_;
683 Signature signature_;
684
685 public:
687 void call(const IndexMask &mask, Params params, Context context) const override;
688};
689
693template<typename T> class CustomMF_Constant : public MultiFunction {
694 private:
695 T value_;
696 Signature signature_;
697
698 public:
699 template<typename U> CustomMF_Constant(U &&value) : value_(std::forward<U>(value))
700 {
701 SignatureBuilder builder{"Constant", signature_};
702 builder.single_output<T>("Value");
703 this->set_signature(&signature_);
704 }
705
706 void call(const IndexMask &mask, Params params, Context /*context*/) const override
707 {
708 MutableSpan<T> output = params.uninitialized_single_output<T>(0);
709 mask.foreach_index_optimized<int64_t>([&](const int64_t i) { new (&output[i]) T(value_); });
710 }
711
712 uint64_t hash() const override
713 {
714 return get_default_hash(value_);
715 }
716
717 bool equals(const MultiFunction &other) const override
718 {
719 const CustomMF_Constant *other1 = dynamic_cast<const CustomMF_Constant *>(&other);
720 if (other1 != nullptr) {
721 return value_ == other1->value_;
722 }
723 const CustomMF_GenericConstant *other2 = dynamic_cast<const CustomMF_GenericConstant *>(
724 &other);
725 if (other2 != nullptr) {
726 const CPPType &type = CPPType::get<T>();
727 if (type == other2->type_) {
728 return type.is_equal_or_false(static_cast<const void *>(&value_), other2->value_);
729 }
730 }
731 return false;
732 }
733};
734
736 private:
737 int output_amount_;
738 Signature signature_;
739
740 public:
741 CustomMF_DefaultOutput(Span<DataType> input_types, Span<DataType> output_types);
742 void call(const IndexMask &mask, Params params, Context context) const override;
743};
744
746 private:
747 Signature signature_;
748
749 public:
751 void call(const IndexMask &mask, Params params, Context context) const override;
752};
753
754} // namespace blender::fn::multi_function
#define UNUSED_VARS(...)
#define ELEM(...)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
unsigned int U
Definition btGjkEpa3.h:78
static const CPPType & get()
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)
CustomMF(const char *name, CallFn call_fn, TypeSequence< ParamTags... >)
void call(const IndexMask &mask, Params params, Context) const override
const IndexMask & update(IndexMaskSegment segment)
IndexMaskSegment slice(const IndexRange &range) const
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float4 mask(const int4 mask, const float4 a)
#define T
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 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 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 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())
bool non_empty_is_range(const Span< T > indices)
bool call_with_devirtualized_parameters(const std::tuple< Devirtualizers... > &devis, const Fn &fn)
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
void uninitialized_fill_n(T *dst, int64_t n, const T &value)
void destruct_n(T *ptr, int64_t n)
#define I
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
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
PointerRNA * ptr
Definition wm_files.cc:4126