| 
									
										
										
										
											2022-02-11 09:07:11 +11:00
										 |  |  | /* SPDX-License-Identifier: GPL-2.0-or-later */ | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-07 09:50:34 +02:00
										 |  |  | #pragma once
 | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** \file
 | 
					
						
							|  |  |  |  * \ingroup fn | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file contains several utilities to create multi-functions with less redundant code. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "FN_multi_function.hh"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  | namespace blender::fn::multi_function::build { | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |  * These presets determine what code is generated for a #CustomMF. Different presets make different | 
					
						
							|  |  |  |  * trade-offs between run-time performance and compile-time/binary size. | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | namespace exec_presets { | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** Method to execute a function in case devirtualization was not possible. */ | 
					
						
							|  |  |  | enum class FallbackMode { | 
					
						
							|  |  |  |   /** Access all elements in virtual arrays through virtual function calls. */ | 
					
						
							|  |  |  |   Simple, | 
					
						
							|  |  |  |   /** Process elements in chunks to reduce virtual function call overhead. */ | 
					
						
							|  |  |  |   Materialized, | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * The "naive" method for executing a #CustomMF. Every element is processed separately and input | 
					
						
							|  |  |  |  * values are retrieved from the virtual arrays one by one. This generates the least amount of | 
					
						
							|  |  |  |  * code, but is also the slowest method. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct Simple { | 
					
						
							|  |  |  |   static constexpr bool use_devirtualization = false; | 
					
						
							|  |  |  |   static constexpr FallbackMode fallback_mode = FallbackMode::Simple; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * This is an improvement over the #Simple method. It still generates a relatively small amount of | 
					
						
							|  |  |  |  * code, because the function is only instantiated once. It's generally faster than #Simple, | 
					
						
							|  |  |  |  * because inputs are retrieved from the virtual arrays in chunks, reducing virtual method call | 
					
						
							|  |  |  |  * overhead. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct Materialized { | 
					
						
							|  |  |  |   static constexpr bool use_devirtualization = false; | 
					
						
							|  |  |  |   static constexpr FallbackMode fallback_mode = FallbackMode::Materialized; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * The most efficient preset, but also potentially generates a lot of code (exponential in the | 
					
						
							|  |  |  |  * number of inputs of the function). It generates separate optimized loops for all combinations of | 
					
						
							|  |  |  |  * inputs. This should be used for small functions of which all inputs are likely to be single | 
					
						
							|  |  |  |  * values or spans, and the number of inputs is relatively small. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct AllSpanOrSingle { | 
					
						
							|  |  |  |   static constexpr bool use_devirtualization = true; | 
					
						
							|  |  |  |   static constexpr FallbackMode fallback_mode = FallbackMode::Materialized; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |   template<typename... ParamTags, typename... LoadedParams, size_t... I> | 
					
						
							|  |  |  |   auto create_devirtualizers(TypeSequence<ParamTags...> /*param_tags*/, | 
					
						
							|  |  |  |                              std::index_sequence<I...> /*indices*/, | 
					
						
							|  |  |  |                              const IndexMask &mask, | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |                              const std::tuple<LoadedParams...> &loaded_params) const | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |     return std::make_tuple(IndexMaskDevirtualizer<true, true>{mask}, [&]() { | 
					
						
							|  |  |  |       typedef ParamTags ParamTag; | 
					
						
							|  |  |  |       typedef typename ParamTag::base_type T; | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |       if constexpr (ParamTag::category == ParamCategory::SingleInput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |         const GVArrayImpl &varray_impl = *std::get<I>(loaded_params); | 
					
						
							|  |  |  |         return GVArrayDevirtualizer<T, true, true>{varray_impl}; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |       else if constexpr (ELEM(ParamTag::category, | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |                               ParamCategory::SingleOutput, | 
					
						
							|  |  |  |                               ParamCategory::SingleMutable)) { | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |         T *ptr = std::get<I>(loaded_params); | 
					
						
							|  |  |  |         return BasicDevirtualizer<T *>{ptr}; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }()...); | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2022-04-28 14:03:49 +10:00
										 |  |  |  * A slightly weaker variant of #AllSpanOrSingle. It generates less code, because it assumes that | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |  * some of the inputs are most likely single values. It should be used for small functions which | 
					
						
							|  |  |  |  * have too many inputs to make #AllSingleOrSpan a reasonable choice. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | template<size_t... Indices> struct SomeSpanOrSingle { | 
					
						
							|  |  |  |   static constexpr bool use_devirtualization = true; | 
					
						
							|  |  |  |   static constexpr FallbackMode fallback_mode = FallbackMode::Materialized; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |   template<typename... ParamTags, typename... LoadedParams, size_t... I> | 
					
						
							|  |  |  |   auto create_devirtualizers(TypeSequence<ParamTags...> /*param_tags*/, | 
					
						
							|  |  |  |                              std::index_sequence<I...> /*indices*/, | 
					
						
							|  |  |  |                              const IndexMask &mask, | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |                              const std::tuple<LoadedParams...> &loaded_params) const | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |     return std::make_tuple(IndexMaskDevirtualizer<true, true>{mask}, [&]() { | 
					
						
							|  |  |  |       typedef ParamTags ParamTag; | 
					
						
							|  |  |  |       typedef typename ParamTag::base_type T; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |       if constexpr (ParamTag::category == ParamCategory::SingleInput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |         constexpr bool UseSpan = ValueSequence<size_t, Indices...>::template contains<I>(); | 
					
						
							|  |  |  |         const GVArrayImpl &varray_impl = *std::get<I>(loaded_params); | 
					
						
							|  |  |  |         return GVArrayDevirtualizer<T, true, UseSpan>{varray_impl}; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |       else if constexpr (ELEM(ParamTag::category, | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |                               ParamCategory::SingleOutput, | 
					
						
							|  |  |  |                               ParamCategory::SingleMutable)) { | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |         T *ptr = std::get<I>(loaded_params); | 
					
						
							|  |  |  |         return BasicDevirtualizer<T *>{ptr}; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }()...); | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | }  // namespace exec_presets
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace detail { | 
					
						
							| 
									
										
										
										
											2022-04-07 18:48:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Executes #element_fn for all indices in the mask. The passed in #args contain the input as well | 
					
						
							|  |  |  |  * as output parameters. Usually types in #args are devirtualized (e.g. a `Span<int>` is passed in | 
					
						
							|  |  |  |  * instead of a `VArray<int>`). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | template<typename MaskT, typename... Args, typename... ParamTags, size_t... I, typename ElementFn> | 
					
						
							| 
									
										
										
										
											2023-01-07 20:23:20 +01:00
										 |  |  | /* Perform additional optimizations on this loop because it is a very hot loop. For example, the
 | 
					
						
							|  |  |  |  * math node in geometry nodes is processed here.  */ | 
					
						
							|  |  |  | #if (defined(__GNUC__) && !defined(__clang__))
 | 
					
						
							|  |  |  | [[gnu::optimize("-funroll-loops")]] [[gnu::optimize("O3")]] | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-01-07 23:49:36 +01:00
										 |  |  | inline void | 
					
						
							|  |  |  | execute_array(TypeSequence<ParamTags...> /*param_tags*/, | 
					
						
							|  |  |  |               std::index_sequence<I...> /*indices*/, | 
					
						
							|  |  |  |               ElementFn element_fn, | 
					
						
							|  |  |  |               MaskT mask, | 
					
						
							|  |  |  |               /* Use restrict to tell the compiler that pointer inputs do not alias each
 | 
					
						
							|  |  |  |                * other. This is important for some compiler optimizations. */ | 
					
						
							|  |  |  |               Args &&__restrict... args) | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-08 15:04:51 +01:00
										 |  |  |   if constexpr (std::is_same_v<std::decay_t<MaskT>, IndexRange>) { | 
					
						
							| 
									
										
										
										
											2023-01-09 17:39:35 +11:00
										 |  |  |     /* Having this explicit loop is necessary for MSVC to be able to vectorize this. */ | 
					
						
							| 
									
										
										
										
											2023-01-08 15:04:51 +01:00
										 |  |  |     const int64_t start = mask.start(); | 
					
						
							|  |  |  |     const int64_t end = mask.one_after_last(); | 
					
						
							|  |  |  |     for (int64_t i = start; i < end; i++) { | 
					
						
							|  |  |  |       element_fn(args[i]...); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else { | 
					
						
							|  |  |  |     for (const int32_t i : mask) { | 
					
						
							|  |  |  |       element_fn(args[i]...); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-04-07 18:48:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | enum class MaterializeArgMode { | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |   Unknown, | 
					
						
							|  |  |  |   Single, | 
					
						
							|  |  |  |   Span, | 
					
						
							|  |  |  |   Materialized, | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2022-04-07 18:48:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | template<typename ParamTag> struct MaterializeArgInfo { | 
					
						
							|  |  |  |   MaterializeArgMode mode = MaterializeArgMode::Unknown; | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |   const typename ParamTag::base_type *internal_span_data; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2022-04-07 18:48:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |  * Similar to #execute_array but is only used with arrays and does not need a mask. | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | template<typename... ParamTags, typename ElementFn, typename... Chunks> | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  | #if (defined(__GNUC__) && !defined(__clang__))
 | 
					
						
							|  |  |  | [[gnu::optimize("-funroll-loops")]] [[gnu::optimize("O3")]] | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | inline void | 
					
						
							|  |  |  | execute_materialized_impl(TypeSequence<ParamTags...> /*param_tags*/, | 
					
						
							|  |  |  |                           const ElementFn element_fn, | 
					
						
							|  |  |  |                           const int64_t size, | 
					
						
							|  |  |  |                           Chunks &&__restrict... chunks) | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |   for (int64_t i = 0; i < size; i++) { | 
					
						
							|  |  |  |     element_fn(chunks[i]...); | 
					
						
							| 
									
										
										
										
											2022-04-04 11:57:39 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-04-04 11:57:39 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Executes #element_fn for all indices in #mask. However, instead of processing every element | 
					
						
							|  |  |  |  * separately, processing happens in chunks. This allows retrieving from input virtual arrays in | 
					
						
							|  |  |  |  * chunks, which reduces virtual function call overhead. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  | template<typename... ParamTags, size_t... I, typename ElementFn, typename... LoadedParams> | 
					
						
							| 
									
										
										
										
											2023-01-07 23:49:36 +01:00
										 |  |  | inline void execute_materialized(TypeSequence<ParamTags...> /* param_tags */, | 
					
						
							|  |  |  |                                  std::index_sequence<I...> /* indices */, | 
					
						
							|  |  |  |                                  const ElementFn element_fn, | 
					
						
							|  |  |  |                                  const IndexMask mask, | 
					
						
							|  |  |  |                                  const std::tuple<LoadedParams...> &loaded_params) | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* In theory, all elements could be processed in one chunk. However, that has the disadvantage
 | 
					
						
							|  |  |  |    * that large temporary arrays are needed. Using small chunks allows using small arrays, which | 
					
						
							|  |  |  |    * are reused multiple times, which improves cache efficiency. The chunk size also shouldn't be | 
					
						
							|  |  |  |    * too small, because then overhead of the outer loop over chunks becomes significant again. */ | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |   static constexpr int64_t MaxChunkSize = 64; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |   const int64_t mask_size = mask.size(); | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |   const int64_t tmp_buffer_size = std::min(mask_size, MaxChunkSize); | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |   /* Local buffers that are used to temporarily store values for processing. */ | 
					
						
							|  |  |  |   std::tuple<TypedBuffer<typename ParamTags::base_type, MaxChunkSize>...> temporary_buffers; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /* Information about every parameter. */ | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |   std::tuple<MaterializeArgInfo<ParamTags>...> args_info; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   ( | 
					
						
							|  |  |  |       /* Setup information for all parameters. */ | 
					
						
							|  |  |  |       [&] { | 
					
						
							| 
									
										
										
										
											2022-09-20 10:42:25 +02:00
										 |  |  |         /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							| 
									
										
										
										
											2022-05-12 13:38:22 +02:00
										 |  |  |         typedef ParamTags ParamTag; | 
					
						
							|  |  |  |         typedef typename ParamTag::base_type T; | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |         [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info); | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |         if constexpr (ParamTag::category == ParamCategory::SingleInput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |           const GVArrayImpl &varray_impl = *std::get<I>(loaded_params); | 
					
						
							|  |  |  |           const CommonVArrayInfo common_info = varray_impl.common_info(); | 
					
						
							|  |  |  |           if (common_info.type == CommonVArrayInfo::Type::Single) { | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |             /* If an input #VArray is a single value, we have to fill the buffer with that value
 | 
					
						
							|  |  |  |              * only once. The same unchanged buffer can then be reused in every chunk. */ | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |             const T &in_single = *static_cast<const T *>(common_info.data); | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |             T *tmp_buffer = std::get<I>(temporary_buffers).ptr(); | 
					
						
							|  |  |  |             uninitialized_fill_n(tmp_buffer, tmp_buffer_size, in_single); | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |             arg_info.mode = MaterializeArgMode::Single; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-01-07 12:55:48 +01:00
										 |  |  |           else if (common_info.type == CommonVArrayInfo::Type::Span) { | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |             /* Remember the span so that it doesn't have to be retrieved in every iteration. */ | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |             arg_info.internal_span_data = static_cast<const T *>(common_info.data); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           else { | 
					
						
							|  |  |  |             arg_info.internal_span_data = nullptr; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }(), | 
					
						
							|  |  |  |       ...); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Outer loop over all chunks. */ | 
					
						
							|  |  |  |   for (int64_t chunk_start = 0; chunk_start < mask_size; chunk_start += MaxChunkSize) { | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |     const int64_t chunk_end = std::min<int64_t>(chunk_start + MaxChunkSize, mask_size); | 
					
						
							|  |  |  |     const int64_t chunk_size = chunk_end - chunk_start; | 
					
						
							|  |  |  |     const IndexMask sliced_mask = mask.slice(chunk_start, chunk_size); | 
					
						
							|  |  |  |     const int64_t mask_start = sliced_mask[0]; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |     const bool sliced_mask_is_range = sliced_mask.is_range(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |     /* Move mutable data into temporary array. */ | 
					
						
							|  |  |  |     if (!sliced_mask_is_range) { | 
					
						
							|  |  |  |       ( | 
					
						
							|  |  |  |           [&] { | 
					
						
							|  |  |  |             /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							|  |  |  |             typedef ParamTags ParamTag; | 
					
						
							|  |  |  |             typedef typename ParamTag::base_type T; | 
					
						
							|  |  |  |             if constexpr (ParamTag::category == ParamCategory::SingleMutable) { | 
					
						
							|  |  |  |               T *tmp_buffer = std::get<I>(temporary_buffers).ptr(); | 
					
						
							|  |  |  |               T *param_buffer = std::get<I>(loaded_params); | 
					
						
							|  |  |  |               for (int64_t i = 0; i < chunk_size; i++) { | 
					
						
							|  |  |  |                 new (tmp_buffer + i) T(std::move(param_buffer[sliced_mask[i]])); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }(), | 
					
						
							|  |  |  |           ...); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |     execute_materialized_impl( | 
					
						
							|  |  |  |         TypeSequence<ParamTags...>(), | 
					
						
							|  |  |  |         element_fn, | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |         chunk_size, | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |         /* Prepare every parameter for this chunk. */ | 
					
						
							|  |  |  |         [&] { | 
					
						
							|  |  |  |           using ParamTag = ParamTags; | 
					
						
							|  |  |  |           using T = typename ParamTag::base_type; | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |           [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info); | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |           T *tmp_buffer = std::get<I>(temporary_buffers); | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |           if constexpr (ParamTag::category == ParamCategory::SingleInput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |             if (arg_info.mode == MaterializeArgMode::Single) { | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |               /* The single value has been filled into a buffer already reused for every chunk. */ | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |               return const_cast<const T *>(tmp_buffer); | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |             if (sliced_mask_is_range && arg_info.internal_span_data != nullptr) { | 
					
						
							|  |  |  |               /* In this case we can just use an existing span instead of "compressing" it into
 | 
					
						
							|  |  |  |                * a new temporary buffer. */ | 
					
						
							|  |  |  |               arg_info.mode = MaterializeArgMode::Span; | 
					
						
							|  |  |  |               return arg_info.internal_span_data + mask_start; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |             const GVArrayImpl &varray_impl = *std::get<I>(loaded_params); | 
					
						
							|  |  |  |             /* As a fallback, do a virtual function call to retrieve all elements in the current
 | 
					
						
							|  |  |  |              * chunk. The elements are stored in a temporary buffer reused for every chunk. */ | 
					
						
							|  |  |  |             varray_impl.materialize_compressed_to_uninitialized(sliced_mask, tmp_buffer); | 
					
						
							|  |  |  |             /* Remember that this parameter has been materialized, so that the values are
 | 
					
						
							|  |  |  |              * destructed properly when the chunk is done. */ | 
					
						
							|  |  |  |             arg_info.mode = MaterializeArgMode::Materialized; | 
					
						
							|  |  |  |             return const_cast<const T *>(tmp_buffer); | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |           else if constexpr (ELEM(ParamTag::category, | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |                                   ParamCategory::SingleOutput, | 
					
						
							|  |  |  |                                   ParamCategory::SingleMutable)) { | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |             /* For outputs, just pass a pointer. This is important so that `__restrict` works. */ | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |             if (sliced_mask_is_range) { | 
					
						
							|  |  |  |               /* Can write into the caller-provided buffer directly. */ | 
					
						
							|  |  |  |               T *param_buffer = std::get<I>(loaded_params); | 
					
						
							|  |  |  |               return param_buffer + mask_start; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |               /* Use the temporary buffer. The values will have to be copied out of that
 | 
					
						
							|  |  |  |                * buffer into the caller-provided buffer afterwards. */ | 
					
						
							|  |  |  |               return const_cast<T *>(tmp_buffer); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |           } | 
					
						
							|  |  |  |         }()...); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |     /* Relocate outputs from temporary buffers to buffers provided by caller. */ | 
					
						
							|  |  |  |     if (!sliced_mask_is_range) { | 
					
						
							|  |  |  |       ( | 
					
						
							|  |  |  |           [&] { | 
					
						
							|  |  |  |             /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							|  |  |  |             typedef ParamTags ParamTag; | 
					
						
							|  |  |  |             typedef typename ParamTag::base_type T; | 
					
						
							|  |  |  |             if constexpr (ELEM(ParamTag::category, | 
					
						
							|  |  |  |                                ParamCategory::SingleOutput, | 
					
						
							|  |  |  |                                ParamCategory::SingleMutable)) { | 
					
						
							|  |  |  |               T *tmp_buffer = std::get<I>(temporary_buffers).ptr(); | 
					
						
							|  |  |  |               T *param_buffer = std::get<I>(loaded_params); | 
					
						
							|  |  |  |               for (int64_t i = 0; i < chunk_size; i++) { | 
					
						
							|  |  |  |                 new (param_buffer + sliced_mask[i]) T(std::move(tmp_buffer[i])); | 
					
						
							|  |  |  |                 std::destroy_at(tmp_buffer + i); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }(), | 
					
						
							|  |  |  |           ...); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |     ( | 
					
						
							|  |  |  |         /* Destruct values that have been materialized before. */ | 
					
						
							|  |  |  |         [&] { | 
					
						
							| 
									
										
										
										
											2022-09-20 10:42:25 +02:00
										 |  |  |           /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							| 
									
										
										
										
											2022-05-12 13:38:22 +02:00
										 |  |  |           typedef ParamTags ParamTag; | 
					
						
							|  |  |  |           typedef typename ParamTag::base_type T; | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |           [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info); | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |           if constexpr (ParamTag::category == ParamCategory::SingleInput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |             if (arg_info.mode == MaterializeArgMode::Materialized) { | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |               T *tmp_buffer = std::get<I>(temporary_buffers).ptr(); | 
					
						
							|  |  |  |               destruct_n(tmp_buffer, chunk_size); | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }(), | 
					
						
							|  |  |  |         ...); | 
					
						
							| 
									
										
										
										
											2022-04-07 18:48:14 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |   ( | 
					
						
							|  |  |  |       /* Destruct buffers for single value inputs. */ | 
					
						
							|  |  |  |       [&] { | 
					
						
							| 
									
										
										
										
											2022-09-20 10:42:25 +02:00
										 |  |  |         /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							| 
									
										
										
										
											2022-05-12 13:38:22 +02:00
										 |  |  |         typedef ParamTags ParamTag; | 
					
						
							|  |  |  |         typedef typename ParamTag::base_type T; | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |         [[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info); | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |         if constexpr (ParamTag::category == ParamCategory::SingleInput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |           if (arg_info.mode == MaterializeArgMode::Single) { | 
					
						
							| 
									
										
										
										
											2023-01-08 17:19:57 +01:00
										 |  |  |             T *tmp_buffer = std::get<I>(temporary_buffers).ptr(); | 
					
						
							|  |  |  |             destruct_n(tmp_buffer, tmp_buffer_size); | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }(), | 
					
						
							|  |  |  |       ...); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | template<typename ElementFn, typename ExecPreset, typename... ParamTags, size_t... I> | 
					
						
							|  |  |  | inline void execute_element_fn_as_multi_function(const ElementFn element_fn, | 
					
						
							|  |  |  |                                                  const ExecPreset exec_preset, | 
					
						
							|  |  |  |                                                  const IndexMask mask, | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |                                                  Params params, | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |                                                  TypeSequence<ParamTags...> /*param_tags*/, | 
					
						
							|  |  |  |                                                  std::index_sequence<I...> /*indices*/) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   /* Load parameters from #Params. */ | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |   /* Contains `const GVArrayImpl *` for inputs and `T *` for outputs. */ | 
					
						
							|  |  |  |   const auto loaded_params = std::make_tuple([&]() { | 
					
						
							|  |  |  |     /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							|  |  |  |     typedef ParamTags ParamTag; | 
					
						
							|  |  |  |     typedef typename ParamTag::base_type T; | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |     if constexpr (ParamTag::category == ParamCategory::SingleInput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |       return params.readonly_single_input(I).get_implementation(); | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |     else if constexpr (ParamTag::category == ParamCategory::SingleOutput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |       return static_cast<T *>(params.uninitialized_single_output(I).data()); | 
					
						
							| 
									
										
										
										
											2022-04-26 17:12:34 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |     else if constexpr (ParamTag::category == ParamCategory::SingleMutable) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |       return static_cast<T *>(params.single_mutable(I).data()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }()...); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Try execute devirtualized if enabled and the input types allow it. */ | 
					
						
							|  |  |  |   bool executed_devirtualized = false; | 
					
						
							|  |  |  |   if constexpr (ExecPreset::use_devirtualization) { | 
					
						
							|  |  |  |     const auto devirtualizers = exec_preset.create_devirtualizers( | 
					
						
							|  |  |  |         TypeSequence<ParamTags...>(), std::index_sequence<I...>(), mask, loaded_params); | 
					
						
							|  |  |  |     executed_devirtualized = call_with_devirtualized_parameters( | 
					
						
							|  |  |  |         devirtualizers, [&](auto &&...args) { | 
					
						
							|  |  |  |           execute_array(TypeSequence<ParamTags...>(), | 
					
						
							|  |  |  |                         std::index_sequence<I...>(), | 
					
						
							|  |  |  |                         element_fn, | 
					
						
							|  |  |  |                         std::forward<decltype(args)>(args)...); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |   /* If devirtualized execution was disabled or not possible, use a fallback method which is
 | 
					
						
							|  |  |  |    * slower but always works. */ | 
					
						
							|  |  |  |   if (!executed_devirtualized) { | 
					
						
							|  |  |  |     /* The materialized method is most common because it avoids most virtual function overhead but
 | 
					
						
							|  |  |  |      * still instantiates the function only once. */ | 
					
						
							|  |  |  |     if constexpr (ExecPreset::fallback_mode == exec_presets::FallbackMode::Materialized) { | 
					
						
							|  |  |  |       execute_materialized(TypeSequence<ParamTags...>(), | 
					
						
							|  |  |  |                            std::index_sequence<I...>(), | 
					
						
							|  |  |  |                            element_fn, | 
					
						
							|  |  |  |                            mask, | 
					
						
							|  |  |  |                            loaded_params); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |       /* This fallback is slower because it uses virtual method calls for every element. */ | 
					
						
							|  |  |  |       execute_array( | 
					
						
							|  |  |  |           TypeSequence<ParamTags...>(), std::index_sequence<I...>(), element_fn, mask, [&]() { | 
					
						
							|  |  |  |             /* Use `typedef` instead of `using` to work around a compiler bug. */ | 
					
						
							|  |  |  |             typedef ParamTags ParamTag; | 
					
						
							|  |  |  |             typedef typename ParamTag::base_type T; | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |             if constexpr (ParamTag::category == ParamCategory::SingleInput) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |               const GVArrayImpl &varray_impl = *std::get<I>(loaded_params); | 
					
						
							|  |  |  |               return GVArray(&varray_impl).typed<T>(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else if constexpr (ELEM(ParamTag::category, | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |                                     ParamCategory::SingleOutput, | 
					
						
							|  |  |  |                                     ParamCategory::SingleMutable)) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |               T *ptr = std::get<I>(loaded_params); | 
					
						
							|  |  |  |               return ptr; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }()...); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-30 17:59:33 +02:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-06-30 17:59:33 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-08 11:37:37 +01:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |  * `element_fn` is expected to return nothing and to have the following parameters: | 
					
						
							|  |  |  |  * - For single-inputs: const value or reference. | 
					
						
							|  |  |  |  * - For single-mutables: non-const reference. | 
					
						
							|  |  |  |  * - For single-outputs: non-const pointer. | 
					
						
							| 
									
										
										
										
											2021-03-08 11:37:37 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | template<typename ElementFn, typename ExecPreset, typename... ParamTags> | 
					
						
							|  |  |  | inline auto build_multi_function_call_from_element_fn(const ElementFn element_fn, | 
					
						
							|  |  |  |                                                       const ExecPreset exec_preset, | 
					
						
							|  |  |  |                                                       TypeSequence<ParamTags...> /*param_tags*/) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   return [element_fn, exec_preset](const IndexMask mask, Params params) { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |     execute_element_fn_as_multi_function(element_fn, | 
					
						
							|  |  |  |                                          exec_preset, | 
					
						
							|  |  |  |                                          mask, | 
					
						
							|  |  |  |                                          params, | 
					
						
							|  |  |  |                                          TypeSequence<ParamTags...>(), | 
					
						
							|  |  |  |                                          std::make_index_sequence<sizeof...(ParamTags)>()); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-08 11:37:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |  * A multi function that just invokes the provided function in its #call method. | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | template<typename CallFn, typename... ParamTags> class CustomMF : public MultiFunction { | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |  private: | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |   Signature signature_; | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |   CallFn call_fn_; | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |  public: | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |   CustomMF(const char *name, CallFn call_fn, TypeSequence<ParamTags...> /*param_tags*/) | 
					
						
							|  |  |  |       : call_fn_(std::move(call_fn)) | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |     SignatureBuilder builder{name, signature_}; | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |     /* Loop over all parameter types and add an entry for each in the signature. */ | 
					
						
							| 
									
										
										
										
											2023-01-07 16:30:56 +01:00
										 |  |  |     ([&] { builder.add(ParamTags(), ""); }(), ...); | 
					
						
							| 
									
										
										
										
											2021-03-22 11:57:24 +01:00
										 |  |  |     this->set_signature(&signature_); | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   void call(IndexMask mask, Params params, Context /*context*/) const override | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |     call_fn_(mask, params); | 
					
						
							| 
									
										
										
										
											2020-06-22 15:50:08 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | template<typename Out, typename... In, typename ElementFn, typename ExecPreset> | 
					
						
							|  |  |  | inline auto build_multi_function_with_n_inputs_one_output(const char *name, | 
					
						
							|  |  |  |                                                           const ElementFn element_fn, | 
					
						
							|  |  |  |                                                           const ExecPreset exec_preset, | 
					
						
							|  |  |  |                                                           TypeSequence<In...> /*in_types*/) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   constexpr auto param_tags = TypeSequence<ParamTag<ParamCategory::SingleInput, In>..., | 
					
						
							|  |  |  |                                            ParamTag<ParamCategory::SingleOutput, Out>>(); | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |   auto call_fn = build_multi_function_call_from_element_fn( | 
					
						
							| 
									
										
										
										
											2023-01-08 15:04:51 +01:00
										 |  |  |       [element_fn](const In &...in, Out &out) { new (&out) Out(element_fn(in...)); }, | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |       exec_preset, | 
					
						
							|  |  |  |       param_tags); | 
					
						
							|  |  |  |   return CustomMF(name, call_fn, param_tags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace detail
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Build multi-function with 1 single-input and 1 single-output parameter. */ | 
					
						
							|  |  |  | template<typename In1, | 
					
						
							|  |  |  |          typename Out1, | 
					
						
							|  |  |  |          typename ElementFn, | 
					
						
							|  |  |  |          typename ExecPreset = exec_presets::Materialized> | 
					
						
							|  |  |  | inline auto SI1_SO(const char *name, | 
					
						
							|  |  |  |                    const ElementFn element_fn, | 
					
						
							|  |  |  |                    const ExecPreset exec_preset = exec_presets::Materialized()) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return detail::build_multi_function_with_n_inputs_one_output<Out1>( | 
					
						
							|  |  |  |       name, element_fn, exec_preset, TypeSequence<In1>()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Build multi-function with 2 single-input and 1 single-output parameter. */ | 
					
						
							|  |  |  | template<typename In1, | 
					
						
							|  |  |  |          typename In2, | 
					
						
							|  |  |  |          typename Out1, | 
					
						
							|  |  |  |          typename ElementFn, | 
					
						
							|  |  |  |          typename ExecPreset = exec_presets::Materialized> | 
					
						
							|  |  |  | inline auto SI2_SO(const char *name, | 
					
						
							|  |  |  |                    const ElementFn element_fn, | 
					
						
							|  |  |  |                    const ExecPreset exec_preset = exec_presets::Materialized()) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return detail::build_multi_function_with_n_inputs_one_output<Out1>( | 
					
						
							|  |  |  |       name, element_fn, exec_preset, TypeSequence<In1, In2>()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Build multi-function with 3 single-input and 1 single-output parameter. */ | 
					
						
							|  |  |  | template<typename In1, | 
					
						
							|  |  |  |          typename In2, | 
					
						
							|  |  |  |          typename In3, | 
					
						
							|  |  |  |          typename Out1, | 
					
						
							|  |  |  |          typename ElementFn, | 
					
						
							|  |  |  |          typename ExecPreset = exec_presets::Materialized> | 
					
						
							|  |  |  | inline auto SI3_SO(const char *name, | 
					
						
							|  |  |  |                    const ElementFn element_fn, | 
					
						
							|  |  |  |                    const ExecPreset exec_preset = exec_presets::Materialized()) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return detail::build_multi_function_with_n_inputs_one_output<Out1>( | 
					
						
							|  |  |  |       name, element_fn, exec_preset, TypeSequence<In1, In2, In3>()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Build multi-function with 4 single-input and 1 single-output parameter. */ | 
					
						
							|  |  |  | template<typename In1, | 
					
						
							|  |  |  |          typename In2, | 
					
						
							|  |  |  |          typename In3, | 
					
						
							|  |  |  |          typename In4, | 
					
						
							|  |  |  |          typename Out1, | 
					
						
							|  |  |  |          typename ElementFn, | 
					
						
							|  |  |  |          typename ExecPreset = exec_presets::Materialized> | 
					
						
							|  |  |  | inline auto SI4_SO(const char *name, | 
					
						
							|  |  |  |                    const ElementFn element_fn, | 
					
						
							|  |  |  |                    const ExecPreset exec_preset = exec_presets::Materialized()) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return detail::build_multi_function_with_n_inputs_one_output<Out1>( | 
					
						
							|  |  |  |       name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4>()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Build multi-function with 5 single-input and 1 single-output parameter. */ | 
					
						
							|  |  |  | template<typename In1, | 
					
						
							|  |  |  |          typename In2, | 
					
						
							|  |  |  |          typename In3, | 
					
						
							|  |  |  |          typename In4, | 
					
						
							|  |  |  |          typename In5, | 
					
						
							|  |  |  |          typename Out1, | 
					
						
							|  |  |  |          typename ElementFn, | 
					
						
							|  |  |  |          typename ExecPreset = exec_presets::Materialized> | 
					
						
							|  |  |  | inline auto SI5_SO(const char *name, | 
					
						
							|  |  |  |                    const ElementFn element_fn, | 
					
						
							|  |  |  |                    const ExecPreset exec_preset = exec_presets::Materialized()) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return detail::build_multi_function_with_n_inputs_one_output<Out1>( | 
					
						
							|  |  |  |       name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5>()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Build multi-function with 6 single-input and 1 single-output parameter. */ | 
					
						
							|  |  |  | template<typename In1, | 
					
						
							|  |  |  |          typename In2, | 
					
						
							|  |  |  |          typename In3, | 
					
						
							|  |  |  |          typename In4, | 
					
						
							|  |  |  |          typename In5, | 
					
						
							|  |  |  |          typename In6, | 
					
						
							|  |  |  |          typename Out1, | 
					
						
							|  |  |  |          typename ElementFn, | 
					
						
							|  |  |  |          typename ExecPreset = exec_presets::Materialized> | 
					
						
							|  |  |  | inline auto SI6_SO(const char *name, | 
					
						
							|  |  |  |                    const ElementFn element_fn, | 
					
						
							|  |  |  |                    const ExecPreset exec_preset = exec_presets::Materialized()) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return detail::build_multi_function_with_n_inputs_one_output<Out1>( | 
					
						
							|  |  |  |       name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5, In6>()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Build multi-function with 1 single-mutable parameter. */ | 
					
						
							|  |  |  | template<typename Mut1, typename ElementFn, typename ExecPreset = exec_presets::AllSpanOrSingle> | 
					
						
							|  |  |  | inline auto SM(const char *name, | 
					
						
							|  |  |  |                const ElementFn element_fn, | 
					
						
							|  |  |  |                const ExecPreset exec_preset = exec_presets::AllSpanOrSingle()) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   constexpr auto param_tags = TypeSequence<ParamTag<ParamCategory::SingleMutable, Mut1>>(); | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  |   auto call_fn = detail::build_multi_function_call_from_element_fn( | 
					
						
							|  |  |  |       element_fn, exec_preset, param_tags); | 
					
						
							|  |  |  |   return detail::CustomMF(name, call_fn, param_tags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  | }  // namespace blender::fn::multi_function::build
 | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  | namespace blender::fn::multi_function { | 
					
						
							| 
									
										
										
										
											2023-01-07 16:19:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-07 19:34:35 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * A multi-function that outputs the same value every time. The value is not owned by an instance | 
					
						
							| 
									
										
										
										
											2021-09-11 13:05:20 +02:00
										 |  |  |  * of this function. If #make_value_copy is false, the caller is responsible for destructing and | 
					
						
							|  |  |  |  * freeing the value. | 
					
						
							| 
									
										
										
										
											2020-07-07 19:34:35 +02:00
										 |  |  |  */ | 
					
						
							|  |  |  | class CustomMF_GenericConstant : public MultiFunction { | 
					
						
							|  |  |  |  private: | 
					
						
							|  |  |  |   const CPPType &type_; | 
					
						
							|  |  |  |   const void *value_; | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |   Signature signature_; | 
					
						
							| 
									
										
										
										
											2021-09-11 13:05:20 +02:00
										 |  |  |   bool owns_value_; | 
					
						
							| 
									
										
										
										
											2020-07-07 19:34:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  |   template<typename T> friend class CustomMF_Constant; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-07 19:34:35 +02:00
										 |  |  |  public: | 
					
						
							| 
									
										
										
										
											2021-09-11 13:05:20 +02:00
										 |  |  |   CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy); | 
					
						
							|  |  |  |   ~CustomMF_GenericConstant(); | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   void call(IndexMask mask, Params params, Context context) const override; | 
					
						
							| 
									
										
										
										
											2020-07-20 12:16:20 +02:00
										 |  |  |   uint64_t hash() const override; | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  |   bool equals(const MultiFunction &other) const override; | 
					
						
							| 
									
										
										
										
											2020-07-07 19:34:35 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * A multi-function that outputs the same array every time. The array is not owned by in instance | 
					
						
							|  |  |  |  * of this function. The caller is responsible for destructing and freeing the values. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | class CustomMF_GenericConstantArray : public MultiFunction { | 
					
						
							|  |  |  |  private: | 
					
						
							|  |  |  |   GSpan array_; | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |   Signature signature_; | 
					
						
							| 
									
										
										
										
											2020-07-07 19:34:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |  public: | 
					
						
							|  |  |  |   CustomMF_GenericConstantArray(GSpan array); | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   void call(IndexMask mask, Params params, Context context) const override; | 
					
						
							| 
									
										
										
										
											2020-07-07 19:34:35 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Generates a multi-function that outputs a constant value. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | template<typename T> class CustomMF_Constant : public MultiFunction { | 
					
						
							|  |  |  |  private: | 
					
						
							|  |  |  |   T value_; | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |   Signature signature_; | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |  public: | 
					
						
							|  |  |  |   template<typename U> CustomMF_Constant(U &&value) : value_(std::forward<U>(value)) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |     SignatureBuilder builder{"Constant", signature_}; | 
					
						
							| 
									
										
										
										
											2023-01-07 16:30:56 +01:00
										 |  |  |     builder.single_output<T>("Value"); | 
					
						
							| 
									
										
										
										
											2021-03-22 11:57:24 +01:00
										 |  |  |     this->set_signature(&signature_); | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   void call(IndexMask mask, Params params, Context /*context*/) const override | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     MutableSpan<T> output = params.uninitialized_single_output<T>(0); | 
					
						
							| 
									
										
										
										
											2022-03-29 10:11:49 +02:00
										 |  |  |     mask.to_best_mask_type([&](const auto &mask) { | 
					
						
							|  |  |  |       for (const int64_t i : mask) { | 
					
						
							|  |  |  |         new (&output[i]) T(value_); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-20 12:16:20 +02:00
										 |  |  |   uint64_t hash() const override | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2021-03-25 16:01:28 +01:00
										 |  |  |     return get_default_hash(value_); | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool equals(const MultiFunction &other) const override | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     const CustomMF_Constant *other1 = dynamic_cast<const CustomMF_Constant *>(&other); | 
					
						
							|  |  |  |     if (other1 != nullptr) { | 
					
						
							|  |  |  |       return value_ == other1->value_; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const CustomMF_GenericConstant *other2 = dynamic_cast<const CustomMF_GenericConstant *>( | 
					
						
							|  |  |  |         &other); | 
					
						
							|  |  |  |     if (other2 != nullptr) { | 
					
						
							| 
									
										
										
										
											2020-07-10 12:56:57 +02:00
										 |  |  |       const CPPType &type = CPPType::get<T>(); | 
					
						
							|  |  |  |       if (type == other2->type_) { | 
					
						
							| 
									
										
										
										
											2021-06-28 13:13:52 +02:00
										 |  |  |         return type.is_equal_or_false(static_cast<const void *>(&value_), other2->value_); | 
					
						
							| 
									
										
										
										
											2020-07-08 15:04:28 +02:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-16 13:38:23 +02:00
										 |  |  | class CustomMF_DefaultOutput : public MultiFunction { | 
					
						
							|  |  |  |  private: | 
					
						
							| 
									
										
										
										
											2020-07-20 12:16:20 +02:00
										 |  |  |   int output_amount_; | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |   Signature signature_; | 
					
						
							| 
									
										
										
										
											2020-07-16 13:38:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |  public: | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |   CustomMF_DefaultOutput(Span<DataType> input_types, Span<DataType> output_types); | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   void call(IndexMask mask, Params params, Context context) const override; | 
					
						
							| 
									
										
										
										
											2020-07-16 13:38:23 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-09 12:54:20 +02:00
										 |  |  | class CustomMF_GenericCopy : public MultiFunction { | 
					
						
							|  |  |  |  private: | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |   Signature signature_; | 
					
						
							| 
									
										
										
										
											2021-09-09 12:54:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |  public: | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  |   CustomMF_GenericCopy(DataType data_type); | 
					
						
							| 
									
										
										
										
											2023-01-14 15:42:52 +01:00
										 |  |  |   void call(IndexMask mask, Params params, Context context) const override; | 
					
						
							| 
									
										
										
										
											2021-09-09 12:54:20 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-07 17:32:28 +01:00
										 |  |  | }  // namespace blender::fn::multi_function
 |