diff options
28 files changed, 1552 insertions, 1158 deletions
diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 0dc46f87537..88e0bae0c13 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_array.hh" +#include "BLI_devirtualize_parameters.hh" #include "BLI_set.hh" #include "BLI_task.hh" diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc index 1c2665769db..0b5d6ad7b10 100644 --- a/source/blender/blenkernel/intern/type_conversions.cc +++ b/source/blender/blenkernel/intern/type_conversions.cc @@ -24,7 +24,8 @@ static void add_implicit_conversion(DataTypeConversions &conversions) conversion_name.c_str(), /* Use lambda instead of passing #ConversionF directly, because otherwise the compiler won't * inline the function. */ - [](const From &a) { return ConversionF(a); }}; + [](const From &a) { return ConversionF(a); }, + fn::CustomMF_presets::AllSpanOrSingle()}; static auto convert_single_to_initialized = [](const void *src, void *dst) { *(To *)dst = ConversionF(*(const From *)src); }; diff --git a/source/blender/blenlib/BLI_devirtualize_parameters.hh b/source/blender/blenlib/BLI_devirtualize_parameters.hh new file mode 100644 index 00000000000..bf4f6c47cfe --- /dev/null +++ b/source/blender/blenlib/BLI_devirtualize_parameters.hh @@ -0,0 +1,309 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * In geometry nodes, many functions accept fields as inputs. For the implementation that means + * that the inputs are virtual arrays. Usually those are backed by actual arrays or single values + * but sometimes virtual arrays are used to compute values on demand or convert between data + * formats. + * + * Using virtual arrays has the downside that individual elements are accessed through a virtual + * method call, which has some overhead compared to normal array access. Whether this overhead is + * negilible depends on the context. For very small functions (e.g. a single addition), the + * overhead can make the function many times slower. Furthermore, it prevents the compiler from + * doing some optimizations (e.g. loop unrolling and inserting SIMD instructions). + * + * The solution is to "devirtualize" the virtual arrays in cases when the overhead cannot be + * ignored. That means that the function is instantiated multiple times at compile time for the + * different cases. For example, there can be an optimized function that adds a span and a single + * value, and another function that adds a span and another span. At run-time there is a dynamic + * dispatch that executes the best function given the specific virtual arrays. + * + * The problem with this devirtualization is that it can result in exponentially increasing compile + * times and binary sizes, depending on the number of parameters that are devirtualized separately. + * So there is always a trade-off between run-time performance and compile-time/binary-size. + * + * This file provides a utility to devirtualize array parameters to a function using a high level + * API. This makes it easy to experiment with different extremes of the mentioned trade-off and + * allows finding a good compromise for each function. + */ + +#include "BLI_parameter_pack_utils.hh" +#include "BLI_virtual_array.hh" + +namespace blender::devirtualize_parameters { + +/** + * Bit flag that specifies how an individual parameter is or can be devirtualized. + */ +enum class DeviMode { + /* This is used as zero-value to compare to, to avoid casting to int. */ + None = 0, + /* Don't use devirtualization for that parameter, just pass it along. */ + Keep = (1 << 0), + /* Devirtualize #Varray as #Span. */ + Span = (1 << 1), + /* Devirtualize #VArray as #SingleAsSpan. */ + Single = (1 << 2), + /* Devirtualize #IndexMask as #IndexRange. */ + Range = (1 << 3), +}; +ENUM_OPERATORS(DeviMode, DeviMode::Range); + +/** Utility to encode multiple #DeviMode in a type. */ +template<DeviMode... Mode> using DeviModeSequence = ValueSequence<DeviMode, Mode...>; + +/** + * Main class that performs the devirtualization. + */ +template<typename Fn, typename... SourceTypes> class Devirtualizer { + private: + /** Utility to get the tag of the I-th source type. */ + template<size_t I> + using type_at_index = typename TypeSequence<SourceTypes...>::template at_index<I>; + static constexpr size_t SourceTypesNum = sizeof...(SourceTypes); + + /** Function to devirtualize. */ + Fn fn_; + + /** + * Source values that will be devirtualized. Note that these are stored as pointers to avoid + * unnecessary copies. The caller is responsible for keeping the memory alive. + */ + std::tuple<const SourceTypes *...> sources_; + + /** Keeps track of whether #fn_ has been called already to avoid calling it twice. */ + bool executed_ = false; + + public: + Devirtualizer(Fn fn, const SourceTypes *...sources) : fn_(std::move(fn)), sources_{sources...} + { + } + + /** + * Return true when the function passed to the constructor has been called already. + */ + bool executed() const + { + return executed_; + } + + /** + * At compile time, generates multiple variants of the function, each optimized for a different + * combination of devirtualized parameters. For every parameter, a bit flag is passed that + * determines how it will be devirtualized. At run-time, if possible, one of the generated + * functions is picked and executed. + * + * To check whether the function was called successfully, call #executed() afterwards. + * + * \note This generates an exponential amount of code in the final binary, depending on how many + * to-be-virtualized parameters there are. + */ + template<DeviMode... AllowedModes> + void try_execute_devirtualized(DeviModeSequence<AllowedModes...> /* allowed_modes */) + { + BLI_assert(!executed_); + static_assert(sizeof...(AllowedModes) == SourceTypesNum); + return this->try_execute_devirtualized_impl(DeviModeSequence<>(), + DeviModeSequence<AllowedModes...>()); + } + + /** + * Execute the function and pass in the original parameters without doing any devirtualization. + */ + void execute_without_devirtualization() + { + BLI_assert(!executed_); + this->try_execute_devirtualized_impl_call( + make_value_sequence<DeviMode, DeviMode::Keep, SourceTypesNum>(), + std::make_index_sequence<SourceTypesNum>()); + } + + private: + /** + * A recursive method that generates all the combinations of devirtualized parameters that the + * caller requested. A recursive function is necessary to achieve generating an exponential + * number of function calls (which has to be used with care, but is expected here). + * + * At every recursive step, the #DeviMode of one parameter is determined. This is achieved by + * extending #DeviModeSequence<Mode...> by one element in each step. The recursion ends once all + * parameters are handled. + */ + template<DeviMode... Mode, DeviMode... AllowedModes> + void try_execute_devirtualized_impl( + /* Initially empty, but then extended by one element in each recursive step. */ + DeviModeSequence<Mode...> /* modes */, + /* Bit flag for every parameter. */ + DeviModeSequence<AllowedModes...> /* allowed_modes */) + { + static_assert(SourceTypesNum == sizeof...(AllowedModes)); + if constexpr (SourceTypesNum == sizeof...(Mode)) { + /* End of recursion, now call the function with the determined #DeviModes. */ + this->try_execute_devirtualized_impl_call(DeviModeSequence<Mode...>(), + std::make_index_sequence<SourceTypesNum>()); + } + else { + /* Index of the parameter that is checked in the current recursive step. */ + constexpr size_t I = sizeof...(Mode); + /* Non-devirtualized parameter type. */ + using SourceType = type_at_index<I>; + /* A bit flag indicating what devirtualizations are allowed in this step. */ + constexpr DeviMode allowed_modes = DeviModeSequence<AllowedModes...>::template at_index<I>(); + + /* Handle #VArray types. */ + if constexpr (is_VArray_v<SourceType>) { + /* The actual virtual array, used for dynamic dispatch at run-time. */ + const SourceType &varray = *std::get<I>(sources_); + /* Check if the virtual array is a single value. */ + if constexpr ((allowed_modes & DeviMode::Single) != DeviMode::None) { + if (varray.is_single()) { + this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Single>(), + DeviModeSequence<AllowedModes...>()); + } + } + /* Check if the virtual array is a span. */ + if constexpr ((allowed_modes & DeviMode::Span) != DeviMode::None) { + if (varray.is_span()) { + this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Span>(), + DeviModeSequence<AllowedModes...>()); + } + } + /* Check if it is ok if the virtual array is not devirtualized. */ + if constexpr ((allowed_modes & DeviMode::Keep) != DeviMode::None) { + this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Keep>(), + DeviModeSequence<AllowedModes...>()); + } + } + + /* Handle #IndexMask. */ + else if constexpr (std::is_same_v<IndexMask, SourceType>) { + /* Check if the mask is actually a contiguous range. */ + if constexpr ((allowed_modes & DeviMode::Range) != DeviMode::None) { + /* The actual mask used for dynamic dispatch at run-time. */ + const IndexMask &mask = *std::get<I>(sources_); + if (mask.is_range()) { + this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Range>(), + DeviModeSequence<AllowedModes...>()); + } + } + /* Check if mask is also allowed to stay a span. */ + if constexpr ((allowed_modes & DeviMode::Span) != DeviMode::None) { + this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Span>(), + DeviModeSequence<AllowedModes...>()); + } + } + + /* Handle unknown types. */ + else { + this->try_execute_devirtualized_impl(DeviModeSequence<Mode..., DeviMode::Keep>(), + DeviModeSequence<AllowedModes...>()); + } + } + } + + /** + * Actually call the function with devirtualized parameters. + */ + template<DeviMode... Mode, size_t... I> + void try_execute_devirtualized_impl_call(DeviModeSequence<Mode...> /* modes */, + std::index_sequence<I...> /* indices */) + { + + fn_(this->get_devirtualized_parameter<I, Mode>()...); + executed_ = true; + } + + /** + * Return the I-th parameter devirtualized using the passed in #DeviMode. This has different + * return types based on the template parameters. + * + * \note It is expected that the caller already knows that the parameter can be devirtualized + * with the given mode. + */ + template<size_t I, DeviMode Mode> decltype(auto) get_devirtualized_parameter() + { + using SourceType = type_at_index<I>; + static_assert(Mode != DeviMode::None); + if constexpr (Mode == DeviMode::Keep) { + /* Don't change the original parameter at all. */ + return *std::get<I>(sources_); + } + if constexpr (is_VArray_v<SourceType>) { + const SourceType &varray = *std::get<I>(sources_); + if constexpr (Mode == DeviMode::Single) { + /* Devirtualize virtual array as single value. */ + return SingleAsSpan(varray); + } + else if constexpr (Mode == DeviMode::Span) { + /* Devirtualize virtual array as span. */ + return varray.get_internal_span(); + } + } + else if constexpr (std::is_same_v<IndexMask, SourceType>) { + const IndexMask &mask = *std::get<I>(sources_); + if constexpr (ELEM(Mode, DeviMode::Span)) { + /* Don't devirtualize mask, it's still a span. */ + return mask; + } + else if constexpr (Mode == DeviMode::Range) { + /* Devirtualize the mask as range. */ + return mask.as_range(); + } + } + } +}; + +} // namespace blender::devirtualize_parameters + +namespace blender { + +/** + * Generate multiple versions of the given function optimized for different virtual arrays. + * One has to be careful with nesting multiple devirtualizations, because that results in an + * exponential number of function instantiations (increasing compile time and binary size). + * + * Generally, this function should only be used when the virtual method call overhead to get an + * element from a virtual array is significant. + */ +template<typename T, typename Func> +inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool enable = true) +{ + using namespace devirtualize_parameters; + if (enable) { + Devirtualizer<decltype(func), VArray<T>> devirtualizer(func, &varray); + constexpr DeviMode devi_mode = DeviMode::Single | DeviMode::Span; + devirtualizer.try_execute_devirtualized(DeviModeSequence<devi_mode>()); + if (devirtualizer.executed()) { + return; + } + } + func(varray); +} + +/** + * Same as `devirtualize_varray`, but devirtualizes two virtual arrays at the same time. + * This is better than nesting two calls to `devirtualize_varray`, because it instantiates fewer + * cases. + */ +template<typename T1, typename T2, typename Func> +inline void devirtualize_varray2(const VArray<T1> &varray1, + const VArray<T2> &varray2, + const Func &func, + bool enable = true) +{ + using namespace devirtualize_parameters; + if (enable) { + Devirtualizer<decltype(func), VArray<T1>, VArray<T2>> devirtualizer(func, &varray1, &varray2); + constexpr DeviMode devi_mode = DeviMode::Single | DeviMode::Span; + devirtualizer.try_execute_devirtualized(DeviModeSequence<devi_mode, devi_mode>()); + if (devirtualizer.executed()) { + return; + } + } + func(varray1, varray2); +} + +} // namespace blender diff --git a/source/blender/blenlib/BLI_parameter_pack_utils.hh b/source/blender/blenlib/BLI_parameter_pack_utils.hh new file mode 100644 index 00000000000..d1ef7bcbc65 --- /dev/null +++ b/source/blender/blenlib/BLI_parameter_pack_utils.hh @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * C++ has a feature called "parameter packs" which allow building variadic templates. + * This file has some utilities to work with such parameter packs. + */ + +#include <tuple> +#include <type_traits> + +#include "BLI_utildefines.h" + +namespace blender { + +/** + * A type that encodes a specific value. + */ +template<typename T, T Element> struct TypeForValue { + static constexpr T value = Element; +}; + +/** + * A type that encodes a list of values of the same type. + * This is similar to #std::integer_sequence, but a bit more general. It's main purpose it to also + * support enums instead of just ints. + */ +template<typename T, T... Elements> struct ValueSequence { + /** + * Get the number of elements in the sequence. + */ + static constexpr size_t size() noexcept + { + return sizeof...(Elements); + } + + /** + * Get the element at a specific index. + */ + template<size_t I> static constexpr T at_index() + { + static_assert(I < sizeof...(Elements)); + return std::tuple_element_t<I, std::tuple<TypeForValue<T, Elements>...>>::value; + } + + /** + * Return true if the element is in the sequence. + */ + template<T Element> static constexpr bool contains() + { + return ((Element == Elements) || ...); + } +}; + +/** + * A type that encodes a list of types. + * #std::tuple can also encode a list of types, but has a much more complex implementation. + */ +template<typename... T> struct TypeSequence { + /** + * Get the number of types in the sequence. + */ + static constexpr size_t size() noexcept + { + return sizeof...(T); + } + + /** + * Get the type at a specific index. + */ + template<size_t I> using at_index = std::tuple_element_t<I, std::tuple<T...>>; +}; + +namespace detail { + +template<typename T, T Value, size_t... I> +inline ValueSequence<T, ((I == 0) ? Value : Value)...> make_value_sequence_impl( + std::index_sequence<I...> /* indices */) +{ + return {}; +} + +template<typename T, T Value1, T Value2, size_t... Value1Indices, size_t... I> +inline ValueSequence<T, + (ValueSequence<size_t, Value1Indices...>::template contains<I>() ? Value1 : + Value2)...> + make_two_value_sequence_impl(ValueSequence<size_t, Value1Indices...> /* value1_indices */, + std::index_sequence<I...> /* indices */) +{ + return {}; +}; + +} // namespace detail + +/** + * Utility to create a #ValueSequence that has the same value at every index. + */ +template<typename T, T Value, size_t Size> +using make_value_sequence = decltype(detail::make_value_sequence_impl<T, Value>( + std::make_index_sequence<Size>())); + +/** + * Utility to create a #ValueSequence that contains two different values. The indices of where the + * first value should be used are passed in. + */ +template<typename T, T Value1, T Value2, size_t Size, size_t... Value1Indices> +using make_two_value_sequence = decltype(detail::make_two_value_sequence_impl<T, Value1, Value2>( + ValueSequence<size_t, Value1Indices...>(), std::make_index_sequence<Size>())); + +namespace parameter_pack_utils_static_tests { +enum class MyEnum { A, B }; +static_assert(std::is_same_v<make_value_sequence<MyEnum, MyEnum::A, 3>, + ValueSequence<MyEnum, MyEnum::A, MyEnum::A, MyEnum::A>>); +static_assert( + std::is_same_v<make_two_value_sequence<MyEnum, MyEnum::A, MyEnum::B, 5, 1, 2>, + ValueSequence<MyEnum, MyEnum::B, MyEnum::A, MyEnum::A, MyEnum::B, MyEnum::B>>); +} // namespace parameter_pack_utils_static_tests + +} // namespace blender diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 41a73b45853..7aa221f62ce 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -1089,6 +1089,12 @@ template<typename T> class VMutableArray : public VArrayCommon<T> { } }; +template<typename T> static constexpr bool is_VArray_v = false; +template<typename T> static constexpr bool is_VArray_v<VArray<T>> = true; + +template<typename T> static constexpr bool is_VMutableArray_v = false; +template<typename T> static constexpr bool is_VMutableArray_v<VMutableArray<T>> = true; + /** * In many cases a virtual array is a span internally. In those cases, access to individual could * be much more efficient than calling a virtual method. When the underlying virtual array is not a @@ -1207,69 +1213,4 @@ template<typename T> class SingleAsSpan { } }; -/** - * Generate multiple versions of the given function optimized for different virtual arrays. - * One has to be careful with nesting multiple devirtualizations, because that results in an - * exponential number of function instantiations (increasing compile time and binary size). - * - * Generally, this function should only be used when the virtual method call overhead to get an - * element from a virtual array is significant. - */ -template<typename T, typename Func> -inline void devirtualize_varray(const VArray<T> &varray, const Func &func, bool enable = true) -{ - /* Support disabling the devirtualization to simplify benchmarking. */ - if (enable) { - if (varray.is_single()) { - func(SingleAsSpan<T>(varray)); - return; - } - if (varray.is_span()) { - func(varray.get_internal_span()); - return; - } - } - func(varray); -} - -/** - * Same as `devirtualize_varray`, but devirtualizes two virtual arrays at the same time. - * This is better than nesting two calls to `devirtualize_varray`, because it instantiates fewer - * cases. - */ -template<typename T1, typename T2, typename Func> -inline void devirtualize_varray2(const VArray<T1> &varray1, - const VArray<T2> &varray2, - const Func &func, - bool enable = true) -{ - /* Support disabling the devirtualization to simplify benchmarking. */ - if (enable) { - const bool is_span1 = varray1.is_span(); - const bool is_span2 = varray2.is_span(); - const bool is_single1 = varray1.is_single(); - const bool is_single2 = varray2.is_single(); - if (is_span1 && is_span2) { - func(varray1.get_internal_span(), varray2.get_internal_span()); - return; - } - if (is_span1 && is_single2) { - func(varray1.get_internal_span(), SingleAsSpan(varray2)); - return; - } - if (is_single1 && is_span2) { - func(SingleAsSpan(varray1), varray2.get_internal_span()); - return; - } - if (is_single1 && is_single2) { - func(SingleAsSpan(varray1), SingleAsSpan(varray2)); - return; - } - } - /* This fallback is used even when one of the inputs could be optimized. It's probably not worth - * it to optimize just one of the inputs, because then the compiler still has to call into - * unknown code, which inhibits many compiler optimizations. */ - func(varray1, varray2); -} - } // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 446a027b03b..e0f28522d6c 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -180,6 +180,7 @@ set(SRC BLI_cpp_type.hh BLI_cpp_type_make.hh BLI_delaunay_2d.h + BLI_devirtualize_parameters.hh BLI_dial_2d.h BLI_disjoint_set.hh BLI_dlrbTree.h @@ -274,6 +275,7 @@ set(SRC BLI_noise.h BLI_noise.hh BLI_path_util.h + BLI_parameter_pack_utils.hh BLI_polyfill_2d.h BLI_polyfill_2d_beautify.h BLI_probing_strategies.hh diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index 088a906ce02..b3865bc3cb7 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -10,256 +10,489 @@ #include <functional> +#include "BLI_devirtualize_parameters.hh" + #include "FN_multi_function.hh" namespace blender::fn { +namespace devi = devirtualize_parameters; + /** - * Generates a multi-function with the following parameters: - * 1. single input (SI) of type In1 - * 2. single output (SO) of type Out1 - * - * This example creates a function that adds 10 to the incoming values: - * `CustomMF_SI_SO<int, int> fn("add 10", [](int value) { return value + 10; });` + * 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. */ -template<typename In1, typename Out1> class CustomMF_SI_SO : public MultiFunction { - private: - using FunctionT = std::function<void(IndexMask, const VArray<In1> &, MutableSpan<Out1>)>; - FunctionT function_; - MFSignature signature_; +namespace CustomMF_presets { + +/** 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, +}; - public: - CustomMF_SI_SO(const char *name, FunctionT function) : function_(std::move(function)) +/** + * 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; + + template<typename Fn, typename... ParamTypes> + void try_devirtualize(devi::Devirtualizer<Fn, ParamTypes...> &devirtualizer) { - MFSignatureBuilder signature{name}; - signature.single_input<In1>("In1"); - signature.single_output<Out1>("Out1"); - signature_ = signature.build(); - this->set_signature(&signature_); + using devi::DeviMode; + devirtualizer.try_execute_devirtualized( + make_value_sequence<DeviMode, + DeviMode::Span | DeviMode::Single | DeviMode::Range, + sizeof...(ParamTypes)>()); } +}; - template<typename ElementFuncT> - CustomMF_SI_SO(const char *name, ElementFuncT element_fn) - : CustomMF_SI_SO(name, CustomMF_SI_SO::create_function(element_fn)) +/** + * A slighly weaker variant of #AllSpanOrSingle. It generates less code, because it assumes that + * 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; + + template<typename Fn, typename... ParamTypes> + void try_devirtualize(devi::Devirtualizer<Fn, ParamTypes...> &devirtualizer) { + using devi::DeviMode; + devirtualizer.try_execute_devirtualized( + make_two_value_sequence<DeviMode, + DeviMode::Span | DeviMode::Single | DeviMode::Range, + DeviMode::Single, + sizeof...(ParamTypes), + 0, + (Indices + 1)...>()); } +}; - template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn) - { - return [=](IndexMask mask, const VArray<In1> &in1, MutableSpan<Out1> out1) { - if (in1.is_single()) { - /* Only evaluate the function once when the input is a single value. */ - const In1 in1_single = in1.get_internal_single(); - const Out1 out1_single = element_fn(in1_single); - out1.fill_indices(mask, out1_single); - return; - } +} // namespace CustomMF_presets + +namespace detail { - if (in1.is_span()) { - const Span<In1> in1_span = in1.get_internal_span(); - mask.to_best_mask_type( - [&](auto mask) { execute_SI_SO(element_fn, mask, in1_span, out1.data()); }); - return; +/** + * 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> +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) +{ + for (const int64_t i : mask) { + element_fn([&]() -> decltype(auto) { + using ParamTag = typename TypeSequence<ParamTags...>::template at_index<I>; + if constexpr (ParamTag::category == MFParamCategory::SingleInput) { + /* For inputs, pass the value (or a reference to it) to the function. */ + return args[i]; } + else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) { + /* For outputs, pass a pointer to the function. This is done instead of passing a + * reference, because the pointer points to uninitialized memory. */ + return &args[i]; + } + }()...); + } +} - /* The input is an unknown virtual array type. To avoid virtual function call overhead for - * every element, elements are retrieved and processed in chunks. */ +} // namespace detail - static constexpr int64_t MaxChunkSize = 32; - TypedBuffer<In1, MaxChunkSize> in1_buffer_owner; - MutableSpan<In1> in1_buffer{in1_buffer_owner.ptr(), MaxChunkSize}; +namespace materialize_detail { - const int64_t mask_size = mask.size(); - for (int64_t chunk_start = 0; chunk_start < mask_size; chunk_start += MaxChunkSize) { - const int64_t chunk_size = std::min(mask_size - chunk_start, MaxChunkSize); - const IndexMask sliced_mask = mask.slice(chunk_start, chunk_size); +enum class ArgMode { + Unknown, + Single, + Span, + Materialized, +}; - /* Load input from the virtual array. */ - MutableSpan<In1> in1_chunk = in1_buffer.take_front(chunk_size); - in1.materialize_compressed_to_uninitialized(sliced_mask, in1_chunk); +template<typename ParamTag> struct ArgInfo { + ArgMode mode = ArgMode::Unknown; + Span<typename ParamTag::base_type> internal_span; +}; - if (sliced_mask.is_range()) { - execute_SI_SO( - element_fn, IndexRange(chunk_size), in1_chunk, out1.data() + sliced_mask[0]); - } - else { - execute_SI_SO_compressed(element_fn, sliced_mask, in1_chunk, out1.data()); - } - destruct_n(in1_chunk.data(), chunk_size); +/** + * Similar to #execute_array but accepts two mask inputs, one for inputs and one for outputs. + */ +template<typename... ParamTags, typename ElementFn, typename... Chunks> +void execute_materialized_impl(TypeSequence<ParamTags...> /* param_tags */, + const ElementFn element_fn, + const IndexRange in_mask, + const IndexMask out_mask, + Chunks &&__restrict... chunks) +{ + BLI_assert(in_mask.size() == out_mask.size()); + for (const int64_t i : IndexRange(in_mask.size())) { + const int64_t in_i = in_mask[i]; + const int64_t out_i = out_mask[i]; + element_fn([&]() -> decltype(auto) { + using ParamTag = ParamTags; + if constexpr (ParamTag::category == MFParamCategory::SingleInput) { + return chunks[in_i]; } - }; - } - - template<typename ElementFuncT, typename MaskT, typename In1Array> - BLI_NOINLINE static void execute_SI_SO(const ElementFuncT &element_fn, - MaskT mask, - const In1Array &in1, - Out1 *__restrict r_out) - { - for (const int64_t i : mask) { - new (r_out + i) Out1(element_fn(in1[i])); - } + else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) { + /* For outputs, a pointer is passed, because the memory is uninitialized. */ + return &chunks[out_i]; + } + }()...); } +} - /** Expects the input array to be "compressed", i.e. there are no gaps between the elements. */ - template<typename ElementFuncT, typename MaskT, typename In1Array> - BLI_NOINLINE static void execute_SI_SO_compressed(const ElementFuncT &element_fn, - MaskT mask, - const In1Array &in1, - Out1 *__restrict r_out) - { - for (const int64_t i : IndexRange(mask.size())) { - new (r_out + mask[i]) Out1(element_fn(in1[i])); - } +/** + * 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. + */ +template<typename... ParamTags, size_t... I, typename ElementFn, typename... Args> +void execute_materialized(TypeSequence<ParamTags...> /* param_tags */, + std::index_sequence<I...> /* indices */, + const ElementFn element_fn, + const IndexMask mask, + Args &&...args) +{ + + /* 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. */ + static constexpr int64_t MaxChunkSize = 32; + const int64_t mask_size = mask.size(); + const int64_t buffer_size = std::min(mask_size, MaxChunkSize); + + /* Local buffers that are used to temporarily store values retrieved from virtual arrays. */ + std::tuple<TypedBuffer<typename ParamTags::base_type, MaxChunkSize>...> buffers_owner; + + /* A span for each parameter which is either empty or points to memory in #buffers_owner. */ + std::tuple<MutableSpan<typename ParamTags::base_type>...> buffers; + + /* Information about every parameter. */ + std::tuple<ArgInfo<ParamTags>...> args_info; + + ( + /* Setup information for all parameters. */ + [&] { + using ParamTag = ParamTags; + using T = typename ParamTag::base_type; + ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); + if constexpr (ParamTag::category == MFParamCategory::SingleInput) { + VArray<T> &varray = *args; + if (varray.is_single()) { + /* 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. */ + MutableSpan<T> in_chunk{std::get<I>(buffers_owner).ptr(), buffer_size}; + const T in_single = varray.get_internal_single(); + uninitialized_fill_n(in_chunk.data(), in_chunk.size(), in_single); + std::get<I>(buffers) = in_chunk; + arg_info.mode = ArgMode::Single; + } + else if (varray.is_span()) { + /* Remember the span so that it doesn't have to be retrieved in every iteration. */ + arg_info.internal_span = varray.get_internal_span(); + } + } + }(), + ...); + + /* Outer loop over all chunks. */ + for (int64_t chunk_start = 0; chunk_start < mask_size; chunk_start += MaxChunkSize) { + const IndexMask sliced_mask = mask.slice(chunk_start, MaxChunkSize); + const int64_t chunk_size = sliced_mask.size(); + const bool sliced_mask_is_range = sliced_mask.is_range(); + + execute_materialized_impl( + TypeSequence<ParamTags...>(), + element_fn, + /* Inputs are "compressed" into contiguous arrays without gaps. */ + IndexRange(chunk_size), + /* Outputs are written directly into the correct place in the output arrays. */ + sliced_mask, + /* Prepare every parameter for this chunk. */ + [&] { + using ParamTag = ParamTags; + using T = typename ParamTag::base_type; + ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); + if constexpr (ParamTag::category == MFParamCategory::SingleInput) { + if (arg_info.mode == ArgMode::Single) { + /* The single value has been filled into a buffer already reused for every chunk. */ + return Span<T>(std::get<I>(buffers)); + } + else { + const VArray<T> &varray = *args; + if (sliced_mask_is_range) { + if (!arg_info.internal_span.is_empty()) { + /* In this case we can just use an existing span instead of "compressing" it into + * a new temporary buffer. */ + const IndexRange sliced_mask_range = sliced_mask.as_range(); + arg_info.mode = ArgMode::Span; + return arg_info.internal_span.slice(sliced_mask_range); + } + } + /* 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. */ + MutableSpan<T> in_chunk{std::get<I>(buffers_owner).ptr(), chunk_size}; + varray.materialize_compressed_to_uninitialized(sliced_mask, in_chunk); + /* Remember that this parameter has been materialized, so that the values are + * destructed properly when the chunk is done. */ + arg_info.mode = ArgMode::Materialized; + return Span<T>(in_chunk); + } + } + else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) { + /* For outputs, just pass a pointer. This is important so that `__restrict` works. */ + return args->data(); + } + }()...); + + ( + /* Destruct values that have been materialized before. */ + [&] { + using ParamTag = ParamTags; + using T = typename ParamTag::base_type; + ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); + if constexpr (ParamTag::category == MFParamCategory::SingleInput) { + if (arg_info.mode == ArgMode::Materialized) { + T *in_chunk = std::get<I>(buffers_owner).ptr(); + destruct_n(in_chunk, chunk_size); + } + } + }(), + ...); } - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override - { - const VArray<In1> &in1 = params.readonly_single_input<In1>(0); - MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(1); - function_(mask, in1, out1); - } -}; + ( + /* Destruct buffers for single value inputs. */ + [&] { + using ParamTag = ParamTags; + using T = typename ParamTag::base_type; + ArgInfo<ParamTags> &arg_info = std::get<I>(args_info); + if constexpr (ParamTag::category == MFParamCategory::SingleInput) { + if (arg_info.mode == ArgMode::Single) { + MutableSpan<T> in_chunk = std::get<I>(buffers); + destruct_n(in_chunk.data(), in_chunk.size()); + } + } + }(), + ...); +} +} // namespace materialize_detail -/** - * Generates a multi-function with the following parameters: - * 1. single input (SI) of type In1 - * 2. single input (SI) of type In2 - * 3. single output (SO) of type Out1 - */ -template<typename In1, typename In2, typename Out1> -class CustomMF_SI_SI_SO : public MultiFunction { +template<typename... ParamTags> class CustomMF : public MultiFunction { private: - using FunctionT = - std::function<void(IndexMask, const VArray<In1> &, const VArray<In2> &, MutableSpan<Out1>)>; - FunctionT function_; + std::function<void(IndexMask mask, MFParams params)> fn_; MFSignature signature_; + using TagsSequence = TypeSequence<ParamTags...>; + public: - CustomMF_SI_SI_SO(const char *name, FunctionT function) : function_(std::move(function)) + template<typename ElementFn, typename ExecPreset = CustomMF_presets::Materialized> + CustomMF(const char *name, + ElementFn element_fn, + ExecPreset exec_preset = CustomMF_presets::Materialized()) { MFSignatureBuilder signature{name}; - signature.single_input<In1>("In1"); - signature.single_input<In2>("In2"); - signature.single_output<Out1>("Out1"); + add_signature_parameters(signature, std::make_index_sequence<TagsSequence::size()>()); signature_ = signature.build(); this->set_signature(&signature_); - } - template<typename ElementFuncT> - CustomMF_SI_SI_SO(const char *name, ElementFuncT element_fn) - : CustomMF_SI_SI_SO(name, CustomMF_SI_SI_SO::create_function(element_fn)) - { + fn_ = [element_fn, exec_preset](IndexMask mask, MFParams params) { + execute( + element_fn, exec_preset, mask, params, std::make_index_sequence<TagsSequence::size()>()); + }; } - template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn) + template<typename ElementFn, typename ExecPreset, size_t... I> + static void execute(ElementFn element_fn, + ExecPreset exec_preset, + IndexMask mask, + MFParams params, + std::index_sequence<I...> /* indices */) { - return [=](IndexMask mask, - const VArray<In1> &in1, - const VArray<In2> &in2, - MutableSpan<Out1> out1) { - /* Devirtualization results in a 2-3x speedup for some simple functions. */ - devirtualize_varray2(in1, in2, [&](const auto &in1, const auto &in2) { - mask.to_best_mask_type( - [&](const auto &mask) { execute_SI_SI_SO(element_fn, mask, in1, in2, out1.data()); }); - }); + std::tuple<typename ParamTags::array_type...> retrieved_params; + ( + /* Get all parameters from #params and store them in #retrieved_params. */ + [&]() { + using ParamTag = typename TagsSequence::template at_index<I>; + using T = typename ParamTag::base_type; + + if constexpr (ParamTag::category == MFParamCategory::SingleInput) { + std::get<I>(retrieved_params) = params.readonly_single_input<T>(I); + } + if constexpr (ParamTag::category == MFParamCategory::SingleOutput) { + std::get<I>(retrieved_params) = params.uninitialized_single_output<T>(I); + } + }(), + ...); + + auto array_executor = [&](auto &&...args) { + detail::execute_array(TagsSequence(), + std::make_index_sequence<TagsSequence::size()>(), + element_fn, + std::forward<decltype(args)>(args)...); }; + + /* First try devirtualized execution, since this is the most efficient. */ + bool executed_devirtualized = false; + if constexpr (ExecPreset::use_devirtualization) { + devi::Devirtualizer<decltype(array_executor), IndexMask, typename ParamTags::array_type...> + devirtualizer{ + array_executor, &mask, [&] { return &std::get<I>(retrieved_params); }()...}; + exec_preset.try_devirtualize(devirtualizer); + executed_devirtualized = devirtualizer.executed(); + } + + /* If devirtualized execution was disabled or not possible, use a fallback method which is + * slower but always works. */ + if (!executed_devirtualized) { + if constexpr (ExecPreset::fallback_mode == CustomMF_presets::FallbackMode::Materialized) { + materialize_detail::execute_materialized( + TypeSequence<ParamTags...>(), std::index_sequence<I...>(), element_fn, mask, [&] { + return &std::get<I>(retrieved_params); + }()...); + } + else { + detail::execute_array(TagsSequence(), + std::make_index_sequence<TagsSequence::size()>(), + element_fn, + mask, + std::get<I>(retrieved_params)...); + } + } } - template<typename ElementFuncT, typename MaskT, typename In1Array, typename In2Array> - BLI_NOINLINE static void execute_SI_SI_SO(const ElementFuncT &element_fn, - MaskT mask, - const In1Array &in1, - const In2Array &in2, - Out1 *__restrict r_out) + template<size_t... I> + static void add_signature_parameters(MFSignatureBuilder &signature, + std::index_sequence<I...> /* indices */) { - for (const int64_t i : mask) { - new (r_out + i) Out1(element_fn(in1[i], in2[i])); - } + ( + /* Loop over all parameter types and add an entry for each in the signature. */ + [&] { + using ParamTag = typename TagsSequence::template at_index<I>; + signature.add(ParamTag(), ""); + }(), + ...); } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { - const VArray<In1> &in1 = params.readonly_single_input<In1>(0); - const VArray<In2> &in2 = params.readonly_single_input<In2>(1); - MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(2); - function_(mask, in1, in2, out1); + fn_(mask, params); } }; /** * Generates a multi-function with the following parameters: * 1. single input (SI) of type In1 - * 2. single input (SI) of type In2 - * 3. single input (SI) of type In3 - * 4. single output (SO) of type Out1 + * 2. single output (SO) of type Out1 + * + * This example creates a function that adds 10 to the incoming values: + * `CustomMF_SI_SO<int, int> fn("add 10", [](int value) { return value + 10; });` */ -template<typename In1, typename In2, typename In3, typename Out1> -class CustomMF_SI_SI_SI_SO : public MultiFunction { - private: - using FunctionT = std::function<void(IndexMask, - const VArray<In1> &, - const VArray<In2> &, - const VArray<In3> &, - MutableSpan<Out1>)>; - FunctionT function_; - MFSignature signature_; - +template<typename In1, typename Out1> +class CustomMF_SI_SO : public CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>, + MFParamTag<MFParamCategory::SingleOutput, Out1>> { public: - CustomMF_SI_SI_SI_SO(const char *name, FunctionT function) : function_(std::move(function)) - { - MFSignatureBuilder signature{name}; - signature.single_input<In1>("In1"); - signature.single_input<In2>("In2"); - signature.single_input<In3>("In3"); - signature.single_output<Out1>("Out1"); - signature_ = signature.build(); - this->set_signature(&signature_); - } - - template<typename ElementFuncT> - CustomMF_SI_SI_SI_SO(const char *name, ElementFuncT element_fn) - : CustomMF_SI_SI_SI_SO(name, CustomMF_SI_SI_SI_SO::create_function(element_fn)) + template<typename ElementFn, typename ExecPreset = CustomMF_presets::Materialized> + CustomMF_SI_SO(const char *name, + ElementFn element_fn, + ExecPreset exec_preset = CustomMF_presets::Materialized()) + : CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>, + MFParamTag<MFParamCategory::SingleOutput, Out1>>( + name, + [element_fn](const In1 &in1, Out1 *out1) { new (out1) Out1(element_fn(in1)); }, + exec_preset) { } +}; - template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn) - { - return [=](IndexMask mask, - const VArray<In1> &in1, - const VArray<In2> &in2, - const VArray<In3> &in3, - MutableSpan<Out1> out1) { - /* Virtual arrays are not devirtualized yet, to avoid generating lots of code without further - * consideration. */ - execute_SI_SI_SI_SO(element_fn, mask, in1, in2, in3, out1.data()); - }; - } - - template<typename ElementFuncT, - typename MaskT, - typename In1Array, - typename In2Array, - typename In3Array> - BLI_NOINLINE static void execute_SI_SI_SI_SO(const ElementFuncT &element_fn, - MaskT mask, - const In1Array &in1, - const In2Array &in2, - const In3Array &in3, - Out1 *__restrict r_out) +/** + * Generates a multi-function with the following parameters: + * 1. single input (SI) of type In1 + * 2. single input (SI) of type In2 + * 3. single output (SO) of type Out1 + */ +template<typename In1, typename In2, typename Out1> +class CustomMF_SI_SI_SO : public CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>, + MFParamTag<MFParamCategory::SingleInput, In2>, + MFParamTag<MFParamCategory::SingleOutput, Out1>> { + public: + template<typename ElementFn, typename ExecPreset = CustomMF_presets::Materialized> + CustomMF_SI_SI_SO(const char *name, + ElementFn element_fn, + ExecPreset exec_preset = CustomMF_presets::Materialized()) + : CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>, + MFParamTag<MFParamCategory::SingleInput, In2>, + MFParamTag<MFParamCategory::SingleOutput, Out1>>( + name, + [element_fn](const In1 &in1, const In2 &in2, Out1 *out1) { + new (out1) Out1(element_fn(in1, in2)); + }, + exec_preset) { - for (const int64_t i : mask) { - new (r_out + i) Out1(element_fn(in1[i], in2[i], in3[i])); - } } +}; - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override +/** + * Generates a multi-function with the following parameters: + * 1. single input (SI) of type In1 + * 2. single input (SI) of type In2 + * 3. single input (SI) of type In3 + * 4. single output (SO) of type Out1 + */ +template<typename In1, typename In2, typename In3, typename Out1> +class CustomMF_SI_SI_SI_SO : public CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>, + MFParamTag<MFParamCategory::SingleInput, In2>, + MFParamTag<MFParamCategory::SingleInput, In3>, + MFParamTag<MFParamCategory::SingleOutput, Out1>> { + public: + template<typename ElementFn, typename ExecPreset = CustomMF_presets::Materialized> + CustomMF_SI_SI_SI_SO(const char *name, + ElementFn element_fn, + ExecPreset exec_preset = CustomMF_presets::Materialized()) + : CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>, + MFParamTag<MFParamCategory::SingleInput, In2>, + MFParamTag<MFParamCategory::SingleInput, In3>, + MFParamTag<MFParamCategory::SingleOutput, Out1>>( + name, + [element_fn](const In1 &in1, const In2 &in2, const In3 &in3, Out1 *out1) { + new (out1) Out1(element_fn(in1, in2, in3)); + }, + exec_preset) { - const VArray<In1> &in1 = params.readonly_single_input<In1>(0); - const VArray<In2> &in2 = params.readonly_single_input<In2>(1); - const VArray<In3> &in3 = params.readonly_single_input<In3>(2); - MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(3); - function_(mask, in1, in2, in3, out1); } }; @@ -272,77 +505,28 @@ class CustomMF_SI_SI_SI_SO : public MultiFunction { * 5. single output (SO) of type Out1 */ template<typename In1, typename In2, typename In3, typename In4, typename Out1> -class CustomMF_SI_SI_SI_SI_SO : public MultiFunction { - private: - using FunctionT = std::function<void(IndexMask, - const VArray<In1> &, - const VArray<In2> &, - const VArray<In3> &, - const VArray<In4> &, - MutableSpan<Out1>)>; - FunctionT function_; - MFSignature signature_; - +class CustomMF_SI_SI_SI_SI_SO : public CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>, + MFParamTag<MFParamCategory::SingleInput, In2>, + MFParamTag<MFParamCategory::SingleInput, In3>, + MFParamTag<MFParamCategory::SingleInput, In4>, + MFParamTag<MFParamCategory::SingleOutput, Out1>> { public: - CustomMF_SI_SI_SI_SI_SO(const char *name, FunctionT function) : function_(std::move(function)) - { - MFSignatureBuilder signature{name}; - signature.single_input<In1>("In1"); - signature.single_input<In2>("In2"); - signature.single_input<In3>("In3"); - signature.single_input<In4>("In4"); - signature.single_output<Out1>("Out1"); - signature_ = signature.build(); - this->set_signature(&signature_); - } - - template<typename ElementFuncT> - CustomMF_SI_SI_SI_SI_SO(const char *name, ElementFuncT element_fn) - : CustomMF_SI_SI_SI_SI_SO(name, CustomMF_SI_SI_SI_SI_SO::create_function(element_fn)) - { - } - - template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn) - { - return [=](IndexMask mask, - const VArray<In1> &in1, - const VArray<In2> &in2, - const VArray<In3> &in3, - const VArray<In4> &in4, - MutableSpan<Out1> out1) { - /* Virtual arrays are not devirtualized yet, to avoid generating lots of code without further - * consideration. */ - execute_SI_SI_SI_SI_SO(element_fn, mask, in1, in2, in3, in4, out1.data()); - }; - } - - template<typename ElementFuncT, - typename MaskT, - typename In1Array, - typename In2Array, - typename In3Array, - typename In4Array> - BLI_NOINLINE static void execute_SI_SI_SI_SI_SO(const ElementFuncT &element_fn, - MaskT mask, - const In1Array &in1, - const In2Array &in2, - const In3Array &in3, - const In4Array &in4, - Out1 *__restrict r_out) - { - for (const int64_t i : mask) { - new (r_out + i) Out1(element_fn(in1[i], in2[i], in3[i], in4[i])); - } - } - - void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + template<typename ElementFn, typename ExecPreset = CustomMF_presets::Materialized> + CustomMF_SI_SI_SI_SI_SO(const char *name, + ElementFn element_fn, + ExecPreset exec_preset = CustomMF_presets::Materialized()) + : CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>, + MFParamTag<MFParamCategory::SingleInput, In2>, + MFParamTag<MFParamCategory::SingleInput, In3>, + MFParamTag<MFParamCategory::SingleInput, In4>, + MFParamTag<MFParamCategory::SingleOutput, Out1>>( + name, + [element_fn]( + const In1 &in1, const In2 &in2, const In3 &in3, const In4 &in4, Out1 *out1) { + new (out1) Out1(element_fn(in1, in2, in3, in4)); + }, + exec_preset) { - const VArray<In1> &in1 = params.readonly_single_input<In1>(0); - const VArray<In2> &in2 = params.readonly_single_input<In2>(1); - const VArray<In3> &in3 = params.readonly_single_input<In3>(2); - const VArray<In4> &in4 = params.readonly_single_input<In4>(3); - MutableSpan<Out1> out1 = params.uninitialized_single_output<Out1>(4); - function_(mask, in1, in2, in3, in4, out1); } }; diff --git a/source/blender/functions/FN_multi_function_param_type.hh b/source/blender/functions/FN_multi_function_param_type.hh index 69ac4c2382e..8a7284624d1 100644 --- a/source/blender/functions/FN_multi_function_param_type.hh +++ b/source/blender/functions/FN_multi_function_param_type.hh @@ -22,6 +22,23 @@ namespace blender::fn { +enum class MFParamCategory { + SingleInput, + VectorInput, + SingleOutput, + VectorOutput, + SingleMutable, + VectorMutable, +}; + +template<MFParamCategory Category, typename T> struct MFParamTag { + static constexpr MFParamCategory category = Category; + using base_type = T; + /* TODO: Doesn't support all categories yet, this can be generalized when necessary. */ + using array_type = + std::conditional_t<Category == MFParamCategory::SingleInput, VArray<T>, MutableSpan<T>>; +}; + class MFParamType { public: enum InterfaceType { @@ -30,15 +47,6 @@ class MFParamType { Mutable, }; - enum Category { - SingleInput, - VectorInput, - SingleOutput, - VectorOutput, - SingleMutable, - VectorMutable, - }; - private: InterfaceType interface_type_; MFDataType data_type_; @@ -89,34 +97,34 @@ class MFParamType { return interface_type_; } - Category category() const + MFParamCategory category() const { switch (data_type_.category()) { case MFDataType::Single: { switch (interface_type_) { case Input: - return SingleInput; + return MFParamCategory::SingleInput; case Output: - return SingleOutput; + return MFParamCategory::SingleOutput; case Mutable: - return SingleMutable; + return MFParamCategory::SingleMutable; } break; } case MFDataType::Vector: { switch (interface_type_) { case Input: - return VectorInput; + return MFParamCategory::VectorInput; case Output: - return VectorOutput; + return MFParamCategory::VectorOutput; case Mutable: - return VectorMutable; + return MFParamCategory::VectorMutable; } break; } } - BLI_assert(false); - return SingleInput; + BLI_assert_unreachable(); + return MFParamCategory::SingleInput; } bool is_input_or_mutable() const diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index 67f31a61dc4..9d09378ab63 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -111,7 +111,7 @@ class MFParamsBuilder { this->assert_current_param_name(expected_name); const int param_index = this->current_param_index(); const MFParamType ¶m_type = signature_->param_types[param_index]; - BLI_assert(param_type.category() == MFParamType::SingleOutput); + BLI_assert(param_type.category() == MFParamCategory::SingleOutput); const CPPType &type = param_type.data_type().single_type(); /* An empty span indicates that this is ignored. */ const GMutableSpan dummy_span{type}; @@ -144,8 +144,8 @@ class MFParamsBuilder { GMutableSpan computed_array(int param_index) { BLI_assert(ELEM(signature_->param_types[param_index].category(), - MFParamType::SingleOutput, - MFParamType::SingleMutable)); + MFParamCategory::SingleOutput, + MFParamCategory::SingleMutable)); int data_index = signature_->data_index(param_index); return mutable_spans_[data_index]; } @@ -153,8 +153,8 @@ class MFParamsBuilder { GVectorArray &computed_vector_array(int param_index) { BLI_assert(ELEM(signature_->param_types[param_index].category(), - MFParamType::VectorOutput, - MFParamType::VectorMutable)); + MFParamCategory::VectorOutput, + MFParamCategory::VectorMutable)); int data_index = signature_->data_index(param_index); return *vector_arrays_[data_index]; } @@ -217,7 +217,7 @@ class MFParams { } const GVArray &readonly_single_input(int param_index, StringRef name = "") { - this->assert_correct_param(param_index, name, MFParamType::SingleInput); + this->assert_correct_param(param_index, name, MFParamCategory::SingleInput); int data_index = builder_->signature_->data_index(param_index); return builder_->virtual_arrays_[data_index]; } @@ -230,7 +230,7 @@ class MFParams { */ bool single_output_is_required(int param_index, StringRef name = "") { - this->assert_correct_param(param_index, name, MFParamType::SingleOutput); + this->assert_correct_param(param_index, name, MFParamCategory::SingleOutput); int data_index = builder_->signature_->data_index(param_index); return !builder_->mutable_spans_[data_index].is_empty(); } @@ -242,7 +242,7 @@ class MFParams { } GMutableSpan uninitialized_single_output(int param_index, StringRef name = "") { - this->assert_correct_param(param_index, name, MFParamType::SingleOutput); + this->assert_correct_param(param_index, name, MFParamCategory::SingleOutput); int data_index = builder_->signature_->data_index(param_index); GMutableSpan span = builder_->mutable_spans_[data_index]; if (!span.is_empty()) { @@ -264,7 +264,7 @@ class MFParams { } GMutableSpan uninitialized_single_output_if_required(int param_index, StringRef name = "") { - this->assert_correct_param(param_index, name, MFParamType::SingleOutput); + this->assert_correct_param(param_index, name, MFParamCategory::SingleOutput); int data_index = builder_->signature_->data_index(param_index); return builder_->mutable_spans_[data_index]; } @@ -277,7 +277,7 @@ class MFParams { } const GVVectorArray &readonly_vector_input(int param_index, StringRef name = "") { - this->assert_correct_param(param_index, name, MFParamType::VectorInput); + this->assert_correct_param(param_index, name, MFParamCategory::VectorInput); int data_index = builder_->signature_->data_index(param_index); return *builder_->virtual_vector_arrays_[data_index]; } @@ -289,7 +289,7 @@ class MFParams { } GVectorArray &vector_output(int param_index, StringRef name = "") { - this->assert_correct_param(param_index, name, MFParamType::VectorOutput); + this->assert_correct_param(param_index, name, MFParamCategory::VectorOutput); int data_index = builder_->signature_->data_index(param_index); return *builder_->vector_arrays_[data_index]; } @@ -300,7 +300,7 @@ class MFParams { } GMutableSpan single_mutable(int param_index, StringRef name = "") { - this->assert_correct_param(param_index, name, MFParamType::SingleMutable); + this->assert_correct_param(param_index, name, MFParamCategory::SingleMutable); int data_index = builder_->signature_->data_index(param_index); return builder_->mutable_spans_[data_index]; } @@ -312,7 +312,7 @@ class MFParams { } GVectorArray &vector_mutable(int param_index, StringRef name = "") { - this->assert_correct_param(param_index, name, MFParamType::VectorMutable); + this->assert_correct_param(param_index, name, MFParamCategory::VectorMutable); int data_index = builder_->signature_->data_index(param_index); return *builder_->vector_arrays_[data_index]; } @@ -329,7 +329,7 @@ class MFParams { #endif } - void assert_correct_param(int param_index, StringRef name, MFParamType::Category category) + void assert_correct_param(int param_index, StringRef name, MFParamCategory category) { UNUSED_VARS_NDEBUG(param_index, name, category); #ifdef DEBUG diff --git a/source/blender/functions/FN_multi_function_signature.hh b/source/blender/functions/FN_multi_function_signature.hh index 597f565984c..62c491609a4 100644 --- a/source/blender/functions/FN_multi_function_signature.hh +++ b/source/blender/functions/FN_multi_function_signature.hh @@ -168,6 +168,32 @@ class MFSignatureBuilder { } } + template<MFParamCategory Category, typename T> + void add(MFParamTag<Category, T> /* tag */, const char *name) + { + switch (Category) { + case MFParamCategory::SingleInput: + this->single_input<T>(name); + return; + case MFParamCategory::VectorInput: + this->vector_input<T>(name); + return; + case MFParamCategory::SingleOutput: + this->single_output<T>(name); + return; + case MFParamCategory::VectorOutput: + this->vector_output<T>(name); + return; + case MFParamCategory::SingleMutable: + this->single_mutable<T>(name); + return; + case MFParamCategory::VectorMutable: + this->vector_mutable<T>(name); + return; + } + BLI_assert_unreachable(); + } + /* Context */ /** This indicates that the function accesses the context. This disables optimizations that diff --git a/source/blender/functions/intern/multi_function.cc b/source/blender/functions/intern/multi_function.cc index 6a9cb9c424c..c05087a4c2d 100644 --- a/source/blender/functions/intern/multi_function.cc +++ b/source/blender/functions/intern/multi_function.cc @@ -96,18 +96,18 @@ void MultiFunction::call_auto(IndexMask mask, MFParams params, MFContext context for (const int param_index : this->param_indices()) { const MFParamType param_type = this->param_type(param_index); switch (param_type.category()) { - case MFParamType::SingleInput: { + case MFParamCategory::SingleInput: { const GVArray &varray = params.readonly_single_input(param_index); offset_params.add_readonly_single_input(varray.slice(input_slice_range)); break; } - case MFParamType::SingleMutable: { + case MFParamCategory::SingleMutable: { const GMutableSpan span = params.single_mutable(param_index); const GMutableSpan sliced_span = span.slice(input_slice_range); offset_params.add_single_mutable(sliced_span); break; } - case MFParamType::SingleOutput: { + case MFParamCategory::SingleOutput: { const GMutableSpan span = params.uninitialized_single_output_if_required(param_index); if (span.is_empty()) { offset_params.add_ignored_single_output(); @@ -118,9 +118,9 @@ void MultiFunction::call_auto(IndexMask mask, MFParams params, MFContext context } break; } - case MFParamType::VectorInput: - case MFParamType::VectorMutable: - case MFParamType::VectorOutput: { + case MFParamCategory::VectorInput: + case MFParamCategory::VectorMutable: + case MFParamCategory::VectorOutput: { BLI_assert_unreachable(); break; } diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc index 6655e051723..bc045bfd44b 100644 --- a/source/blender/functions/intern/multi_function_procedure.cc +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -314,7 +314,7 @@ bool MFProcedure::validate_all_params_provided() const const MultiFunction &fn = instruction->fn(); for (const int param_index : fn.param_indices()) { const MFParamType param_type = fn.param_type(param_index); - if (param_type.category() == MFParamType::SingleOutput) { + if (param_type.category() == MFParamCategory::SingleOutput) { /* Single outputs are optional. */ continue; } diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc index c58ca0bb8fd..d852fe19924 100644 --- a/source/blender/functions/intern/multi_function_procedure_executor.cc +++ b/source/blender/functions/intern/multi_function_procedure_executor.cc @@ -854,32 +854,32 @@ class VariableStates { }; switch (param_type.category()) { - case MFParamType::SingleInput: { + case MFParamCategory::SingleInput: { const GVArray &data = params.readonly_single_input(param_index); add_state(value_allocator_.obtain_GVArray(data), true); break; } - case MFParamType::VectorInput: { + case MFParamCategory::VectorInput: { const GVVectorArray &data = params.readonly_vector_input(param_index); add_state(value_allocator_.obtain_GVVectorArray(data), true); break; } - case MFParamType::SingleOutput: { + case MFParamCategory::SingleOutput: { GMutableSpan data = params.uninitialized_single_output(param_index); add_state(value_allocator_.obtain_Span_not_owned(data.data()), false, data.data()); break; } - case MFParamType::VectorOutput: { + case MFParamCategory::VectorOutput: { GVectorArray &data = params.vector_output(param_index); add_state(value_allocator_.obtain_GVectorArray_not_owned(data), false, &data); break; } - case MFParamType::SingleMutable: { + case MFParamCategory::SingleMutable: { GMutableSpan data = params.single_mutable(param_index); add_state(value_allocator_.obtain_Span_not_owned(data.data()), true, data.data()); break; } - case MFParamType::VectorMutable: { + case MFParamCategory::VectorMutable: { GVectorArray &data = params.vector_mutable(param_index); add_state(value_allocator_.obtain_GVectorArray_not_owned(data), true, &data); break; diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index a1e5f0216b6..38f9da2a60c 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_array.hh" +#include "BLI_devirtualize_parameters.hh" #include "BLI_set.hh" #include "BLI_task.hh" diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh index e58c1068368..51057f600f4 100644 --- a/source/blender/nodes/NOD_math_functions.hh +++ b/source/blender/nodes/NOD_math_functions.hh @@ -9,6 +9,8 @@ #include "BLI_math_vector.hh" #include "BLI_string_ref.hh" +#include "FN_multi_function_builder.hh" + namespace blender::nodes { struct FloatMathOperationInfo { @@ -49,55 +51,58 @@ inline bool try_dispatch_float_math_fl_to_fl(const int operation, Callback &&cal return false; } + static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle(); + static auto exec_preset_slow = fn::CustomMF_presets::Materialized(); + /* This is just an utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_MATH_EXPONENT: - return dispatch([](float a) { return expf(a); }); + return dispatch(exec_preset_slow, [](float a) { return expf(a); }); case NODE_MATH_SQRT: - return dispatch([](float a) { return safe_sqrtf(a); }); + return dispatch(exec_preset_fast, [](float a) { return safe_sqrtf(a); }); case NODE_MATH_INV_SQRT: - return dispatch([](float a) { return safe_inverse_sqrtf(a); }); + return dispatch(exec_preset_fast, [](float a) { return safe_inverse_sqrtf(a); }); case NODE_MATH_ABSOLUTE: - return dispatch([](float a) { return fabs(a); }); + return dispatch(exec_preset_fast, [](float a) { return fabs(a); }); case NODE_MATH_RADIANS: - return dispatch([](float a) { return (float)DEG2RAD(a); }); + return dispatch(exec_preset_fast, [](float a) { return (float)DEG2RAD(a); }); case NODE_MATH_DEGREES: - return dispatch([](float a) { return (float)RAD2DEG(a); }); + return dispatch(exec_preset_fast, [](float a) { return (float)RAD2DEG(a); }); case NODE_MATH_SIGN: - return dispatch([](float a) { return compatible_signf(a); }); + return dispatch(exec_preset_fast, [](float a) { return compatible_signf(a); }); case NODE_MATH_ROUND: - return dispatch([](float a) { return floorf(a + 0.5f); }); + return dispatch(exec_preset_fast, [](float a) { return floorf(a + 0.5f); }); case NODE_MATH_FLOOR: - return dispatch([](float a) { return floorf(a); }); + return dispatch(exec_preset_fast, [](float a) { return floorf(a); }); case NODE_MATH_CEIL: - return dispatch([](float a) { return ceilf(a); }); + return dispatch(exec_preset_fast, [](float a) { return ceilf(a); }); case NODE_MATH_FRACTION: - return dispatch([](float a) { return a - floorf(a); }); + return dispatch(exec_preset_fast, [](float a) { return a - floorf(a); }); case NODE_MATH_TRUNC: - return dispatch([](float a) { return a >= 0.0f ? floorf(a) : ceilf(a); }); + return dispatch(exec_preset_fast, [](float a) { return a >= 0.0f ? floorf(a) : ceilf(a); }); case NODE_MATH_SINE: - return dispatch([](float a) { return sinf(a); }); + return dispatch(exec_preset_slow, [](float a) { return sinf(a); }); case NODE_MATH_COSINE: - return dispatch([](float a) { return cosf(a); }); + return dispatch(exec_preset_slow, [](float a) { return cosf(a); }); case NODE_MATH_TANGENT: - return dispatch([](float a) { return tanf(a); }); + return dispatch(exec_preset_slow, [](float a) { return tanf(a); }); case NODE_MATH_SINH: - return dispatch([](float a) { return sinhf(a); }); + return dispatch(exec_preset_slow, [](float a) { return sinhf(a); }); case NODE_MATH_COSH: - return dispatch([](float a) { return coshf(a); }); + return dispatch(exec_preset_slow, [](float a) { return coshf(a); }); case NODE_MATH_TANH: - return dispatch([](float a) { return tanhf(a); }); + return dispatch(exec_preset_slow, [](float a) { return tanhf(a); }); case NODE_MATH_ARCSINE: - return dispatch([](float a) { return safe_asinf(a); }); + return dispatch(exec_preset_slow, [](float a) { return safe_asinf(a); }); case NODE_MATH_ARCCOSINE: - return dispatch([](float a) { return safe_acosf(a); }); + return dispatch(exec_preset_slow, [](float a) { return safe_acosf(a); }); case NODE_MATH_ARCTANGENT: - return dispatch([](float a) { return atanf(a); }); + return dispatch(exec_preset_slow, [](float a) { return atanf(a); }); } return false; } @@ -113,41 +118,45 @@ inline bool try_dispatch_float_math_fl_fl_to_fl(const int operation, Callback && return false; } + static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle(); + static auto exec_preset_slow = fn::CustomMF_presets::Materialized(); + /* This is just an utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_MATH_ADD: - return dispatch([](float a, float b) { return a + b; }); + return dispatch(exec_preset_fast, [](float a, float b) { return a + b; }); case NODE_MATH_SUBTRACT: - return dispatch([](float a, float b) { return a - b; }); + return dispatch(exec_preset_fast, [](float a, float b) { return a - b; }); case NODE_MATH_MULTIPLY: - return dispatch([](float a, float b) { return a * b; }); + return dispatch(exec_preset_fast, [](float a, float b) { return a * b; }); case NODE_MATH_DIVIDE: - return dispatch([](float a, float b) { return safe_divide(a, b); }); + return dispatch(exec_preset_fast, [](float a, float b) { return safe_divide(a, b); }); case NODE_MATH_POWER: - return dispatch([](float a, float b) { return safe_powf(a, b); }); + return dispatch(exec_preset_slow, [](float a, float b) { return safe_powf(a, b); }); case NODE_MATH_LOGARITHM: - return dispatch([](float a, float b) { return safe_logf(a, b); }); + return dispatch(exec_preset_slow, [](float a, float b) { return safe_logf(a, b); }); case NODE_MATH_MINIMUM: - return dispatch([](float a, float b) { return std::min(a, b); }); + return dispatch(exec_preset_fast, [](float a, float b) { return std::min(a, b); }); case NODE_MATH_MAXIMUM: - return dispatch([](float a, float b) { return std::max(a, b); }); + return dispatch(exec_preset_fast, [](float a, float b) { return std::max(a, b); }); case NODE_MATH_LESS_THAN: - return dispatch([](float a, float b) { return (float)(a < b); }); + return dispatch(exec_preset_fast, [](float a, float b) { return (float)(a < b); }); case NODE_MATH_GREATER_THAN: - return dispatch([](float a, float b) { return (float)(a > b); }); + return dispatch(exec_preset_fast, [](float a, float b) { return (float)(a > b); }); case NODE_MATH_MODULO: - return dispatch([](float a, float b) { return safe_modf(a, b); }); + return dispatch(exec_preset_fast, [](float a, float b) { return safe_modf(a, b); }); case NODE_MATH_SNAP: - return dispatch([](float a, float b) { return floorf(safe_divide(a, b)) * b; }); + return dispatch(exec_preset_fast, + [](float a, float b) { return floorf(safe_divide(a, b)) * b; }); case NODE_MATH_ARCTAN2: - return dispatch([](float a, float b) { return atan2f(a, b); }); + return dispatch(exec_preset_slow, [](float a, float b) { return atan2f(a, b); }); case NODE_MATH_PINGPONG: - return dispatch([](float a, float b) { return pingpongf(a, b); }); + return dispatch(exec_preset_fast, [](float a, float b) { return pingpongf(a, b); }); } return false; } @@ -164,57 +173,29 @@ inline bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, Callback } /* This is just an utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_MATH_MULTIPLY_ADD: - return dispatch([](float a, float b, float c) { return a * b + c; }); + return dispatch(fn::CustomMF_presets::AllSpanOrSingle(), + [](float a, float b, float c) { return a * b + c; }); case NODE_MATH_COMPARE: - return dispatch([](float a, float b, float c) -> float { - return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f; - }); + return dispatch(fn::CustomMF_presets::SomeSpanOrSingle<0, 1>(), + [](float a, float b, float c) -> float { + return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f; + }); case NODE_MATH_SMOOTH_MIN: - return dispatch([](float a, float b, float c) { return smoothminf(a, b, c); }); + return dispatch(fn::CustomMF_presets::SomeSpanOrSingle<0, 1>(), + [](float a, float b, float c) { return smoothminf(a, b, c); }); case NODE_MATH_SMOOTH_MAX: - return dispatch([](float a, float b, float c) { return -smoothminf(-a, -b, c); }); + return dispatch(fn::CustomMF_presets::SomeSpanOrSingle<0, 1>(), + [](float a, float b, float c) { return -smoothminf(-a, -b, c); }); case NODE_MATH_WRAP: - return dispatch([](float a, float b, float c) { return wrapf(a, b, c); }); - } - return false; -} - -/** - * This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature. - */ -template<typename Callback> -inline bool try_dispatch_float_math_fl_fl_to_bool(const NodeCompareOperation operation, - Callback &&callback) -{ - const FloatMathOperationInfo *info = get_float_compare_operation_info(operation); - if (info == nullptr) { - return false; - } - - /* This is just an utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); - return true; - }; - - switch (operation) { - case NODE_COMPARE_LESS_THAN: - return dispatch([](float a, float b) { return a < b; }); - case NODE_COMPARE_LESS_EQUAL: - return dispatch([](float a, float b) { return a <= b; }); - case NODE_COMPARE_GREATER_THAN: - return dispatch([](float a, float b) { return a > b; }); - case NODE_COMPARE_GREATER_EQUAL: - return dispatch([](float a, float b) { return a >= b; }); - default: - return false; + return dispatch(fn::CustomMF_presets::SomeSpanOrSingle<0>(), + [](float a, float b, float c) { return wrapf(a, b, c); }); } return false; } @@ -233,35 +214,41 @@ inline bool try_dispatch_float_math_fl3_fl3_to_fl3(const NodeVectorMathOperation return false; } + static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle(); + static auto exec_preset_slow = fn::CustomMF_presets::Materialized(); + /* This is just a utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_VECTOR_MATH_ADD: - return dispatch([](float3 a, float3 b) { return a + b; }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return a + b; }); case NODE_VECTOR_MATH_SUBTRACT: - return dispatch([](float3 a, float3 b) { return a - b; }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return a - b; }); case NODE_VECTOR_MATH_MULTIPLY: - return dispatch([](float3 a, float3 b) { return a * b; }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return a * b; }); case NODE_VECTOR_MATH_DIVIDE: - return dispatch([](float3 a, float3 b) { return safe_divide(a, b); }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return safe_divide(a, b); }); case NODE_VECTOR_MATH_CROSS_PRODUCT: - return dispatch([](float3 a, float3 b) { return cross_high_precision(a, b); }); + return dispatch(exec_preset_fast, + [](float3 a, float3 b) { return cross_high_precision(a, b); }); case NODE_VECTOR_MATH_PROJECT: - return dispatch([](float3 a, float3 b) { return project(a, b); }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return project(a, b); }); case NODE_VECTOR_MATH_REFLECT: - return dispatch([](float3 a, float3 b) { return reflect(a, normalize(b)); }); + return dispatch(exec_preset_fast, + [](float3 a, float3 b) { return reflect(a, normalize(b)); }); case NODE_VECTOR_MATH_SNAP: - return dispatch([](float3 a, float3 b) { return floor(safe_divide(a, b)) * b; }); + return dispatch(exec_preset_fast, + [](float3 a, float3 b) { return floor(safe_divide(a, b)) * b; }); case NODE_VECTOR_MATH_MODULO: - return dispatch([](float3 a, float3 b) { return mod(a, b); }); + return dispatch(exec_preset_slow, [](float3 a, float3 b) { return mod(a, b); }); case NODE_VECTOR_MATH_MINIMUM: - return dispatch([](float3 a, float3 b) { return min(a, b); }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return min(a, b); }); case NODE_VECTOR_MATH_MAXIMUM: - return dispatch([](float3 a, float3 b) { return max(a, b); }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return max(a, b); }); default: return false; } @@ -282,17 +269,19 @@ inline bool try_dispatch_float_math_fl3_fl3_to_fl(const NodeVectorMathOperation return false; } + static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle(); + /* This is just a utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_VECTOR_MATH_DOT_PRODUCT: - return dispatch([](float3 a, float3 b) { return dot(a, b); }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return dot(a, b); }); case NODE_VECTOR_MATH_DISTANCE: - return dispatch([](float3 a, float3 b) { return distance(a, b); }); + return dispatch(exec_preset_fast, [](float3 a, float3 b) { return distance(a, b); }); default: return false; } @@ -313,21 +302,25 @@ inline bool try_dispatch_float_math_fl3_fl3_fl3_to_fl3(const NodeVectorMathOpera return false; } + static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle(); + static auto exec_preset_slow = fn::CustomMF_presets::Materialized(); + /* This is just a utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_VECTOR_MATH_MULTIPLY_ADD: - return dispatch([](float3 a, float3 b, float3 c) { return a * b + c; }); + return dispatch(exec_preset_fast, [](float3 a, float3 b, float3 c) { return a * b + c; }); case NODE_VECTOR_MATH_WRAP: - return dispatch([](float3 a, float3 b, float3 c) { + return dispatch(exec_preset_slow, [](float3 a, float3 b, float3 c) { return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z)); }); case NODE_VECTOR_MATH_FACEFORWARD: - return dispatch([](float3 a, float3 b, float3 c) { return faceforward(a, b, c); }); + return dispatch(exec_preset_fast, + [](float3 a, float3 b, float3 c) { return faceforward(a, b, c); }); default: return false; } @@ -348,15 +341,18 @@ inline bool try_dispatch_float_math_fl3_fl3_fl_to_fl3(const NodeVectorMathOperat return false; } + static auto exec_preset_slow = fn::CustomMF_presets::Materialized(); + /* This is just a utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_VECTOR_MATH_REFRACT: - return dispatch([](float3 a, float3 b, float c) { return refract(a, normalize(b), c); }); + return dispatch(exec_preset_slow, + [](float3 a, float3 b, float c) { return refract(a, normalize(b), c); }); default: return false; } @@ -377,15 +373,17 @@ inline bool try_dispatch_float_math_fl3_to_fl(const NodeVectorMathOperation oper return false; } + static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle(); + /* This is just a utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_VECTOR_MATH_LENGTH: - return dispatch([](float3 in) { return length(in); }); + return dispatch(exec_preset_fast, [](float3 in) { return length(in); }); default: return false; } @@ -404,15 +402,17 @@ inline bool try_dispatch_float_math_fl3_fl_to_fl3(const NodeVectorMathOperation return false; } + static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle(); + /* This is just a utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_VECTOR_MATH_SCALE: - return dispatch([](float3 a, float b) { return a * b; }); + return dispatch(exec_preset_fast, [](float3 a, float b) { return a * b; }); default: return false; } @@ -433,29 +433,36 @@ inline bool try_dispatch_float_math_fl3_to_fl3(const NodeVectorMathOperation ope return false; } + static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle(); + static auto exec_preset_slow = fn::CustomMF_presets::Materialized(); + /* This is just a utility function to keep the individual cases smaller. */ - auto dispatch = [&](auto math_function) -> bool { - callback(math_function, *info); + auto dispatch = [&](auto exec_preset, auto math_function) -> bool { + callback(exec_preset, math_function, *info); return true; }; switch (operation) { case NODE_VECTOR_MATH_NORMALIZE: - return dispatch([](float3 in) { return normalize(in); }); /* Should be safe. */ + return dispatch(exec_preset_fast, + [](float3 in) { return normalize(in); }); /* Should be safe. */ case NODE_VECTOR_MATH_FLOOR: - return dispatch([](float3 in) { return floor(in); }); + return dispatch(exec_preset_fast, [](float3 in) { return floor(in); }); case NODE_VECTOR_MATH_CEIL: - return dispatch([](float3 in) { return ceil(in); }); + return dispatch(exec_preset_fast, [](float3 in) { return ceil(in); }); case NODE_VECTOR_MATH_FRACTION: - return dispatch([](float3 in) { return fract(in); }); + return dispatch(exec_preset_fast, [](float3 in) { return fract(in); }); case NODE_VECTOR_MATH_ABSOLUTE: - return dispatch([](float3 in) { return abs(in); }); + return dispatch(exec_preset_fast, [](float3 in) { return abs(in); }); case NODE_VECTOR_MATH_SINE: - return dispatch([](float3 in) { return float3(sinf(in.x), sinf(in.y), sinf(in.z)); }); + return dispatch(exec_preset_slow, + [](float3 in) { return float3(sinf(in.x), sinf(in.y), sinf(in.z)); }); case NODE_VECTOR_MATH_COSINE: - return dispatch([](float3 in) { return float3(cosf(in.x), cosf(in.y), cosf(in.z)); }); + return dispatch(exec_preset_slow, + [](float3 in) { return float3(cosf(in.x), cosf(in.y), cosf(in.z)); }); case NODE_VECTOR_MATH_TANGENT: - return dispatch([](float3 in) { return float3(tanf(in.x), tanf(in.y), tanf(in.z)); }); + return dispatch(exec_preset_slow, + [](float3 in) { return float3(tanf(in.x), tanf(in.y), tanf(in.z)); }); default: return false; } diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index e29dc0c0c28..b6d7e6c9a5f 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -70,23 +70,24 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) static const fn::MultiFunction *get_multi_function(bNode &bnode) { - static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{"And", - [](bool a, bool b) { return a && b; }}; - static fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{"Or", - [](bool a, bool b) { return a || b; }}; - static fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }}; - static fn::CustomMF_SI_SI_SO<bool, bool, bool> nand_fn{"Not And", - [](bool a, bool b) { return !(a && b); }}; - static fn::CustomMF_SI_SI_SO<bool, bool, bool> nor_fn{"Nor", - [](bool a, bool b) { return !(a || b); }}; - static fn::CustomMF_SI_SI_SO<bool, bool, bool> xnor_fn{"Equal", - [](bool a, bool b) { return a == b; }}; - static fn::CustomMF_SI_SI_SO<bool, bool, bool> xor_fn{"Not Equal", - [](bool a, bool b) { return a != b; }}; - static fn::CustomMF_SI_SI_SO<bool, bool, bool> imply_fn{"Imply", - [](bool a, bool b) { return !a || b; }}; - static fn::CustomMF_SI_SI_SO<bool, bool, bool> nimply_fn{"Subtract", - [](bool a, bool b) { return a && !b; }}; + static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle(); + static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{ + "And", [](bool a, bool b) { return a && b; }, exec_preset}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{ + "Or", [](bool a, bool b) { return a || b; }, exec_preset}; + static fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }, exec_preset}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nand_fn{ + "Not And", [](bool a, bool b) { return !(a && b); }, exec_preset}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nor_fn{ + "Nor", [](bool a, bool b) { return !(a || b); }, exec_preset}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> xnor_fn{ + "Equal", [](bool a, bool b) { return a == b; }, exec_preset}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> xor_fn{ + "Not Equal", [](bool a, bool b) { return a != b; }, exec_preset}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> imply_fn{ + "Imply", [](bool a, bool b) { return !a || b; }, exec_preset}; + static fn::CustomMF_SI_SI_SO<bool, bool, bool> nimply_fn{ + "Subtract", [](bool a, bool b) { return a && !b; }, exec_preset}; switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc index e6fdf1820fa..7bc9d2d79e8 100644 --- a/source/blender/nodes/function/nodes/node_fn_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_compare.cc @@ -171,71 +171,77 @@ static const fn::MultiFunction *get_multi_function(bNode &node) { const NodeFunctionCompare *data = (NodeFunctionCompare *)node.storage; + static auto exec_preset_all = fn::CustomMF_presets::AllSpanOrSingle(); + static auto exec_preset_first_two = fn::CustomMF_presets::SomeSpanOrSingle<0, 1>(); + switch (data->data_type) { case SOCK_FLOAT: switch (data->operation) { case NODE_COMPARE_LESS_THAN: { static fn::CustomMF_SI_SI_SO<float, float, bool> fn{ - "Less Than", [](float a, float b) { return a < b; }}; + "Less Than", [](float a, float b) { return a < b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_LESS_EQUAL: { static fn::CustomMF_SI_SI_SO<float, float, bool> fn{ - "Less Equal", [](float a, float b) { return a <= b; }}; + "Less Equal", [](float a, float b) { return a <= b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_GREATER_THAN: { static fn::CustomMF_SI_SI_SO<float, float, bool> fn{ - "Greater Than", [](float a, float b) { return a > b; }}; + "Greater Than", [](float a, float b) { return a > b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_GREATER_EQUAL: { static fn::CustomMF_SI_SI_SO<float, float, bool> fn{ - "Greater Equal", [](float a, float b) { return a >= b; }}; + "Greater Equal", [](float a, float b) { return a >= b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_EQUAL: { static fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> fn{ - "Equal", [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }}; + "Equal", + [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_NOT_EQUAL: static fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> fn{ "Not Equal", - [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }}; + [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }, + exec_preset_first_two}; return &fn; } break; case SOCK_INT: switch (data->operation) { case NODE_COMPARE_LESS_THAN: { - static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Less Than", - [](int a, int b) { return a < b; }}; + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{ + "Less Than", [](int a, int b) { return a < b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_LESS_EQUAL: { - static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Less Equal", - [](int a, int b) { return a <= b; }}; + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{ + "Less Equal", [](int a, int b) { return a <= b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_GREATER_THAN: { - static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Greater Than", - [](int a, int b) { return a > b; }}; + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{ + "Greater Than", [](int a, int b) { return a > b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_GREATER_EQUAL: { - static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Greater Equal", - [](int a, int b) { return a >= b; }}; + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{ + "Greater Equal", [](int a, int b) { return a >= b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_EQUAL: { - static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Equal", - [](int a, int b) { return a == b; }}; + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{ + "Equal", [](int a, int b) { return a == b; }, exec_preset_all}; return &fn; } case NODE_COMPARE_NOT_EQUAL: { - static fn::CustomMF_SI_SI_SO<int, int, bool> fn{"Not Equal", - [](int a, int b) { return a != b; }}; + static fn::CustomMF_SI_SI_SO<int, int, bool> fn{ + "Not Equal", [](int a, int b) { return a != b; }, exec_preset_all}; return &fn; } } @@ -247,31 +253,36 @@ static const fn::MultiFunction *get_multi_function(bNode &node) case NODE_COMPARE_MODE_AVERAGE: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Less Than - Average", - [](float3 a, float3 b) { return component_average(a) < component_average(b); }}; + [](float3 a, float3 b) { return component_average(a) < component_average(b); }, + exec_preset_all}; return &fn; } case NODE_COMPARE_MODE_DOT_PRODUCT: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Less Than - Dot Product", - [](float3 a, float3 b, float comp) { return math::dot(a, b) < comp; }}; + [](float3 a, float3 b, float comp) { return math::dot(a, b) < comp; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_DIRECTION: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Less Than - Direction", - [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) < angle; }}; + [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) < angle; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_ELEMENT: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Less Than - Element-wise", - [](float3 a, float3 b) { return a.x < b.x && a.y < b.y && a.z < b.z; }}; + [](float3 a, float3 b) { return a.x < b.x && a.y < b.y && a.z < b.z; }, + exec_preset_all}; return &fn; } case NODE_COMPARE_MODE_LENGTH: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Less Than - Length", - [](float3 a, float3 b) { return math::length(a) < math::length(b); }}; + [](float3 a, float3 b) { return math::length(a) < math::length(b); }, + exec_preset_all}; return &fn; } } @@ -281,31 +292,36 @@ static const fn::MultiFunction *get_multi_function(bNode &node) case NODE_COMPARE_MODE_AVERAGE: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Less Equal - Average", - [](float3 a, float3 b) { return component_average(a) <= component_average(b); }}; + [](float3 a, float3 b) { return component_average(a) <= component_average(b); }, + exec_preset_all}; return &fn; } case NODE_COMPARE_MODE_DOT_PRODUCT: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Less Equal - Dot Product", - [](float3 a, float3 b, float comp) { return math::dot(a, b) <= comp; }}; + [](float3 a, float3 b, float comp) { return math::dot(a, b) <= comp; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_DIRECTION: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Less Equal - Direction", - [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) <= angle; }}; + [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) <= angle; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_ELEMENT: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Less Equal - Element-wise", - [](float3 a, float3 b) { return a.x <= b.x && a.y <= b.y && a.z <= b.z; }}; + [](float3 a, float3 b) { return a.x <= b.x && a.y <= b.y && a.z <= b.z; }, + exec_preset_all}; return &fn; } case NODE_COMPARE_MODE_LENGTH: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Less Equal - Length", - [](float3 a, float3 b) { return math::length(a) <= math::length(b); }}; + [](float3 a, float3 b) { return math::length(a) <= math::length(b); }, + exec_preset_all}; return &fn; } } @@ -315,31 +331,36 @@ static const fn::MultiFunction *get_multi_function(bNode &node) case NODE_COMPARE_MODE_AVERAGE: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Greater Than - Average", - [](float3 a, float3 b) { return component_average(a) > component_average(b); }}; + [](float3 a, float3 b) { return component_average(a) > component_average(b); }, + exec_preset_all}; return &fn; } case NODE_COMPARE_MODE_DOT_PRODUCT: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Greater Than - Dot Product", - [](float3 a, float3 b, float comp) { return math::dot(a, b) > comp; }}; + [](float3 a, float3 b, float comp) { return math::dot(a, b) > comp; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_DIRECTION: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Greater Than - Direction", - [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) > angle; }}; + [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) > angle; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_ELEMENT: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Greater Than - Element-wise", - [](float3 a, float3 b) { return a.x > b.x && a.y > b.y && a.z > b.z; }}; + [](float3 a, float3 b) { return a.x > b.x && a.y > b.y && a.z > b.z; }, + exec_preset_all}; return &fn; } case NODE_COMPARE_MODE_LENGTH: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Greater Than - Length", - [](float3 a, float3 b) { return math::length(a) > math::length(b); }}; + [](float3 a, float3 b) { return math::length(a) > math::length(b); }, + exec_preset_all}; return &fn; } } @@ -349,31 +370,36 @@ static const fn::MultiFunction *get_multi_function(bNode &node) case NODE_COMPARE_MODE_AVERAGE: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Greater Equal - Average", - [](float3 a, float3 b) { return component_average(a) >= component_average(b); }}; + [](float3 a, float3 b) { return component_average(a) >= component_average(b); }, + exec_preset_all}; return &fn; } case NODE_COMPARE_MODE_DOT_PRODUCT: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Greater Equal - Dot Product", - [](float3 a, float3 b, float comp) { return math::dot(a, b) >= comp; }}; + [](float3 a, float3 b, float comp) { return math::dot(a, b) >= comp; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_DIRECTION: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ "Greater Equal - Direction", - [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) >= angle; }}; + [](float3 a, float3 b, float angle) { return angle_v3v3(a, b) >= angle; }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_ELEMENT: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Greater Equal - Element-wise", - [](float3 a, float3 b) { return a.x >= b.x && a.y >= b.y && a.z >= b.z; }}; + [](float3 a, float3 b) { return a.x >= b.x && a.y >= b.y && a.z >= b.z; }, + exec_preset_all}; return &fn; } case NODE_COMPARE_MODE_LENGTH: { static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{ "Greater Equal - Length", - [](float3 a, float3 b) { return math::length(a) >= math::length(b); }}; + [](float3 a, float3 b) { return math::length(a) >= math::length(b); }, + exec_preset_all}; return &fn; } } @@ -382,38 +408,48 @@ static const fn::MultiFunction *get_multi_function(bNode &node) switch (data->mode) { case NODE_COMPARE_MODE_AVERAGE: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ - "Equal - Average", [](float3 a, float3 b, float epsilon) { + "Equal - Average", + [](float3 a, float3 b, float epsilon) { return abs(component_average(a) - component_average(b)) <= epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_DOT_PRODUCT: { static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float, float, bool> fn{ - "Equal - Dot Product", [](float3 a, float3 b, float comp, float epsilon) { + "Equal - Dot Product", + [](float3 a, float3 b, float comp, float epsilon) { return abs(math::dot(a, b) - comp) <= epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_DIRECTION: { static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float, float, bool> fn{ - "Equal - Direction", [](float3 a, float3 b, float angle, float epsilon) { + "Equal - Direction", + [](float3 a, float3 b, float angle, float epsilon) { return abs(angle_v3v3(a, b) - angle) <= epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_ELEMENT: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ - "Equal - Element-wise", [](float3 a, float3 b, float epsilon) { + "Equal - Element-wise", + [](float3 a, float3 b, float epsilon) { return abs(a.x - b.x) <= epsilon && abs(a.y - b.y) <= epsilon && abs(a.z - b.z) <= epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_LENGTH: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ - "Equal - Length", [](float3 a, float3 b, float epsilon) { + "Equal - Length", + [](float3 a, float3 b, float epsilon) { return abs(math::length(a) - math::length(b)) <= epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } } @@ -422,38 +458,48 @@ static const fn::MultiFunction *get_multi_function(bNode &node) switch (data->mode) { case NODE_COMPARE_MODE_AVERAGE: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ - "Not Equal - Average", [](float3 a, float3 b, float epsilon) { + "Not Equal - Average", + [](float3 a, float3 b, float epsilon) { return abs(component_average(a) - component_average(b)) > epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_DOT_PRODUCT: { static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float, float, bool> fn{ - "Not Equal - Dot Product", [](float3 a, float3 b, float comp, float epsilon) { + "Not Equal - Dot Product", + [](float3 a, float3 b, float comp, float epsilon) { return abs(math::dot(a, b) - comp) >= epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_DIRECTION: { static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float, float, bool> fn{ - "Not Equal - Direction", [](float3 a, float3 b, float angle, float epsilon) { + "Not Equal - Direction", + [](float3 a, float3 b, float angle, float epsilon) { return abs(angle_v3v3(a, b) - angle) > epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_ELEMENT: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ - "Not Equal - Element-wise", [](float3 a, float3 b, float epsilon) { + "Not Equal - Element-wise", + [](float3 a, float3 b, float epsilon) { return abs(a.x - b.x) > epsilon && abs(a.y - b.y) > epsilon && abs(a.z - b.z) > epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_MODE_LENGTH: { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{ - "Not Equal - Length", [](float3 a, float3 b, float epsilon) { + "Not Equal - Length", + [](float3 a, float3 b, float epsilon) { return abs(math::length(a) - math::length(b)) > epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } } @@ -464,32 +510,40 @@ static const fn::MultiFunction *get_multi_function(bNode &node) switch (data->operation) { case NODE_COMPARE_EQUAL: { static fn::CustomMF_SI_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, float, bool> fn{ - "Equal", [](ColorGeometry4f a, ColorGeometry4f b, float epsilon) { + "Equal", + [](ColorGeometry4f a, ColorGeometry4f b, float epsilon) { return abs(a.r - b.r) <= epsilon && abs(a.g - b.g) <= epsilon && abs(a.b - b.b) <= epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_NOT_EQUAL: { static fn::CustomMF_SI_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, float, bool> fn{ - "Not Equal", [](ColorGeometry4f a, ColorGeometry4f b, float epsilon) { + "Not Equal", + [](ColorGeometry4f a, ColorGeometry4f b, float epsilon) { return abs(a.r - b.r) > epsilon && abs(a.g - b.g) > epsilon && abs(a.b - b.b) > epsilon; - }}; + }, + exec_preset_first_two}; return &fn; } case NODE_COMPARE_COLOR_BRIGHTER: { static fn::CustomMF_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, bool> fn{ - "Brighter", [](ColorGeometry4f a, ColorGeometry4f b) { + "Brighter", + [](ColorGeometry4f a, ColorGeometry4f b) { return rgb_to_grayscale(a) > rgb_to_grayscale(b); - }}; + }, + exec_preset_all}; return &fn; } case NODE_COMPARE_COLOR_DARKER: { static fn::CustomMF_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, bool> fn{ - "Darker", [](ColorGeometry4f a, ColorGeometry4f b) { + "Darker", + [](ColorGeometry4f a, ColorGeometry4f b) { return rgb_to_grayscale(a) < rgb_to_grayscale(b); - }}; + }, + exec_preset_all}; return &fn; } } diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index fddc0e99082..9c9d8620a7e 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -2,6 +2,7 @@ #include <cmath> +#include "BLI_noise.hh" #include "BLI_string.h" #include "RNA_enum_types.h" @@ -40,11 +41,15 @@ static void node_float_to_int_label(const bNodeTree *UNUSED(ntree), static const fn::MultiFunction *get_multi_function(bNode &bnode) { - static fn::CustomMF_SI_SO<float, int> round_fn{"Round", [](float a) { return (int)round(a); }}; - static fn::CustomMF_SI_SO<float, int> floor_fn{"Floor", [](float a) { return (int)floor(a); }}; - static fn::CustomMF_SI_SO<float, int> ceil_fn{"Ceiling", [](float a) { return (int)ceil(a); }}; - static fn::CustomMF_SI_SO<float, int> trunc_fn{"Truncate", - [](float a) { return (int)trunc(a); }}; + static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle(); + static fn::CustomMF_SI_SO<float, int> round_fn{ + "Round", [](float a) { return (int)round(a); }, exec_preset}; + static fn::CustomMF_SI_SO<float, int> floor_fn{ + "Floor", [](float a) { return (int)floor(a); }, exec_preset}; + static fn::CustomMF_SI_SO<float, int> ceil_fn{ + "Ceiling", [](float a) { return (int)ceil(a); }, exec_preset}; + static fn::CustomMF_SI_SO<float, int> trunc_fn{ + "Truncate", [](float a) { return (int)trunc(a); }, exec_preset}; switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) { case FN_NODE_FLOAT_TO_INT_ROUND: diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc index 3c4513d4e62..a0942ced1be 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_value.cc +++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc @@ -134,162 +134,6 @@ static void fn_node_random_value_gather_link_search(GatherLinkSearchOpParams &pa } } -class RandomVectorFunction : public fn::MultiFunction { - public: - RandomVectorFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Random Value"}; - signature.single_input<float3>("Min"); - signature.single_input<float3>("Max"); - signature.single_input<int>("ID"); - signature.single_input<int>("Seed"); - signature.single_output<float3>("Value"); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &min_values = params.readonly_single_input<float3>(0, "Min"); - const VArray<float3> &max_values = params.readonly_single_input<float3>(1, "Max"); - const VArray<int> &ids = params.readonly_single_input<int>(2, "ID"); - const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed"); - MutableSpan<float3> values = params.uninitialized_single_output<float3>(4, "Value"); - - for (int64_t i : mask) { - const float3 min_value = min_values[i]; - const float3 max_value = max_values[i]; - const int seed = seeds[i]; - const int id = ids[i]; - - const float x = noise::hash_to_float(seed, id, 0); - const float y = noise::hash_to_float(seed, id, 1); - const float z = noise::hash_to_float(seed, id, 2); - - values[i] = float3(x, y, z) * (max_value - min_value) + min_value; - } - } -}; - -class RandomFloatFunction : public fn::MultiFunction { - public: - RandomFloatFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Random Value"}; - signature.single_input<float>("Min"); - signature.single_input<float>("Max"); - signature.single_input<int>("ID"); - signature.single_input<int>("Seed"); - signature.single_output<float>("Value"); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float> &min_values = params.readonly_single_input<float>(0, "Min"); - const VArray<float> &max_values = params.readonly_single_input<float>(1, "Max"); - const VArray<int> &ids = params.readonly_single_input<int>(2, "ID"); - const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed"); - MutableSpan<float> values = params.uninitialized_single_output<float>(4, "Value"); - - for (int64_t i : mask) { - const float min_value = min_values[i]; - const float max_value = max_values[i]; - const int seed = seeds[i]; - const int id = ids[i]; - - const float value = noise::hash_to_float(seed, id); - values[i] = value * (max_value - min_value) + min_value; - } - } -}; - -class RandomIntFunction : public fn::MultiFunction { - public: - RandomIntFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Random Value"}; - signature.single_input<int>("Min"); - signature.single_input<int>("Max"); - signature.single_input<int>("ID"); - signature.single_input<int>("Seed"); - signature.single_output<int>("Value"); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<int> &min_values = params.readonly_single_input<int>(0, "Min"); - const VArray<int> &max_values = params.readonly_single_input<int>(1, "Max"); - const VArray<int> &ids = params.readonly_single_input<int>(2, "ID"); - const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed"); - MutableSpan<int> values = params.uninitialized_single_output<int>(4, "Value"); - - /* Add one to the maximum and use floor to produce an even - * distribution for the first and last values (See T93591). */ - for (int64_t i : mask) { - const float min_value = min_values[i]; - const float max_value = max_values[i] + 1.0f; - const int seed = seeds[i]; - const int id = ids[i]; - - const float value = noise::hash_to_float(id, seed); - values[i] = floor(value * (max_value - min_value) + min_value); - } - } -}; - -class RandomBoolFunction : public fn::MultiFunction { - public: - RandomBoolFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Random Value"}; - signature.single_input<float>("Probability"); - signature.single_input<int>("ID"); - signature.single_input<int>("Seed"); - signature.single_output<bool>("Value"); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float> &probabilities = params.readonly_single_input<float>(0, "Probability"); - const VArray<int> &ids = params.readonly_single_input<int>(1, "ID"); - const VArray<int> &seeds = params.readonly_single_input<int>(2, "Seed"); - MutableSpan<bool> values = params.uninitialized_single_output<bool>(3, "Value"); - - for (int64_t i : mask) { - const int seed = seeds[i]; - const int id = ids[i]; - const float probability = probabilities[i]; - values[i] = noise::hash_to_float(id, seed) <= probability; - } - } -}; - static void fn_node_random_value_build_multi_function(NodeMultiFunctionBuilder &builder) { const NodeRandomValue &storage = node_storage(builder.node()); @@ -297,22 +141,64 @@ static void fn_node_random_value_build_multi_function(NodeMultiFunctionBuilder & switch (data_type) { case CD_PROP_FLOAT3: { - static RandomVectorFunction fn; + static fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float3>> + fn{"Random Vector", + [](float3 min_value, float3 max_value, int id, int seed, float3 *r_value) { + const float x = noise::hash_to_float(seed, id, 0); + const float y = noise::hash_to_float(seed, id, 1); + const float z = noise::hash_to_float(seed, id, 2); + *r_value = float3(x, y, z) * (max_value - min_value) + min_value; + }, + fn::CustomMF_presets::SomeSpanOrSingle<2>()}; builder.set_matching_fn(fn); break; } case CD_PROP_FLOAT: { - static RandomFloatFunction fn; + static fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float>> + fn{"Random Float", + [](float min_value, float max_value, int id, int seed, float *r_value) { + const float value = noise::hash_to_float(seed, id); + *r_value = value * (max_value - min_value) + min_value; + }, + fn::CustomMF_presets::SomeSpanOrSingle<2>()}; builder.set_matching_fn(fn); break; } case CD_PROP_INT32: { - static RandomIntFunction fn; + static fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, int>> + fn{"Random Int", + [](int min_value, int max_value, int id, int seed, int *r_value) { + const float value = noise::hash_to_float(id, seed); + /* Add one to the maximum and use floor to produce an even + * distribution for the first and last values (See T93591). */ + *r_value = floor(value * (max_value + 1 - min_value) + min_value); + }, + fn::CustomMF_presets::SomeSpanOrSingle<2>()}; builder.set_matching_fn(fn); break; } case CD_PROP_BOOL: { - static RandomBoolFunction fn; + static fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, int>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, bool>> + fn{"Random Bool", + [](float probability, int id, int seed, bool *r_value) { + *r_value = noise::hash_to_float(id, seed) <= probability; + }, + fn::CustomMF_presets::SomeSpanOrSingle<1>()}; builder.set_matching_fn(fn); break; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index 121ee015d2d..e0348f27e51 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -551,8 +551,10 @@ static Field<int> get_curve_count_field(GeoNodeExecParams params, const GeometryNodeCurveResampleMode mode) { if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { - static fn::CustomMF_SI_SO<int, int> max_one_fn("Clamp Above One", - [](int value) { return std::max(1, value); }); + static fn::CustomMF_SI_SO<int, int> max_one_fn( + "Clamp Above One", + [](int value) { return std::max(1, value); }, + fn::CustomMF_presets::AllSpanOrSingle()); auto clamp_op = std::make_shared<FieldOperation>( FieldOperation(max_one_fn, {Field<int>(params.extract_input<Field<int>>("Count"))})); @@ -561,12 +563,14 @@ static Field<int> get_curve_count_field(GeoNodeExecParams params, if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn( - "Length Input to Count", [](const float curve_length, const float sample_length) { + "Length Input to Count", + [](const float curve_length, const float sample_length) { /* Find the number of sampled segments by dividing the total length by * the sample length. Then there is one more sampled point than segment. */ const int count = int(curve_length / sample_length) + 1; return std::max(1, count); - }); + }, + fn::CustomMF_presets::AllSpanOrSingle()); auto get_count_op = std::make_shared<FieldOperation>( FieldOperation(get_count_fn, @@ -588,9 +592,11 @@ static Field<int> get_curve_count_field(GeoNodeExecParams params, static Field<bool> get_selection_field(GeoNodeExecParams params) { static fn::CustomMF_SI_SI_SO<bool, int, bool> get_selection_fn( - "Create Curve Selection", [](const bool orig_selection, const int evaluated_points_num) { + "Create Curve Selection", + [](const bool orig_selection, const int evaluated_points_num) { return orig_selection && evaluated_points_num > 1; - }); + }, + fn::CustomMF_presets::AllSpanOrSingle()); auto selection_op = std::make_shared<FieldOperation>( FieldOperation(get_selection_fn, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc index 6661d03a851..9077b59254c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -203,9 +203,11 @@ static Field<float> get_length_input_field(const GeoNodeExecParams ¶ms, /* Just make sure the length is in bounds of the curve. */ Field<float> length_field = params.get_input<Field<float>>("Length"); auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>( - __func__, [curve_total_length](float length) { + __func__, + [curve_total_length](float length) { return std::clamp(length, 0.0f, curve_total_length); - }); + }, + fn::CustomMF_presets::AllSpanOrSingle()); auto clamp_op = std::make_shared<FieldOperation>( FieldOperation(std::move(clamp_fn), {std::move(length_field)})); @@ -215,10 +217,12 @@ static Field<float> get_length_input_field(const GeoNodeExecParams ¶ms, /* Convert the factor to a length and clamp it to the bounds of the curve. */ Field<float> factor_field = params.get_input<Field<float>>("Factor"); auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>( - __func__, [curve_total_length](float factor) { + __func__, + [curve_total_length](float factor) { const float length = factor * curve_total_length; return std::clamp(length, 0.0f, curve_total_length); - }); + }, + fn::CustomMF_presets::AllSpanOrSingle()); auto process_op = std::make_shared<FieldOperation>( FieldOperation(std::move(clamp_fn), {std::move(factor_field)})); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 0072fbcde93..4591cfa1da2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -1274,7 +1274,9 @@ static void node_geo_exec(GeoNodeExecParams params) /* Create a combined field from the offset and the scale so the field evaluator * can take care of the multiplication and to simplify each extrude function. */ static fn::CustomMF_SI_SI_SO<float3, float, float3> multiply_fn{ - "Scale", [](const float3 &offset, const float scale) { return offset * scale; }}; + "Scale", + [](const float3 &offset, const float scale) { return offset * scale; }, + fn::CustomMF_presets::AllSpanOrSingle()}; std::shared_ptr<FieldOperation> multiply_op = std::make_shared<FieldOperation>( FieldOperation(multiply_fn, {std::move(offset_field), std::move(scale_field)})); const Field<float3> final_offset{std::move(multiply_op)}; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc index 6a7aab1cea7..1a0cc53cb6c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -124,7 +124,9 @@ static void node_geo_exec(GeoNodeExecParams params) /* Use another multi-function operation to make sure the input radius is greater than zero. * TODO: Use mutable multi-function once that is supported. */ static fn::CustomMF_SI_SO<float, float> max_zero_fn( - __func__, [](float value) { return std::max(0.0f, value); }); + __func__, + [](float value) { return std::max(0.0f, value); }, + fn::CustomMF_presets::AllSpanOrSingle()); auto max_zero_op = std::make_shared<FieldOperation>( FieldOperation(max_zero_fn, {std::move(radius)})); Field<float> positive_radius(std::move(max_zero_op), 0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index 6e27b346407..ddc87e3dac4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -139,35 +139,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms) } } -template<typename T> class SwitchFieldsFunction : public fn::MultiFunction { - public: - SwitchFieldsFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Switch"}; - signature.single_input<bool>("Switch"); - signature.single_input<T>("False"); - signature.single_input<T>("True"); - signature.single_output<T>("Output"); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<bool> &switches = params.readonly_single_input<bool>(0, "Switch"); - const VArray<T> &falses = params.readonly_single_input<T>(1, "False"); - const VArray<T> &trues = params.readonly_single_input<T>(2, "True"); - MutableSpan<T> values = params.uninitialized_single_output_if_required<T>(3, "Output"); - for (int64_t i : mask) { - new (&values[i]) T(switches[i] ? trues[i] : falses[i]); - } - } -}; - template<typename T> void switch_fields(GeoNodeExecParams ¶ms, const StringRef suffix) { if (params.lazy_require_input("Switch")) { @@ -190,7 +161,11 @@ template<typename T> void switch_fields(GeoNodeExecParams ¶ms, const StringR Field<T> falses_field = params.extract_input<Field<T>>(name_false); Field<T> trues_field = params.extract_input<Field<T>>(name_true); - auto switch_fn = std::make_unique<SwitchFieldsFunction<T>>(); + static fn::CustomMF_SI_SI_SI_SO<bool, T, T, T> switch_fn{ + "Switch", [](bool condition, const T &false_value, const T &true_value) { + return condition ? true_value : false_value; + }}; + auto switch_op = std::make_shared<FieldOperation>(FieldOperation( std::move(switch_fn), {std::move(switches_field), std::move(falses_field), std::move(trues_field)})); diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index 8e7934bf34e..9ba9a279c57 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -223,327 +223,106 @@ static float3 clamp_range(const float3 value, const float3 min, const float3 max clamp_range(value.z, min.z, max.z)); } -static void map_range_vector_signature(fn::MFSignatureBuilder *signature, bool use_steps) +template<bool Clamp> static auto build_float_linear() { - signature->single_input<float3>("Vector"); - signature->single_input<float3>("From Min"); - signature->single_input<float3>("From Max"); - signature->single_input<float3>("To Min"); - signature->single_input<float3>("To Max"); - if (use_steps) { - signature->single_input<float3>("Steps"); - } - signature->single_output<float3>("Vector"); + return fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float>>{ + Clamp ? "Map Range (clamped)" : "Map Range (unclamped)", + [](float value, float from_min, float from_max, float to_min, float to_max, float *r_value) { + const float factor = safe_divide(value - from_min, from_max - from_min); + float result = to_min + factor * (to_max - to_min); + if constexpr (Clamp) { + result = clamp_range(result, to_min, to_max); + } + *r_value = result; + }, + fn::CustomMF_presets::SomeSpanOrSingle<0>()}; } -class MapRangeVectorFunction : public fn::MultiFunction { - private: - bool clamp_; - - public: - MapRangeVectorFunction(bool clamp) : clamp_(clamp) - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Vector Map Range"}; - map_range_vector_signature(&signature, false); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); - const VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); - const VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); - const VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); - const VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); - MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); - - for (int64_t i : mask) { - float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); - results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; - } - - if (clamp_) { - for (int64_t i : mask) { - results[i] = clamp_range(results[i], to_min[i], to_max[i]); - } - } - } -}; - -class MapRangeSteppedVectorFunction : public fn::MultiFunction { - private: - bool clamp_; - - public: - MapRangeSteppedVectorFunction(bool clamp) : clamp_(clamp) - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Vector Map Range Stepped"}; - map_range_vector_signature(&signature, true); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); - const VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); - const VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); - const VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); - const VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); - const VArray<float3> &steps = params.readonly_single_input<float3>(5, "Steps"); - MutableSpan<float3> results = params.uninitialized_single_output<float3>(6, "Vector"); - - for (int64_t i : mask) { - float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); - factor = math::safe_divide(math::floor(factor * (steps[i] + 1.0f)), steps[i]); - results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; - } - - if (clamp_) { - for (int64_t i : mask) { - results[i] = clamp_range(results[i], to_min[i], to_max[i]); - } - } - } -}; - -class MapRangeSmoothstepVectorFunction : public fn::MultiFunction { - public: - MapRangeSmoothstepVectorFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; - map_range_vector_signature(&signature, false); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); - const VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); - const VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); - const VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); - const VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); - MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); - - for (int64_t i : mask) { - float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); - clamp_v3(factor, 0.0f, 1.0f); - factor = (float3(3.0f) - 2.0f * factor) * (factor * factor); - results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; - } - } -}; - -class MapRangeSmootherstepVectorFunction : public fn::MultiFunction { - public: - MapRangeSmootherstepVectorFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Vector Map Range Smoothstep"}; - map_range_vector_signature(&signature, false); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float3> &values = params.readonly_single_input<float3>(0, "Vector"); - const VArray<float3> &from_min = params.readonly_single_input<float3>(1, "From Min"); - const VArray<float3> &from_max = params.readonly_single_input<float3>(2, "From Max"); - const VArray<float3> &to_min = params.readonly_single_input<float3>(3, "To Min"); - const VArray<float3> &to_max = params.readonly_single_input<float3>(4, "To Max"); - MutableSpan<float3> results = params.uninitialized_single_output<float3>(5, "Vector"); - - for (int64_t i : mask) { - float3 factor = math::safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); - clamp_v3(factor, 0.0f, 1.0f); - factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f); - results[i] = factor * (to_max[i] - to_min[i]) + to_min[i]; - } - } -}; - -static void map_range_signature(fn::MFSignatureBuilder *signature, bool use_steps) +template<bool Clamp> static auto build_float_stepped() { - signature->single_input<float>("Value"); - signature->single_input<float>("From Min"); - signature->single_input<float>("From Max"); - signature->single_input<float>("To Min"); - signature->single_input<float>("To Max"); - if (use_steps) { - signature->single_input<float>("Steps"); - } - signature->single_output<float>("Result"); + return fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float>>{ + Clamp ? "Map Range Stepped (clamped)" : "Map Range Stepped (unclamped)", + [](float value, + float from_min, + float from_max, + float to_min, + float to_max, + float steps, + float *r_value) { + float factor = safe_divide(value - from_min, from_max - from_min); + factor = safe_divide(floorf(factor * (steps + 1.0f)), steps); + float result = to_min + factor * (to_max - to_min); + if constexpr (Clamp) { + result = clamp_range(result, to_min, to_max); + } + *r_value = result; + }, + fn::CustomMF_presets::SomeSpanOrSingle<0>()}; } -class MapRangeFunction : public fn::MultiFunction { - private: - bool clamp_; - - public: - MapRangeFunction(bool clamp) : clamp_(clamp) - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Map Range"}; - map_range_signature(&signature, false); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - const VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); - const VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); - const VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); - const VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); - MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); - - for (int64_t i : mask) { - float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); - results[i] = to_min[i] + factor * (to_max[i] - to_min[i]); - } - - if (clamp_) { - for (int64_t i : mask) { - results[i] = clamp_range(results[i], to_min[i], to_max[i]); - } - } - } -}; - -class MapRangeSteppedFunction : public fn::MultiFunction { - private: - bool clamp_; - - public: - MapRangeSteppedFunction(bool clamp) : clamp_(clamp) - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Map Range Stepped"}; - map_range_signature(&signature, true); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - const VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); - const VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); - const VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); - const VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); - const VArray<float> &steps = params.readonly_single_input<float>(5, "Steps"); - MutableSpan<float> results = params.uninitialized_single_output<float>(6, "Result"); - - for (int64_t i : mask) { - float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); - factor = safe_divide(floorf(factor * (steps[i] + 1.0f)), steps[i]); - results[i] = to_min[i] + factor * (to_max[i] - to_min[i]); - } - - if (clamp_) { - for (int64_t i : mask) { - results[i] = clamp_range(results[i], to_min[i], to_max[i]); - } - } - } -}; - -class MapRangeSmoothstepFunction : public fn::MultiFunction { - public: - MapRangeSmoothstepFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Map Range Smoothstep"}; - map_range_signature(&signature, false); - return signature.build(); - } - - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - const VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); - const VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); - const VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); - const VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); - MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); - - for (int64_t i : mask) { - float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); - factor = std::clamp(factor, 0.0f, 1.0f); - factor = (3.0f - 2.0f * factor) * (factor * factor); - results[i] = to_min[i] + factor * (to_max[i] - to_min[i]); - } - } -}; - -class MapRangeSmootherstepFunction : public fn::MultiFunction { - public: - MapRangeSmootherstepFunction() - { - static fn::MFSignature signature = create_signature(); - this->set_signature(&signature); - } - - static fn::MFSignature create_signature() - { - fn::MFSignatureBuilder signature{"Map Range Smoothstep"}; - map_range_signature(&signature, false); - return signature.build(); - } +template<bool Clamp> static auto build_vector_linear() +{ + return fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float3>>{ + Clamp ? "Vector Map Range (clamped)" : "Vector Map Range (unclamped)", + [](const float3 &value, + const float3 &from_min, + const float3 &from_max, + const float3 &to_min, + const float3 &to_max, + float3 *r_value) { + float3 factor = math::safe_divide(value - from_min, from_max - from_min); + float3 result = factor * (to_max - to_min) + to_min; + if constexpr (Clamp) { + result = clamp_range(result, to_min, to_max); + } + *r_value = result; + }, + fn::CustomMF_presets::SomeSpanOrSingle<0>()}; +} - void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override - { - const VArray<float> &values = params.readonly_single_input<float>(0, "Value"); - const VArray<float> &from_min = params.readonly_single_input<float>(1, "From Min"); - const VArray<float> &from_max = params.readonly_single_input<float>(2, "From Max"); - const VArray<float> &to_min = params.readonly_single_input<float>(3, "To Min"); - const VArray<float> &to_max = params.readonly_single_input<float>(4, "To Max"); - MutableSpan<float> results = params.uninitialized_single_output<float>(5, "Result"); - - for (int64_t i : mask) { - float factor = safe_divide(values[i] - from_min[i], from_max[i] - from_min[i]); - factor = std::clamp(factor, 0.0f, 1.0f); - factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f); - results[i] = to_min[i] + factor * (to_max[i] - to_min[i]); - } - } -}; +template<bool Clamp> static auto build_vector_stepped() +{ + return fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float3>>{ + Clamp ? "Vector Map Range Stepped (clamped)" : "Vector Map Range Stepped (unclamped)", + [](const float3 &value, + const float3 &from_min, + const float3 &from_max, + const float3 &to_min, + const float3 &to_max, + const float3 &steps, + float3 *r_value) { + float3 factor = math::safe_divide(value - from_min, from_max - from_min); + factor = math::safe_divide(math::floor(factor * (steps + 1.0f)), steps); + float3 result = factor * (to_max - to_min) + to_min; + if constexpr (Clamp) { + result = clamp_range(result, to_min, to_max); + } + *r_value = result; + }, + fn::CustomMF_presets::SomeSpanOrSingle<0>()}; +} static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &builder) { @@ -556,34 +335,70 @@ static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &bui switch (interpolation_type) { case NODE_MAP_RANGE_LINEAR: { if (clamp) { - static MapRangeVectorFunction fn_with_clamp{true}; - builder.set_matching_fn(fn_with_clamp); + static auto fn = build_vector_linear<true>(); + builder.set_matching_fn(fn); } else { - static MapRangeVectorFunction fn_without_clamp{false}; - builder.set_matching_fn(fn_without_clamp); + static auto fn = build_vector_linear<false>(); + builder.set_matching_fn(fn); } break; } case NODE_MAP_RANGE_STEPPED: { if (clamp) { - static MapRangeSteppedVectorFunction fn_stepped_with_clamp{true}; - builder.set_matching_fn(fn_stepped_with_clamp); + static auto fn = build_vector_stepped<true>(); + builder.set_matching_fn(fn); } else { - static MapRangeSteppedVectorFunction fn_stepped_without_clamp{false}; - builder.set_matching_fn(fn_stepped_without_clamp); + static auto fn = build_vector_stepped<false>(); + builder.set_matching_fn(fn); } break; } case NODE_MAP_RANGE_SMOOTHSTEP: { - static MapRangeSmoothstepVectorFunction smoothstep; - builder.set_matching_fn(smoothstep); + static fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float3>> + fn{"Vector Map Range Smoothstep", + [](const float3 &value, + const float3 &from_min, + const float3 &from_max, + const float3 &to_min, + const float3 &to_max, + float3 *r_value) { + float3 factor = math::safe_divide(value - from_min, from_max - from_min); + clamp_v3(factor, 0.0f, 1.0f); + factor = (float3(3.0f) - 2.0f * factor) * (factor * factor); + *r_value = factor * (to_max - to_min) + to_min; + }, + fn::CustomMF_presets::SomeSpanOrSingle<0>()}; + builder.set_matching_fn(fn); break; } case NODE_MAP_RANGE_SMOOTHERSTEP: { - static MapRangeSmootherstepVectorFunction smootherstep; - builder.set_matching_fn(smootherstep); + static fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float3>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float3>> + fn{"Vector Map Range Smootherstep", + [](const float3 &value, + const float3 &from_min, + const float3 &from_max, + const float3 &to_min, + const float3 &to_max, + float3 *r_value) { + float3 factor = math::safe_divide(value - from_min, from_max - from_min); + clamp_v3(factor, 0.0f, 1.0f); + factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f); + *r_value = factor * (to_max - to_min) + to_min; + }, + fn::CustomMF_presets::SomeSpanOrSingle<0>()}; + builder.set_matching_fn(fn); break; } default: @@ -594,34 +409,70 @@ static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &bui switch (interpolation_type) { case NODE_MAP_RANGE_LINEAR: { if (clamp) { - static MapRangeFunction fn_with_clamp{true}; - builder.set_matching_fn(fn_with_clamp); + static auto fn = build_float_linear<true>(); + builder.set_matching_fn(fn); } else { - static MapRangeFunction fn_without_clamp{false}; - builder.set_matching_fn(fn_without_clamp); + static auto fn = build_float_linear<false>(); + builder.set_matching_fn(fn); } break; } case NODE_MAP_RANGE_STEPPED: { if (clamp) { - static MapRangeSteppedFunction fn_stepped_with_clamp{true}; - builder.set_matching_fn(fn_stepped_with_clamp); + static auto fn = build_float_stepped<true>(); + builder.set_matching_fn(fn); } else { - static MapRangeSteppedFunction fn_stepped_without_clamp{false}; - builder.set_matching_fn(fn_stepped_without_clamp); + static auto fn = build_float_stepped<false>(); + builder.set_matching_fn(fn); } break; } case NODE_MAP_RANGE_SMOOTHSTEP: { - static MapRangeSmoothstepFunction smoothstep; - builder.set_matching_fn(smoothstep); + static fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float>> + fn{"Map Range Smoothstep", + [](float value, + float from_min, + float from_max, + float to_min, + float to_max, + float *r_value) { + float factor = safe_divide(value - from_min, from_max - from_min); + factor = std::clamp(factor, 0.0f, 1.0f); + factor = (3.0f - 2.0f * factor) * (factor * factor); + *r_value = to_min + factor * (to_max - to_min); + }, + fn::CustomMF_presets::SomeSpanOrSingle<0>()}; + builder.set_matching_fn(fn); break; } case NODE_MAP_RANGE_SMOOTHERSTEP: { - static MapRangeSmootherstepFunction smootherstep; - builder.set_matching_fn(smootherstep); + static fn::CustomMF<fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleInput, float>, + fn::MFParamTag<fn::MFParamCategory::SingleOutput, float>> + fn{"Map Range Smoothstep", + [](float value, + float from_min, + float from_max, + float to_min, + float to_max, + float *r_value) { + float factor = safe_divide(value - from_min, from_max - from_min); + factor = std::clamp(factor, 0.0f, 1.0f); + factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f); + *r_value = to_min + factor * (to_max - to_min); + }, + fn::CustomMF_presets::SomeSpanOrSingle<0>()}; + builder.set_matching_fn(fn); break; } default: diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index a828011a3ab..8a2b18d7d76 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -106,28 +106,30 @@ static const fn::MultiFunction *get_base_multi_function(bNode &node) const int mode = node.custom1; const fn::MultiFunction *base_fn = nullptr; - try_dispatch_float_math_fl_to_fl(mode, [&](auto function, const FloatMathOperationInfo &info) { - static fn::CustomMF_SI_SO<float, float> fn{info.title_case_name.c_str(), function}; - base_fn = &fn; - }); + try_dispatch_float_math_fl_to_fl( + mode, [&](auto devi_fn, auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SO<float, float> fn{ + info.title_case_name.c_str(), function, devi_fn}; + base_fn = &fn; + }); if (base_fn != nullptr) { return base_fn; } - try_dispatch_float_math_fl_fl_to_fl(mode, - [&](auto function, const FloatMathOperationInfo &info) { - static fn::CustomMF_SI_SI_SO<float, float, float> fn{ - info.title_case_name.c_str(), function}; - base_fn = &fn; - }); + try_dispatch_float_math_fl_fl_to_fl( + mode, [&](auto devi_fn, auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SO<float, float, float> fn{ + info.title_case_name.c_str(), function, devi_fn}; + base_fn = &fn; + }); if (base_fn != nullptr) { return base_fn; } try_dispatch_float_math_fl_fl_fl_to_fl( - mode, [&](auto function, const FloatMathOperationInfo &info) { + mode, [&](auto devi_fn, auto function, const FloatMathOperationInfo &info) { static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{ - info.title_case_name.c_str(), function}; + info.title_case_name.c_str(), function, devi_fn}; base_fn = &fn; }); if (base_fn != nullptr) { diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc index 0d751157817..94a6febe92e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc @@ -106,7 +106,9 @@ static int gpu_shader_combxyz(GPUMaterial *mat, static void sh_node_combxyz_build_multi_function(NodeMultiFunctionBuilder &builder) { static fn::CustomMF_SI_SI_SI_SO<float, float, float, float3> fn{ - "Combine Vector", [](float x, float y, float z) { return float3(x, y, z); }}; + "Combine Vector", + [](float x, float y, float z) { return float3(x, y, z); }, + fn::CustomMF_presets::AllSpanOrSingle()}; builder.set_matching_fn(fn); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 02a3552704e..21f5c44c640 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -231,20 +231,20 @@ static const fn::MultiFunction *get_multi_function(bNode &node) const fn::MultiFunction *multi_fn = nullptr; - try_dispatch_float_math_fl3_fl3_to_fl3(operation, - [&](auto function, const FloatMathOperationInfo &info) { - static fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ - info.title_case_name.c_str(), function}; - multi_fn = &fn; - }); + try_dispatch_float_math_fl3_fl3_to_fl3( + operation, [&](auto exec_preset, auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ + info.title_case_name.c_str(), function, exec_preset}; + multi_fn = &fn; + }); if (multi_fn != nullptr) { return multi_fn; } try_dispatch_float_math_fl3_fl3_fl3_to_fl3( - operation, [&](auto function, const FloatMathOperationInfo &info) { + operation, [&](auto exec_preset, auto function, const FloatMathOperationInfo &info) { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{ - info.title_case_name.c_str(), function}; + info.title_case_name.c_str(), function, exec_preset}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -252,38 +252,39 @@ static const fn::MultiFunction *get_multi_function(bNode &node) } try_dispatch_float_math_fl3_fl3_fl_to_fl3( - operation, [&](auto function, const FloatMathOperationInfo &info) { + operation, [&](auto exec_preset, auto function, const FloatMathOperationInfo &info) { static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{ - info.title_case_name.c_str(), function}; + info.title_case_name.c_str(), function, exec_preset}; multi_fn = &fn; }); if (multi_fn != nullptr) { return multi_fn; } - try_dispatch_float_math_fl3_fl3_to_fl(operation, - [&](auto function, const FloatMathOperationInfo &info) { - static fn::CustomMF_SI_SI_SO<float3, float3, float> fn{ - info.title_case_name.c_str(), function}; - multi_fn = &fn; - }); + try_dispatch_float_math_fl3_fl3_to_fl( + operation, [&](auto exec_preset, auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SO<float3, float3, float> fn{ + info.title_case_name.c_str(), function, exec_preset}; + multi_fn = &fn; + }); if (multi_fn != nullptr) { return multi_fn; } - try_dispatch_float_math_fl3_fl_to_fl3(operation, - [&](auto function, const FloatMathOperationInfo &info) { - static fn::CustomMF_SI_SI_SO<float3, float, float3> fn{ - info.title_case_name.c_str(), function}; - multi_fn = &fn; - }); + try_dispatch_float_math_fl3_fl_to_fl3( + operation, [&](auto exec_preset, auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SI_SO<float3, float, float3> fn{ + info.title_case_name.c_str(), function, exec_preset}; + multi_fn = &fn; + }); if (multi_fn != nullptr) { return multi_fn; } try_dispatch_float_math_fl3_to_fl3( - operation, [&](auto function, const FloatMathOperationInfo &info) { - static fn::CustomMF_SI_SO<float3, float3> fn{info.title_case_name.c_str(), function}; + operation, [&](auto exec_preset, auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SO<float3, float3> fn{ + info.title_case_name.c_str(), function, exec_preset}; multi_fn = &fn; }); if (multi_fn != nullptr) { @@ -291,8 +292,9 @@ static const fn::MultiFunction *get_multi_function(bNode &node) } try_dispatch_float_math_fl3_to_fl( - operation, [&](auto function, const FloatMathOperationInfo &info) { - static fn::CustomMF_SI_SO<float3, float> fn{info.title_case_name.c_str(), function}; + operation, [&](auto exec_preset, auto function, const FloatMathOperationInfo &info) { + static fn::CustomMF_SI_SO<float3, float> fn{ + info.title_case_name.c_str(), function, exec_preset}; multi_fn = &fn; }); if (multi_fn != nullptr) { |