Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc1
-rw-r--r--source/blender/blenkernel/intern/type_conversions.cc3
-rw-r--r--source/blender/blenlib/BLI_devirtualize_parameters.hh309
-rw-r--r--source/blender/blenlib/BLI_parameter_pack_utils.hh122
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh71
-rw-r--r--source/blender/blenlib/CMakeLists.txt2
-rw-r--r--source/blender/functions/FN_multi_function_builder.hh700
-rw-r--r--source/blender/functions/FN_multi_function_param_type.hh44
-rw-r--r--source/blender/functions/FN_multi_function_params.hh28
-rw-r--r--source/blender/functions/FN_multi_function_signature.hh26
-rw-r--r--source/blender/functions/intern/multi_function.cc12
-rw-r--r--source/blender/functions/intern/multi_function_procedure.cc2
-rw-r--r--source/blender/functions/intern/multi_function_procedure_executor.cc12
-rw-r--r--source/blender/geometry/intern/mesh_to_curve_convert.cc1
-rw-r--r--source/blender/nodes/NOD_math_functions.hh251
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc35
-rw-r--r--source/blender/nodes/function/nodes/node_fn_compare.cc186
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc15
-rw-r--r--source/blender/nodes/function/nodes/node_fn_random_value.cc206
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_switch.cc35
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_map_range.cc527
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc26
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_xyz.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc54
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 &param_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 &params)
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 &params,
/* 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 &params,
/* 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 &params)
}
}
-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 &params, const StringRef suffix)
{
if (params.lazy_require_input("Switch")) {
@@ -190,7 +161,11 @@ template<typename T> void switch_fields(GeoNodeExecParams &params, 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) {