Functions: refactor multi-function builder API

* New `build_mf` namespace for the multi-function builders.
* The type name of the created multi-functions is now "private",
  i.e. the caller has to use `auto`. This has the benefit that the
  implementation can change more freely without affecting
  the caller.
* `CustomMF` does not use `std::function` internally anymore.
  This reduces some overhead during code generation and at
  run-time.
* `CustomMF` now supports single-mutable parameters.
This commit is contained in:
Jacques Lucke 2023-01-07 16:19:59 +01:00
parent 380db3edb3
commit b3146200a8
31 changed files with 637 additions and 816 deletions

View File

@ -440,12 +440,12 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
make_array_write_attribute<float3>,
tag_component_positions_changed);
static const fn::CustomMF_SI_SO<int8_t, int8_t> handle_type_clamp{
static auto handle_type_clamp = fn::build_mf::SI1_SO<int8_t, int8_t>(
"Handle Type Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN);
},
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
ATTR_DOMAIN_POINT,
CD_PROP_INT8,
@ -484,10 +484,10 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
make_array_write_attribute<float>,
tag_component_positions_changed);
static const fn::CustomMF_SI_SO<int8_t, int8_t> nurbs_order_clamp{
static const auto nurbs_order_clamp = fn::build_mf::SI1_SO<int8_t, int8_t>(
"NURBS Order Validate",
[](int8_t value) { return std::max<int8_t>(value, 0); },
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
@ -501,12 +501,12 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
tag_component_topology_changed,
AttributeValidator{&nurbs_order_clamp});
static const fn::CustomMF_SI_SO<int8_t, int8_t> normal_mode_clamp{
static const auto normal_mode_clamp = fn::build_mf::SI1_SO<int8_t, int8_t>(
"Normal Mode Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, NORMAL_MODE_MINIMUM_TWIST, NORMAL_MODE_Z_UP);
},
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
@ -520,12 +520,12 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
tag_component_normals_changed,
AttributeValidator{&normal_mode_clamp});
static const fn::CustomMF_SI_SO<int8_t, int8_t> knots_mode_clamp{
static const auto knots_mode_clamp = fn::build_mf::SI1_SO<int8_t, int8_t>(
"Knots Mode Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT_BEZIER);
},
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
@ -539,12 +539,12 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
tag_component_topology_changed,
AttributeValidator{&knots_mode_clamp});
static const fn::CustomMF_SI_SO<int8_t, int8_t> curve_type_clamp{
static const auto curve_type_clamp = fn::build_mf::SI1_SO<int8_t, int8_t>(
"Curve Type Validate",
[](int8_t value) {
return std::clamp<int8_t>(value, CURVE_TYPE_CATMULL_ROM, CURVE_TYPES_NUM);
},
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider curve_type("curve_type",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
@ -558,10 +558,10 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
tag_component_curve_types_changed,
AttributeValidator{&curve_type_clamp});
static const fn::CustomMF_SI_SO<int, int> resolution_clamp{
static const auto resolution_clamp = fn::build_mf::SI1_SO<int, int>(
"Resolution Validate",
[](int value) { return std::max<int>(value, 1); },
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider resolution("resolution",
ATTR_DOMAIN_CURVE,
CD_PROP_INT32,

View File

@ -1264,13 +1264,13 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
make_array_write_attribute<int>,
nullptr);
static const fn::CustomMF_SI_SO<int, int> material_index_clamp{
static const auto material_index_clamp = fn::build_mf::SI1_SO<int, int>(
"Material Index Validate",
[](int value) {
/* Use #short for the maximum since many areas still use that type for indices. */
return std::clamp<int>(value, 0, std::numeric_limits<short>::max());
},
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider material_index("material_index",
ATTR_DOMAIN_FACE,
CD_PROP_INT32,

View File

@ -20,12 +20,12 @@ static void add_implicit_conversion(DataTypeConversions &conversions)
static const CPPType &to_type = CPPType::get<To>();
static const std::string conversion_name = from_type.name() + " to " + to_type.name();
static fn::CustomMF_SI_SO<From, To> multi_function{
static auto multi_function = fn::build_mf::SI1_SO<From, To>(
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); },
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
static auto convert_single_to_initialized = [](const void *src, void *dst) {
*(To *)dst = ConversionF(*(const From *)src);
};

View File

@ -8,17 +8,15 @@
* This file contains several utilities to create multi-functions with less redundant code.
*/
#include <functional>
#include "FN_multi_function.hh"
namespace blender::fn {
namespace blender::fn::build_mf {
/**
* 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.
*/
namespace CustomMF_presets {
namespace exec_presets {
/** Method to execute a function in case devirtualization was not possible. */
enum class FallbackMode {
@ -63,7 +61,7 @@ struct AllSpanOrSingle {
auto create_devirtualizers(TypeSequence<ParamTags...> /*param_tags*/,
std::index_sequence<I...> /*indices*/,
const IndexMask &mask,
const std::tuple<LoadedParams...> &loaded_params)
const std::tuple<LoadedParams...> &loaded_params) const
{
return std::make_tuple(IndexMaskDevirtualizer<true, true>{mask}, [&]() {
typedef ParamTags ParamTag;
@ -72,7 +70,9 @@ struct AllSpanOrSingle {
const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
return GVArrayDevirtualizer<T, true, true>{varray_impl};
}
else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) {
else if constexpr (ELEM(ParamTag::category,
MFParamCategory::SingleOutput,
MFParamCategory::SingleMutable)) {
T *ptr = std::get<I>(loaded_params);
return BasicDevirtualizer<T *>{ptr};
}
@ -93,7 +93,7 @@ template<size_t... Indices> struct SomeSpanOrSingle {
auto create_devirtualizers(TypeSequence<ParamTags...> /*param_tags*/,
std::index_sequence<I...> /*indices*/,
const IndexMask &mask,
const std::tuple<LoadedParams...> &loaded_params)
const std::tuple<LoadedParams...> &loaded_params) const
{
return std::make_tuple(IndexMaskDevirtualizer<true, true>{mask}, [&]() {
typedef ParamTags ParamTag;
@ -104,7 +104,9 @@ template<size_t... Indices> struct SomeSpanOrSingle {
const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
return GVArrayDevirtualizer<T, true, UseSpan>{varray_impl};
}
else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) {
else if constexpr (ELEM(ParamTag::category,
MFParamCategory::SingleOutput,
MFParamCategory::SingleMutable)) {
T *ptr = std::get<I>(loaded_params);
return BasicDevirtualizer<T *>{ptr};
}
@ -112,7 +114,7 @@ template<size_t... Indices> struct SomeSpanOrSingle {
}
};
} // namespace CustomMF_presets
} // namespace exec_presets
namespace detail {
@ -142,23 +144,23 @@ void execute_array(TypeSequence<ParamTags...> /*param_tags*/,
* reference, because the pointer points to uninitialized memory. */
return args + i;
}
else if constexpr (ParamTag::category == MFParamCategory::SingleMutable) {
/* For mutables, pass a mutable reference to the function. */
return args[i];
}
}()...);
}
}
} // namespace detail
namespace materialize_detail {
enum class ArgMode {
enum class MaterializeArgMode {
Unknown,
Single,
Span,
Materialized,
};
template<typename ParamTag> struct ArgInfo {
ArgMode mode = ArgMode::Unknown;
template<typename ParamTag> struct MaterializeArgInfo {
MaterializeArgMode mode = MaterializeArgMode::Unknown;
Span<typename ParamTag::base_type> internal_span;
};
@ -182,9 +184,11 @@ void execute_materialized_impl(TypeSequence<ParamTags...> /*param_tags*/,
return chunks[in_i];
}
else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) {
/* For outputs, a pointer is passed, because the memory is uninitialized. */
return chunks + out_i;
}
else if constexpr (ParamTag::category == MFParamCategory::SingleMutable) {
return chunks[out_i];
}
}()...);
}
}
@ -217,7 +221,7 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
std::tuple<MutableSpan<typename ParamTags::base_type>...> buffers;
/* Information about every parameter. */
std::tuple<ArgInfo<ParamTags>...> args_info;
std::tuple<MaterializeArgInfo<ParamTags>...> args_info;
(
/* Setup information for all parameters. */
@ -225,7 +229,7 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
/* Use `typedef` instead of `using` to work around a compiler bug. */
typedef ParamTags ParamTag;
typedef typename ParamTag::base_type T;
[[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
[[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
if constexpr (ParamTag::category == MFParamCategory::SingleInput) {
const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
const CommonVArrayInfo common_info = varray_impl.common_info();
@ -236,7 +240,7 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
const T &in_single = *static_cast<const T *>(common_info.data);
uninitialized_fill_n(in_chunk.data(), in_chunk.size(), in_single);
std::get<I>(buffers) = in_chunk;
arg_info.mode = ArgMode::Single;
arg_info.mode = MaterializeArgMode::Single;
}
else if (common_info.type == CommonVArrayInfo::Type::Span) {
/* Remember the span so that it doesn't have to be retrieved in every iteration. */
@ -264,9 +268,9 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
[&] {
using ParamTag = ParamTags;
using T = typename ParamTag::base_type;
[[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
[[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
if constexpr (ParamTag::category == MFParamCategory::SingleInput) {
if (arg_info.mode == ArgMode::Single) {
if (arg_info.mode == MaterializeArgMode::Single) {
/* The single value has been filled into a buffer already reused for every chunk. */
return Span<T>(std::get<I>(buffers));
}
@ -276,7 +280,7 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
/* 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;
arg_info.mode = MaterializeArgMode::Span;
return arg_info.internal_span.slice(sliced_mask_range);
}
}
@ -287,11 +291,13 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
varray_impl.materialize_compressed_to_uninitialized(sliced_mask, in_chunk.data());
/* Remember that this parameter has been materialized, so that the values are
* destructed properly when the chunk is done. */
arg_info.mode = ArgMode::Materialized;
arg_info.mode = MaterializeArgMode::Materialized;
return Span<T>(in_chunk);
}
}
else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) {
else if constexpr (ELEM(ParamTag::category,
MFParamCategory::SingleOutput,
MFParamCategory::SingleMutable)) {
/* For outputs, just pass a pointer. This is important so that `__restrict` works. */
return std::get<I>(loaded_params);
}
@ -303,9 +309,9 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
/* Use `typedef` instead of `using` to work around a compiler bug. */
typedef ParamTags ParamTag;
typedef typename ParamTag::base_type T;
[[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
[[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
if constexpr (ParamTag::category == MFParamCategory::SingleInput) {
if (arg_info.mode == ArgMode::Materialized) {
if (arg_info.mode == MaterializeArgMode::Materialized) {
T *in_chunk = std::get<I>(buffers_owner).ptr();
destruct_n(in_chunk, chunk_size);
}
@ -320,9 +326,9 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
/* Use `typedef` instead of `using` to work around a compiler bug. */
typedef ParamTags ParamTag;
typedef typename ParamTag::base_type T;
[[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
[[maybe_unused]] MaterializeArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
if constexpr (ParamTag::category == MFParamCategory::SingleInput) {
if (arg_info.mode == ArgMode::Single) {
if (arg_info.mode == MaterializeArgMode::Single) {
MutableSpan<T> in_chunk = std::get<I>(buffers);
destruct_n(in_chunk.data(), in_chunk.size());
}
@ -330,272 +336,254 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
}(),
...);
}
} // namespace materialize_detail
template<typename... ParamTags> class CustomMF : public MultiFunction {
private:
std::function<void(IndexMask mask, MFParams params)> fn_;
MFSignature signature_;
template<typename ElementFn, typename ExecPreset, typename... ParamTags, size_t... I>
inline void execute_element_fn_as_multi_function(const ElementFn element_fn,
const ExecPreset exec_preset,
const IndexMask mask,
MFParams params,
TypeSequence<ParamTags...> /*param_tags*/,
std::index_sequence<I...> /*indices*/)
{
using TagsSequence = TypeSequence<ParamTags...>;
/* Load parameters from #MFParams. */
/* Contains `const GVArrayImpl *` for inputs and `T *` for outputs. */
const auto loaded_params = std::make_tuple([&]() {
/* Use `typedef` instead of `using` to work around a compiler bug. */
typedef ParamTags ParamTag;
typedef typename ParamTag::base_type T;
public:
template<typename ElementFn, typename ExecPreset = CustomMF_presets::Materialized>
CustomMF(const char *name,
ElementFn element_fn,
ExecPreset exec_preset = CustomMF_presets::Materialized())
{
MFSignatureBuilder signature{name};
add_signature_parameters(signature, std::make_index_sequence<TagsSequence::size()>());
signature_ = signature.build();
this->set_signature(&signature_);
if constexpr (ParamTag::category == MFParamCategory::SingleInput) {
return params.readonly_single_input(I).get_implementation();
}
else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) {
return static_cast<T *>(params.uninitialized_single_output(I).data());
}
else if constexpr (ParamTag::category == MFParamCategory::SingleMutable) {
return static_cast<T *>(params.single_mutable(I).data());
}
}()...);
fn_ = [element_fn, exec_preset](IndexMask mask, MFParams params) {
execute(
element_fn, exec_preset, mask, params, std::make_index_sequence<TagsSequence::size()>());
};
/* Try execute devirtualized if enabled and the input types allow it. */
bool executed_devirtualized = false;
if constexpr (ExecPreset::use_devirtualization) {
const auto devirtualizers = exec_preset.create_devirtualizers(
TypeSequence<ParamTags...>(), std::index_sequence<I...>(), mask, loaded_params);
executed_devirtualized = call_with_devirtualized_parameters(
devirtualizers, [&](auto &&...args) {
execute_array(TypeSequence<ParamTags...>(),
std::index_sequence<I...>(),
element_fn,
std::forward<decltype(args)>(args)...);
});
}
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*/)
{
/* Contains `const GVArrayImpl *` for inputs and `T *` for outputs. */
const auto loaded_params = std::make_tuple([&]() {
/* Use `typedef` instead of `using` to work around a compiler bug. */
typedef typename TagsSequence::template at_index<I> ParamTag;
typedef typename ParamTag::base_type T;
if constexpr (ParamTag::category == MFParamCategory::SingleInput) {
return params.readonly_single_input(I).get_implementation();
}
else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) {
return static_cast<T *>(params.uninitialized_single_output(I).data());
}
}()...);
/* First try devirtualized execution, since this is the most efficient. */
bool executed_devirtualized = false;
if constexpr (ExecPreset::use_devirtualization) {
const auto devirtualizers = exec_preset.create_devirtualizers(
TagsSequence(), std::index_sequence<I...>(), mask, loaded_params);
executed_devirtualized = call_with_devirtualized_parameters(
devirtualizers, [&](auto &&...args) {
detail::execute_array(TagsSequence(),
std::index_sequence<I...>(),
element_fn,
std::forward<decltype(args)>(args)...);
});
/* If devirtualized execution was disabled or not possible, use a fallback method which is
* slower but always works. */
if (!executed_devirtualized) {
/* The materialized method is most common because it avoids most virtual function overhead but
* still instantiates the function only once. */
if constexpr (ExecPreset::fallback_mode == exec_presets::FallbackMode::Materialized) {
execute_materialized(TypeSequence<ParamTags...>(),
std::index_sequence<I...>(),
element_fn,
mask,
loaded_params);
}
/* 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(
TagsSequence(), std::index_sequence<I...>(), element_fn, mask, loaded_params);
}
else {
detail::execute_array(
TagsSequence(), std::index_sequence<I...>(), element_fn, mask, [&]() {
/* Use `typedef` instead of `using` to work around a compiler bug. */
typedef typename TagsSequence::template at_index<I> ParamTag;
typedef typename ParamTag::base_type T;
if constexpr (ParamTag::category == MFParamCategory::SingleInput) {
const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
return GVArray(&varray_impl).typed<T>();
}
else if constexpr (ParamTag::category == MFParamCategory::SingleOutput) {
T *ptr = std::get<I>(loaded_params);
return ptr;
}
}()...);
}
else {
/* This fallback is slower because it uses virtual method calls for every element. */
execute_array(
TypeSequence<ParamTags...>(), std::index_sequence<I...>(), element_fn, mask, [&]() {
/* Use `typedef` instead of `using` to work around a compiler bug. */
typedef ParamTags ParamTag;
typedef typename ParamTag::base_type T;
if constexpr (ParamTag::category == MFParamCategory::SingleInput) {
const GVArrayImpl &varray_impl = *std::get<I>(loaded_params);
return GVArray(&varray_impl).typed<T>();
}
else if constexpr (ELEM(ParamTag::category,
MFParamCategory::SingleOutput,
MFParamCategory::SingleMutable)) {
T *ptr = std::get<I>(loaded_params);
return ptr;
}
}()...);
}
}
template<size_t... I>
static void add_signature_parameters(MFSignatureBuilder &signature,
std::index_sequence<I...> /* indices */)
{
(
/* Loop over all parameter types and add an entry for each in the signature. */
[&] {
/* Use `typedef` instead of `using` to work around a compiler bug. */
typedef typename TagsSequence::template at_index<I> ParamTag;
signature.add(ParamTag(), "");
}(),
...);
}
void call(IndexMask mask, MFParams params, MFContext /*context*/) const override
{
fn_(mask, params);
}
};
}
/**
* 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; });`
* `element_fn` is expected to return nothing and to have the following parameters:
* - For single-inputs: const value or reference.
* - For single-mutables: non-const reference.
* - For single-outputs: non-const pointer.
*/
template<typename In1, typename Out1>
class CustomMF_SI_SO : public CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>,
MFParamTag<MFParamCategory::SingleOutput, Out1>> {
public:
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 ElementFn, typename ExecPreset, typename... ParamTags>
inline auto build_multi_function_call_from_element_fn(const ElementFn element_fn,
const ExecPreset exec_preset,
TypeSequence<ParamTags...> /*param_tags*/)
{
return [element_fn, exec_preset](const IndexMask mask, MFParams params) {
execute_element_fn_as_multi_function(element_fn,
exec_preset,
mask,
params,
TypeSequence<ParamTags...>(),
std::make_index_sequence<sizeof...(ParamTags)>());
};
}
/**
* 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
* A multi function that just invokes the provided function in its #call method.
*/
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)
{
}
};
/**
* 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)
{
}
};
/**
* 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 input (SI) of type In4
* 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 CustomMF<MFParamTag<MFParamCategory::SingleInput, In1>,
MFParamTag<MFParamCategory::SingleInput, In2>,
MFParamTag<MFParamCategory::SingleInput, In3>,
MFParamTag<MFParamCategory::SingleInput, In4>,
MFParamTag<MFParamCategory::SingleOutput, Out1>> {
public:
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)
{
}
};
/**
* Generates a multi-function with the following parameters:
* 1. single mutable (SM) of type Mut1
*/
template<typename Mut1> class CustomMF_SM : public MultiFunction {
template<typename CallFn, typename... ParamTags> class CustomMF : public MultiFunction {
private:
using FunctionT = std::function<void(IndexMask, MutableSpan<Mut1>)>;
FunctionT function_;
MFSignature signature_;
CallFn call_fn_;
public:
CustomMF_SM(const char *name, FunctionT function) : function_(std::move(function))
CustomMF(const char *name, CallFn call_fn, TypeSequence<ParamTags...> /*param_tags*/)
: call_fn_(std::move(call_fn))
{
MFSignatureBuilder signature{name};
signature.single_mutable<Mut1>("Mut1");
/* Loop over all parameter types and add an entry for each in the signature. */
([&] { signature.add(ParamTags(), ""); }(), ...);
signature_ = signature.build();
this->set_signature(&signature_);
}
template<typename ElementFuncT>
CustomMF_SM(const char *name, ElementFuncT element_fn)
: CustomMF_SM(name, CustomMF_SM::create_function(element_fn))
{
}
template<typename ElementFuncT> static FunctionT create_function(ElementFuncT element_fn)
{
return [=](IndexMask mask, MutableSpan<Mut1> mut1) {
mask.to_best_mask_type([&](const auto &mask) {
for (const int64_t i : mask) {
element_fn(mut1[i]);
}
});
};
}
void call(IndexMask mask, MFParams params, MFContext /*context*/) const override
{
MutableSpan<Mut1> mut1 = params.single_mutable<Mut1>(0);
function_(mask, mut1);
call_fn_(mask, params);
}
};
template<typename Out, typename... In, typename ElementFn, typename ExecPreset>
inline auto build_multi_function_with_n_inputs_one_output(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset,
TypeSequence<In...> /*in_types*/)
{
constexpr auto param_tags = TypeSequence<MFParamTag<MFParamCategory::SingleInput, In>...,
MFParamTag<MFParamCategory::SingleOutput, Out>>();
auto call_fn = build_multi_function_call_from_element_fn(
[element_fn](const In &...in, Out *out) { new (out) Out(element_fn(in...)); },
exec_preset,
param_tags);
return CustomMF(name, call_fn, param_tags);
}
} // namespace detail
/** Build multi-function with 1 single-input and 1 single-output parameter. */
template<typename In1,
typename Out1,
typename ElementFn,
typename ExecPreset = exec_presets::Materialized>
inline auto SI1_SO(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset = exec_presets::Materialized())
{
return detail::build_multi_function_with_n_inputs_one_output<Out1>(
name, element_fn, exec_preset, TypeSequence<In1>());
}
/** Build multi-function with 2 single-input and 1 single-output parameter. */
template<typename In1,
typename In2,
typename Out1,
typename ElementFn,
typename ExecPreset = exec_presets::Materialized>
inline auto SI2_SO(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset = exec_presets::Materialized())
{
return detail::build_multi_function_with_n_inputs_one_output<Out1>(
name, element_fn, exec_preset, TypeSequence<In1, In2>());
}
/** Build multi-function with 3 single-input and 1 single-output parameter. */
template<typename In1,
typename In2,
typename In3,
typename Out1,
typename ElementFn,
typename ExecPreset = exec_presets::Materialized>
inline auto SI3_SO(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset = exec_presets::Materialized())
{
return detail::build_multi_function_with_n_inputs_one_output<Out1>(
name, element_fn, exec_preset, TypeSequence<In1, In2, In3>());
}
/** Build multi-function with 4 single-input and 1 single-output parameter. */
template<typename In1,
typename In2,
typename In3,
typename In4,
typename Out1,
typename ElementFn,
typename ExecPreset = exec_presets::Materialized>
inline auto SI4_SO(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset = exec_presets::Materialized())
{
return detail::build_multi_function_with_n_inputs_one_output<Out1>(
name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4>());
}
/** Build multi-function with 5 single-input and 1 single-output parameter. */
template<typename In1,
typename In2,
typename In3,
typename In4,
typename In5,
typename Out1,
typename ElementFn,
typename ExecPreset = exec_presets::Materialized>
inline auto SI5_SO(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset = exec_presets::Materialized())
{
return detail::build_multi_function_with_n_inputs_one_output<Out1>(
name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5>());
}
/** Build multi-function with 6 single-input and 1 single-output parameter. */
template<typename In1,
typename In2,
typename In3,
typename In4,
typename In5,
typename In6,
typename Out1,
typename ElementFn,
typename ExecPreset = exec_presets::Materialized>
inline auto SI6_SO(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset = exec_presets::Materialized())
{
return detail::build_multi_function_with_n_inputs_one_output<Out1>(
name, element_fn, exec_preset, TypeSequence<In1, In2, In3, In4, In5, In6>());
}
/** Build multi-function with 1 single-mutable parameter. */
template<typename Mut1, typename ElementFn, typename ExecPreset = exec_presets::AllSpanOrSingle>
inline auto SM(const char *name,
const ElementFn element_fn,
const ExecPreset exec_preset = exec_presets::AllSpanOrSingle())
{
constexpr auto param_tags = TypeSequence<MFParamTag<MFParamCategory::SingleMutable, Mut1>>();
auto call_fn = detail::build_multi_function_call_from_element_fn(
element_fn, exec_preset, param_tags);
return detail::CustomMF(name, call_fn, param_tags);
}
} // namespace blender::fn::build_mf
namespace blender::fn {
/**
* A multi-function that outputs the same value every time. The value is not owned by an instance
* of this function. If #make_value_copy is false, the caller is responsible for destructing and

View File

@ -519,8 +519,8 @@ GField make_field_constant_if_possible(GField field)
Field<bool> invert_boolean_field(const Field<bool> &field)
{
static CustomMF_SI_SO<bool, bool> not_fn{
"Not", [](bool a) { return !a; }, CustomMF_presets::AllSpanOrSingle()};
static auto not_fn = build_mf::SI1_SO<bool, bool>(
"Not", [](bool a) { return !a; }, build_mf::exec_presets::AllSpanOrSingle());
auto not_op = std::make_shared<FieldOperation>(FieldOperation(not_fn, {field}));
return Field<bool>(not_op);
}

View File

@ -104,11 +104,9 @@ TEST(field, InputAndFunction)
{
GField index_field{std::make_shared<IndexFieldInput>()};
std::unique_ptr<MultiFunction> add_fn = std::make_unique<CustomMF_SI_SI_SO<int, int, int>>(
"add", [](int a, int b) { return a + b; });
GField output_field{std::make_shared<FieldOperation>(
FieldOperation(std::move(add_fn), {index_field, index_field})),
0};
auto add_fn = build_mf::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; });
GField output_field{
std::make_shared<FieldOperation>(FieldOperation(add_fn, {index_field, index_field})), 0};
Array<int> result(10);
@ -129,16 +127,12 @@ TEST(field, TwoFunctions)
{
GField index_field{std::make_shared<IndexFieldInput>()};
std::unique_ptr<MultiFunction> add_fn = std::make_unique<CustomMF_SI_SI_SO<int, int, int>>(
"add", [](int a, int b) { return a + b; });
GField add_field{std::make_shared<FieldOperation>(
FieldOperation(std::move(add_fn), {index_field, index_field})),
0};
auto add_fn = build_mf::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; });
GField add_field{
std::make_shared<FieldOperation>(FieldOperation(add_fn, {index_field, index_field})), 0};
std::unique_ptr<MultiFunction> add_10_fn = std::make_unique<CustomMF_SI_SO<int, int>>(
"add_10", [](int a) { return a + 10; });
GField result_field{
std::make_shared<FieldOperation>(FieldOperation(std::move(add_10_fn), {add_field})), 0};
auto add_10_fn = build_mf::SI1_SO<int, int>("add_10", [](int a) { return a + 10; });
GField result_field{std::make_shared<FieldOperation>(FieldOperation(add_10_fn, {add_field})), 0};
Array<int> result(10);
@ -230,11 +224,9 @@ TEST(field, TwoFunctionsTwoOutputs)
Field<int> result_field_1{fn, 0};
Field<int> intermediate_field{fn, 1};
std::unique_ptr<MultiFunction> add_10_fn = std::make_unique<CustomMF_SI_SO<int, int>>(
"add_10", [](int a) { return a + 10; });
auto add_10_fn = build_mf::SI1_SO<int, int>("add_10", [](int a) { return a + 10; });
Field<int> result_field_2{
std::make_shared<FieldOperation>(FieldOperation(std::move(add_10_fn), {intermediate_field})),
0};
std::make_shared<FieldOperation>(FieldOperation(add_10_fn, {intermediate_field})), 0};
FieldContext field_context;
FieldEvaluator field_evaluator{field_context, &mask};

View File

@ -19,7 +19,7 @@ TEST(multi_function_procedure, ConstantOutput)
*/
CustomMF_Constant<int> constant_fn{5};
CustomMF_SI_SI_SO<int, int, int> add_fn{"Add", [](int a, int b) { return a + b; }};
auto add_fn = build_mf::SI2_SO<int, int, int>("Add", [](int a, int b) { return a + b; });
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
@ -56,8 +56,8 @@ TEST(multi_function_procedure, SimpleTest)
* }
*/
CustomMF_SI_SI_SO<int, int, int> add_fn{"add", [](int a, int b) { return a + b; }};
CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }};
auto add_fn = fn::build_mf::SI2_SO<int, int, int>("add", [](int a, int b) { return a + b; });
auto add_10_fn = fn::build_mf::SM<int>("add_10", [](int &a) { a += 10; });
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
@ -106,8 +106,8 @@ TEST(multi_function_procedure, BranchTest)
* }
*/
CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }};
CustomMF_SM<int> add_100_fn{"add_100", [](int &a) { a += 100; }};
auto add_10_fn = build_mf::SM<int>("add_10", [](int &a) { a += 10; });
auto add_100_fn = build_mf::SM<int>("add_100", [](int &a) { a += 100; });
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
@ -153,10 +153,10 @@ TEST(multi_function_procedure, EvaluateOne)
*/
int tot_evaluations = 0;
CustomMF_SI_SO<int, int> add_10_fn{"add_10", [&](int a) {
tot_evaluations++;
return a + 10;
}};
const auto add_10_fn = fn::build_mf::SI1_SO<int, int>("add_10", [&](int a) {
tot_evaluations++;
return a + 10;
});
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
@ -205,11 +205,11 @@ TEST(multi_function_procedure, SimpleLoop)
CustomMF_Constant<int> const_1_fn{1};
CustomMF_Constant<int> const_0_fn{0};
CustomMF_SI_SI_SO<int, int, bool> greater_or_equal_fn{"greater or equal",
[](int a, int b) { return a >= b; }};
CustomMF_SM<int> double_fn{"double", [](int &a) { a *= 2; }};
CustomMF_SM<int> add_1000_fn{"add 1000", [](int &a) { a += 1000; }};
CustomMF_SM<int> add_1_fn{"add 1", [](int &a) { a += 1; }};
auto greater_or_equal_fn = fn::build_mf::SI2_SO<int, int, bool>(
"greater or equal", [](int a, int b) { return a >= b; });
auto double_fn = build_mf::SM<int>("double", [](int &a) { a *= 2; });
auto add_1000_fn = build_mf::SM<int>("add 1000", [](int &a) { a += 1000; });
auto add_1_fn = build_mf::SM<int>("add 1", [](int &a) { a += 1; });
MFProcedure procedure;
MFProcedureBuilder builder{procedure};
@ -338,7 +338,7 @@ TEST(multi_function_procedure, BufferReuse)
* }
*/
CustomMF_SI_SO<int, int> add_10_fn{"add 10", [](int a) { return a + 10; }};
auto add_10_fn = build_mf::SI1_SO<int, int>("add 10", [](int a) { return a + 10; });
MFProcedure procedure;
MFProcedureBuilder builder{procedure};

View File

@ -149,98 +149,6 @@ TEST(multi_function, GenericAppendFunction)
EXPECT_EQ(vectors_ref[3][0], 1);
}
TEST(multi_function, CustomMF_SI_SO)
{
CustomMF_SI_SO<std::string, uint> fn("strlen",
[](const std::string &str) { return str.size(); });
Array<std::string> strings = {"hello", "world", "test", "another test"};
Array<uint> sizes(strings.size(), 0);
MFParamsBuilder params(fn, strings.size());
params.add_readonly_single_input(strings.as_span());
params.add_uninitialized_single_output(sizes.as_mutable_span());
MFContextBuilder context;
fn.call(IndexRange(strings.size()), params, context);
EXPECT_EQ(sizes[0], 5);
EXPECT_EQ(sizes[1], 5);
EXPECT_EQ(sizes[2], 4);
EXPECT_EQ(sizes[3], 12);
}
TEST(multi_function, CustomMF_SI_SI_SO)
{
CustomMF_SI_SI_SO<int, int, int> fn("mul", [](int a, int b) { return a * b; });
Array<int> values_a = {4, 6, 8, 9};
int value_b = 10;
Array<int> outputs(values_a.size(), -1);
MFParamsBuilder params(fn, values_a.size());
params.add_readonly_single_input(values_a.as_span());
params.add_readonly_single_input(&value_b);
params.add_uninitialized_single_output(outputs.as_mutable_span());
MFContextBuilder context;
fn.call({0, 1, 3}, params, context);
EXPECT_EQ(outputs[0], 40);
EXPECT_EQ(outputs[1], 60);
EXPECT_EQ(outputs[2], -1);
EXPECT_EQ(outputs[3], 90);
}
TEST(multi_function, CustomMF_SI_SI_SI_SO)
{
CustomMF_SI_SI_SI_SO<int, std::string, bool, uint> fn{
"custom",
[](int a, const std::string &b, bool c) { return uint(uint(a) + b.size() + uint(c)); }};
Array<int> values_a = {5, 7, 3, 8};
Array<std::string> values_b = {"hello", "world", "another", "test"};
Array<bool> values_c = {true, false, false, true};
Array<uint> outputs(values_a.size(), 0);
MFParamsBuilder params(fn, values_a.size());
params.add_readonly_single_input(values_a.as_span());
params.add_readonly_single_input(values_b.as_span());
params.add_readonly_single_input(values_c.as_span());
params.add_uninitialized_single_output(outputs.as_mutable_span());
MFContextBuilder context;
fn.call({1, 2, 3}, params, context);
EXPECT_EQ(outputs[0], 0);
EXPECT_EQ(outputs[1], 12);
EXPECT_EQ(outputs[2], 10);
EXPECT_EQ(outputs[3], 13);
}
TEST(multi_function, CustomMF_SM)
{
CustomMF_SM<std::string> fn("AddSuffix", [](std::string &value) { value += " test"; });
Array<std::string> values = {"a", "b", "c", "d", "e"};
MFParamsBuilder params(fn, values.size());
params.add_single_mutable(values.as_mutable_span());
MFContextBuilder context;
fn.call({1, 2, 3}, params, context);
EXPECT_EQ(values[0], "a");
EXPECT_EQ(values[1], "b test");
EXPECT_EQ(values[2], "c test");
EXPECT_EQ(values[3], "d test");
EXPECT_EQ(values[4], "e");
}
TEST(multi_function, CustomMF_Constant)
{
CustomMF_Constant<int> fn{42};

View File

@ -17,10 +17,10 @@ namespace blender::geometry {
static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field)
{
static fn::CustomMF_SI_SO<int, int> max_one_fn(
static auto max_one_fn = fn::build_mf::SI1_SO<int, int>(
"Clamp Above One",
[](int value) { return std::max(1, value); },
fn::CustomMF_presets::AllSpanOrSingle());
fn::build_mf::exec_presets::AllSpanOrSingle());
auto clamp_op = std::make_shared<fn::FieldOperation>(
fn::FieldOperation(max_one_fn, {count_field}));
@ -29,7 +29,7 @@ static fn::Field<int> get_count_input_max_one(const fn::Field<int> &count_field)
static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length_field)
{
static fn::CustomMF_SI_SI_SO<float, float, int> get_count_fn(
static auto get_count_fn = fn::build_mf::SI2_SO<float, float, int>(
"Length Input to Count",
[](const float curve_length, const float sample_length) {
/* Find the number of sampled segments by dividing the total length by
@ -37,7 +37,7 @@ static fn::Field<int> get_count_input_from_length(const fn::Field<float> &length
const int count = int(curve_length / sample_length) + 1;
return std::max(1, count);
},
fn::CustomMF_presets::AllSpanOrSingle());
fn::build_mf::exec_presets::AllSpanOrSingle());
auto get_count_op = std::make_shared<fn::FieldOperation>(fn::FieldOperation(
get_count_fn,

View File

@ -51,8 +51,8 @@ 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();
static auto exec_preset_fast = fn::build_mf::exec_presets::AllSpanOrSingle();
static auto exec_preset_slow = fn::build_mf::exec_presets::Materialized();
/* This is just an utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {
@ -118,8 +118,8 @@ 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();
static auto exec_preset_fast = fn::build_mf::exec_presets::AllSpanOrSingle();
static auto exec_preset_slow = fn::build_mf::exec_presets::Materialized();
/* This is just an utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {
@ -180,21 +180,21 @@ inline bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, Callback
switch (operation) {
case NODE_MATH_MULTIPLY_ADD:
return dispatch(fn::CustomMF_presets::AllSpanOrSingle(),
return dispatch(fn::build_mf::exec_presets::AllSpanOrSingle(),
[](float a, float b, float c) { return a * b + c; });
case NODE_MATH_COMPARE:
return dispatch(fn::CustomMF_presets::SomeSpanOrSingle<0, 1>(),
return dispatch(fn::build_mf::exec_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(fn::CustomMF_presets::SomeSpanOrSingle<0, 1>(),
return dispatch(fn::build_mf::exec_presets::SomeSpanOrSingle<0, 1>(),
[](float a, float b, float c) { return smoothminf(a, b, c); });
case NODE_MATH_SMOOTH_MAX:
return dispatch(fn::CustomMF_presets::SomeSpanOrSingle<0, 1>(),
return dispatch(fn::build_mf::exec_presets::SomeSpanOrSingle<0, 1>(),
[](float a, float b, float c) { return -smoothminf(-a, -b, c); });
case NODE_MATH_WRAP:
return dispatch(fn::CustomMF_presets::SomeSpanOrSingle<0>(),
return dispatch(fn::build_mf::exec_presets::SomeSpanOrSingle<0>(),
[](float a, float b, float c) { return wrapf(a, b, c); });
}
return false;
@ -214,8 +214,8 @@ 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();
static auto exec_preset_fast = fn::build_mf::exec_presets::AllSpanOrSingle();
static auto exec_preset_slow = fn::build_mf::exec_presets::Materialized();
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {
@ -269,7 +269,7 @@ inline bool try_dispatch_float_math_fl3_fl3_to_fl(const NodeVectorMathOperation
return false;
}
static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle();
static auto exec_preset_fast = fn::build_mf::exec_presets::AllSpanOrSingle();
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {
@ -302,8 +302,8 @@ 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();
static auto exec_preset_fast = fn::build_mf::exec_presets::AllSpanOrSingle();
static auto exec_preset_slow = fn::build_mf::exec_presets::Materialized();
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {
@ -341,7 +341,7 @@ inline bool try_dispatch_float_math_fl3_fl3_fl_to_fl3(const NodeVectorMathOperat
return false;
}
static auto exec_preset_slow = fn::CustomMF_presets::Materialized();
static auto exec_preset_slow = fn::build_mf::exec_presets::Materialized();
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {
@ -373,7 +373,7 @@ inline bool try_dispatch_float_math_fl3_to_fl(const NodeVectorMathOperation oper
return false;
}
static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle();
static auto exec_preset_fast = fn::build_mf::exec_presets::AllSpanOrSingle();
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {
@ -402,7 +402,7 @@ inline bool try_dispatch_float_math_fl3_fl_to_fl3(const NodeVectorMathOperation
return false;
}
static auto exec_preset_fast = fn::CustomMF_presets::AllSpanOrSingle();
static auto exec_preset_fast = fn::build_mf::exec_presets::AllSpanOrSingle();
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {
@ -433,8 +433,8 @@ 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();
static auto exec_preset_fast = fn::build_mf::exec_presets::AllSpanOrSingle();
static auto exec_preset_slow = fn::build_mf::exec_presets::Materialized();
/* This is just a utility function to keep the individual cases smaller. */
auto dispatch = [&](auto exec_preset, auto math_function) -> bool {

View File

@ -67,24 +67,25 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
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};
static auto exec_preset = fn::build_mf::exec_presets::AllSpanOrSingle();
static auto and_fn = fn::build_mf::SI2_SO<bool, bool, bool>(
"And", [](bool a, bool b) { return a && b; }, exec_preset);
static auto or_fn = fn::build_mf::SI2_SO<bool, bool, bool>(
"Or", [](bool a, bool b) { return a || b; }, exec_preset);
static auto not_fn = fn::build_mf::SI1_SO<bool, bool>(
"Not", [](bool a) { return !a; }, exec_preset);
static auto nand_fn = fn::build_mf::SI2_SO<bool, bool, bool>(
"Not And", [](bool a, bool b) { return !(a && b); }, exec_preset);
static auto nor_fn = fn::build_mf::SI2_SO<bool, bool, bool>(
"Nor", [](bool a, bool b) { return !(a || b); }, exec_preset);
static auto xnor_fn = fn::build_mf::SI2_SO<bool, bool, bool>(
"Equal", [](bool a, bool b) { return a == b; }, exec_preset);
static auto xor_fn = fn::build_mf::SI2_SO<bool, bool, bool>(
"Not Equal", [](bool a, bool b) { return a != b; }, exec_preset);
static auto imply_fn = fn::build_mf::SI2_SO<bool, bool, bool>(
"Imply", [](bool a, bool b) { return !a || b; }, exec_preset);
static auto nimply_fn = fn::build_mf::SI2_SO<bool, bool, bool>(
"Subtract", [](bool a, bool b) { return a && !b; }, exec_preset);
switch (bnode.custom1) {
case NODE_BOOLEAN_MATH_AND:

View File

@ -53,22 +53,22 @@ static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
const NodeCombSepColor &storage = node_storage(bnode);
static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> rgba_fn{
"RGB", [](float r, float g, float b, float a) { return ColorGeometry4f(r, g, b, a); }};
static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsva_fn{
static auto rgba_fn = fn::build_mf::SI4_SO<float, float, float, float, ColorGeometry4f>(
"RGB", [](float r, float g, float b, float a) { return ColorGeometry4f(r, g, b, a); });
static auto hsva_fn = fn::build_mf::SI4_SO<float, float, float, float, ColorGeometry4f>(
"HSV", [](float h, float s, float v, float a) {
ColorGeometry4f r_color;
hsv_to_rgb(h, s, v, &r_color.r, &r_color.g, &r_color.b);
r_color.a = a;
return r_color;
}};
static fn::CustomMF_SI_SI_SI_SI_SO<float, float, float, float, ColorGeometry4f> hsla_fn{
});
static auto hsla_fn = fn::build_mf::SI4_SO<float, float, float, float, ColorGeometry4f>(
"HSL", [](float h, float s, float l, float a) {
ColorGeometry4f color;
hsl_to_rgb(h, s, l, &color.r, &color.g, &color.b);
color.a = a;
return color;
}};
});
switch (storage.mode) {
case NODE_COMBSEP_COLOR_RGB:

View File

@ -168,77 +168,77 @@ static const fn::MultiFunction *get_multi_function(const 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>();
static auto exec_preset_all = fn::build_mf::exec_presets::AllSpanOrSingle();
static auto exec_preset_first_two = fn::build_mf::exec_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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<float, float, bool>(
"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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<float, float, bool>(
"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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<float, float, bool>(
"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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<float, float, bool>(
"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{
static auto fn = fn::build_mf::SI3_SO<float, float, float, bool>(
"Equal",
[](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_NOT_EQUAL:
static fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float, float, float, bool>(
"Not Equal",
[](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; },
exec_preset_first_two};
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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<int, int, bool>(
"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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<int, int, bool>(
"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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<int, int, bool>(
"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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<int, int, bool>(
"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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<int, int, bool>(
"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; }, exec_preset_all};
static auto fn = fn::build_mf::SI2_SO<int, int, bool>(
"Not Equal", [](int a, int b) { return a != b; }, exec_preset_all);
return &fn;
}
}
@ -248,38 +248,38 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
case NODE_COMPARE_LESS_THAN:
switch (data->mode) {
case NODE_COMPARE_MODE_AVERAGE: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Less Than - Average",
[](float3 a, float3 b) { return component_average(a) < component_average(b); },
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_MODE_DOT_PRODUCT: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Less Than - Dot Product",
[](float3 a, float3 b, float comp) { return math::dot(a, b) < comp; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_DIRECTION: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Less Than - Direction",
[](float3 a, float3 b, float angle) { return angle_v3v3(a, b) < angle; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_ELEMENT: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Less Than - Element-wise",
[](float3 a, float3 b) { return a.x < b.x && a.y < b.y && a.z < b.z; },
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_MODE_LENGTH: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Less Than - Length",
[](float3 a, float3 b) { return math::length(a) < math::length(b); },
exec_preset_all};
exec_preset_all);
return &fn;
}
}
@ -287,38 +287,38 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
case NODE_COMPARE_LESS_EQUAL:
switch (data->mode) {
case NODE_COMPARE_MODE_AVERAGE: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Less Equal - Average",
[](float3 a, float3 b) { return component_average(a) <= component_average(b); },
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_MODE_DOT_PRODUCT: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Less Equal - Dot Product",
[](float3 a, float3 b, float comp) { return math::dot(a, b) <= comp; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_DIRECTION: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Less Equal - Direction",
[](float3 a, float3 b, float angle) { return angle_v3v3(a, b) <= angle; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_ELEMENT: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Less Equal - Element-wise",
[](float3 a, float3 b) { return a.x <= b.x && a.y <= b.y && a.z <= b.z; },
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_MODE_LENGTH: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Less Equal - Length",
[](float3 a, float3 b) { return math::length(a) <= math::length(b); },
exec_preset_all};
exec_preset_all);
return &fn;
}
}
@ -326,38 +326,38 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
case NODE_COMPARE_GREATER_THAN:
switch (data->mode) {
case NODE_COMPARE_MODE_AVERAGE: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Greater Than - Average",
[](float3 a, float3 b) { return component_average(a) > component_average(b); },
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_MODE_DOT_PRODUCT: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Greater Than - Dot Product",
[](float3 a, float3 b, float comp) { return math::dot(a, b) > comp; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_DIRECTION: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Greater Than - Direction",
[](float3 a, float3 b, float angle) { return angle_v3v3(a, b) > angle; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_ELEMENT: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Greater Than - Element-wise",
[](float3 a, float3 b) { return a.x > b.x && a.y > b.y && a.z > b.z; },
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_MODE_LENGTH: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Greater Than - Length",
[](float3 a, float3 b) { return math::length(a) > math::length(b); },
exec_preset_all};
exec_preset_all);
return &fn;
}
}
@ -365,38 +365,38 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
case NODE_COMPARE_GREATER_EQUAL:
switch (data->mode) {
case NODE_COMPARE_MODE_AVERAGE: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Greater Equal - Average",
[](float3 a, float3 b) { return component_average(a) >= component_average(b); },
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_MODE_DOT_PRODUCT: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Greater Equal - Dot Product",
[](float3 a, float3 b, float comp) { return math::dot(a, b) >= comp; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_DIRECTION: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Greater Equal - Direction",
[](float3 a, float3 b, float angle) { return angle_v3v3(a, b) >= angle; },
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_ELEMENT: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Greater Equal - Element-wise",
[](float3 a, float3 b) { return a.x >= b.x && a.y >= b.y && a.z >= b.z; },
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_MODE_LENGTH: {
static fn::CustomMF_SI_SI_SO<float3, float3, bool> fn{
static auto fn = fn::build_mf::SI2_SO<float3, float3, bool>(
"Greater Equal - Length",
[](float3 a, float3 b) { return math::length(a) >= math::length(b); },
exec_preset_all};
exec_preset_all);
return &fn;
}
}
@ -404,49 +404,49 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
case NODE_COMPARE_EQUAL:
switch (data->mode) {
case NODE_COMPARE_MODE_AVERAGE: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Equal - Average",
[](float3 a, float3 b, float epsilon) {
return abs(component_average(a) - component_average(b)) <= epsilon;
},
exec_preset_first_two};
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{
static auto fn = fn::build_mf::SI4_SO<float3, float3, float, float, bool>(
"Equal - Dot Product",
[](float3 a, float3 b, float comp, float epsilon) {
return abs(math::dot(a, b) - comp) <= epsilon;
},
exec_preset_first_two};
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{
static auto fn = fn::build_mf::SI4_SO<float3, float3, float, float, bool>(
"Equal - Direction",
[](float3 a, float3 b, float angle, float epsilon) {
return abs(angle_v3v3(a, b) - angle) <= epsilon;
},
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_ELEMENT: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"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};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_LENGTH: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Equal - Length",
[](float3 a, float3 b, float epsilon) {
return abs(math::length(a) - math::length(b)) <= epsilon;
},
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
}
@ -454,49 +454,49 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
case NODE_COMPARE_NOT_EQUAL:
switch (data->mode) {
case NODE_COMPARE_MODE_AVERAGE: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Not Equal - Average",
[](float3 a, float3 b, float epsilon) {
return abs(component_average(a) - component_average(b)) > epsilon;
},
exec_preset_first_two};
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{
static auto fn = fn::build_mf::SI4_SO<float3, float3, float, float, bool>(
"Not Equal - Dot Product",
[](float3 a, float3 b, float comp, float epsilon) {
return abs(math::dot(a, b) - comp) >= epsilon;
},
exec_preset_first_two};
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{
static auto fn = fn::build_mf::SI4_SO<float3, float3, float, float, bool>(
"Not Equal - Direction",
[](float3 a, float3 b, float angle, float epsilon) {
return abs(angle_v3v3(a, b) - angle) > epsilon;
},
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_ELEMENT: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"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};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_MODE_LENGTH: {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, bool>(
"Not Equal - Length",
[](float3 a, float3 b, float epsilon) {
return abs(math::length(a) - math::length(b)) > epsilon;
},
exec_preset_first_two};
exec_preset_first_two);
return &fn;
}
}
@ -506,41 +506,41 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
case SOCK_RGBA:
switch (data->operation) {
case NODE_COMPARE_EQUAL: {
static fn::CustomMF_SI_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<ColorGeometry4f, ColorGeometry4f, float, bool>(
"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};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_NOT_EQUAL: {
static fn::CustomMF_SI_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, float, bool> fn{
static auto fn = fn::build_mf::SI3_SO<ColorGeometry4f, ColorGeometry4f, float, bool>(
"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};
exec_preset_first_two);
return &fn;
}
case NODE_COMPARE_COLOR_BRIGHTER: {
static fn::CustomMF_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, bool> fn{
static auto fn = fn::build_mf::SI2_SO<ColorGeometry4f, ColorGeometry4f, bool>(
"Brighter",
[](ColorGeometry4f a, ColorGeometry4f b) {
return rgb_to_grayscale(a) > rgb_to_grayscale(b);
},
exec_preset_all};
exec_preset_all);
return &fn;
}
case NODE_COMPARE_COLOR_DARKER: {
static fn::CustomMF_SI_SI_SO<ColorGeometry4f, ColorGeometry4f, bool> fn{
static auto fn = fn::build_mf::SI2_SO<ColorGeometry4f, ColorGeometry4f, bool>(
"Darker",
[](ColorGeometry4f a, ColorGeometry4f b) {
return rgb_to_grayscale(a) < rgb_to_grayscale(b);
},
exec_preset_all};
exec_preset_all);
return &fn;
}
}
@ -548,13 +548,13 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
case SOCK_STRING:
switch (data->operation) {
case NODE_COMPARE_EQUAL: {
static fn::CustomMF_SI_SI_SO<std::string, std::string, bool> fn{
"Equal", [](std::string a, std::string b) { return a == b; }};
static auto fn = fn::build_mf::SI2_SO<std::string, std::string, bool>(
"Equal", [](std::string a, std::string b) { return a == b; });
return &fn;
}
case NODE_COMPARE_NOT_EQUAL: {
static fn::CustomMF_SI_SI_SO<std::string, std::string, bool> fn{
"Not Equal", [](std::string a, std::string b) { return a != b; }};
static auto fn = fn::build_mf::SI2_SO<std::string, std::string, bool>(
"Not Equal", [](std::string a, std::string b) { return a != b; });
return &fn;
}
}

View File

@ -38,15 +38,15 @@ static void node_label(const bNodeTree * /*tree*/, const bNode *node, char *labe
static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
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};
static auto exec_preset = fn::build_mf::exec_presets::AllSpanOrSingle();
static auto round_fn = fn::build_mf::SI1_SO<float, int>(
"Round", [](float a) { return int(round(a)); }, exec_preset);
static auto floor_fn = fn::build_mf::SI1_SO<float, int>(
"Floor", [](float a) { return int(floor(a)); }, exec_preset);
static auto ceil_fn = fn::build_mf::SI1_SO<float, int>(
"Ceiling", [](float a) { return int(ceil(a)); }, exec_preset);
static auto trunc_fn = fn::build_mf::SI1_SO<float, int>(
"Truncate", [](float a) { return int(trunc(a)); }, exec_preset);
switch (static_cast<FloatToIntRoundingMode>(bnode.custom1)) {
case FN_NODE_FLOAT_TO_INT_ROUND:

View File

@ -141,64 +141,49 @@ static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
switch (data_type) {
case CD_PROP_FLOAT3: {
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>()};
static auto fn = fn::build_mf::SI4_SO<float3, float3, int, int, float3>(
"Random Vector",
[](float3 min_value, float3 max_value, int id, int seed) -> float3 {
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);
return float3(x, y, z) * (max_value - min_value) + min_value;
},
fn::build_mf::exec_presets::SomeSpanOrSingle<2>());
builder.set_matching_fn(fn);
break;
}
case CD_PROP_FLOAT: {
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>()};
static auto fn = fn::build_mf::SI4_SO<float, float, int, int, float>(
"Random Float",
[](float min_value, float max_value, int id, int seed) -> float {
const float value = noise::hash_to_float(seed, id);
return value * (max_value - min_value) + min_value;
},
fn::build_mf::exec_presets::SomeSpanOrSingle<2>());
builder.set_matching_fn(fn);
break;
}
case CD_PROP_INT32: {
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>()};
static auto fn = fn::build_mf::SI4_SO<int, int, int, int, int>(
"Random Int",
[](int min_value, int max_value, int id, int seed) -> int {
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). */
return floor(value * (max_value + 1 - min_value) + min_value);
},
fn::build_mf::exec_presets::SomeSpanOrSingle<2>());
builder.set_matching_fn(fn);
break;
}
case CD_PROP_BOOL: {
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>()};
static auto fn = fn::build_mf::SI3_SO<float, int, int, bool>(
"Random Bool",
[](float probability, int id, int seed) -> bool {
return noise::hash_to_float(id, seed) <= probability;
},
fn::build_mf::exec_presets::SomeSpanOrSingle<1>());
builder.set_matching_fn(fn);
break;
}

View File

@ -30,10 +30,12 @@ static std::string replace_all(const StringRefNull str,
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static fn::CustomMF_SI_SI_SI_SO<std::string, std::string, std::string, std::string> substring_fn{
"Replace", [](const std::string &str, const std::string &find, const std::string &replace) {
return replace_all(str, find, replace);
}};
static auto substring_fn =
fn::build_mf::SI3_SO<std::string, std::string, std::string, std::string>(
"Replace",
[](const std::string &str, const std::string &find, const std::string &replace) {
return replace_all(str, find, replace);
});
builder.set_matching_fn(&substring_fn);
}

View File

@ -54,7 +54,7 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static fn::CustomMF_SI_SI_SO<float3, float3, float3> obj_euler_rot{
static auto obj_euler_rot = fn::build_mf::SI2_SO<float3, float3, float3>(
"Rotate Euler by Euler/Object", [](const float3 &input, const float3 &rotation) {
float input_mat[3][3];
eul_to_mat3(input_mat, input);
@ -65,8 +65,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &bnode)
float3 result;
mat3_to_eul(result, mat_res);
return result;
}};
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> obj_AA_rot{
});
static auto obj_AA_rot = fn::build_mf::SI3_SO<float3, float3, float, float3>(
"Rotate Euler by AxisAngle/Object",
[](const float3 &input, const float3 &axis, float angle) {
float input_mat[3][3];
@ -78,8 +78,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &bnode)
float3 result;
mat3_to_eul(result, mat_res);
return result;
}};
static fn::CustomMF_SI_SI_SO<float3, float3, float3> local_euler_rot{
});
static auto local_euler_rot = fn::build_mf::SI2_SO<float3, float3, float3>(
"Rotate Euler by Euler/Local", [](const float3 &input, const float3 &rotation) {
float input_mat[3][3];
eul_to_mat3(input_mat, input);
@ -90,8 +90,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &bnode)
float3 result;
mat3_to_eul(result, mat_res);
return result;
}};
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> local_AA_rot{
});
static auto local_AA_rot = fn::build_mf::SI3_SO<float3, float3, float, float3>(
"Rotate Euler by AxisAngle/Local", [](const float3 &input, const float3 &axis, float angle) {
float input_mat[3][3];
eul_to_mat3(input_mat, input);
@ -102,14 +102,18 @@ static const fn::MultiFunction *get_multi_function(const bNode &bnode)
float3 result;
mat3_to_eul(result, mat_res);
return result;
}};
});
short type = bnode.custom1;
short space = bnode.custom2;
if (type == FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE) {
return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_AA_rot : &local_AA_rot;
return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ?
static_cast<const MultiFunction *>(&obj_AA_rot) :
&local_AA_rot;
}
if (type == FN_NODE_ROTATE_EULER_TYPE_EULER) {
return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_euler_rot : &local_euler_rot;
return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ?
static_cast<const MultiFunction *>(&obj_euler_rot) :
&local_euler_rot;
}
BLI_assert_unreachable();
return nullptr;

View File

@ -16,13 +16,13 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static fn::CustomMF_SI_SI_SI_SO<std::string, int, int, std::string> slice_fn{
static auto slice_fn = fn::build_mf::SI3_SO<std::string, int, int, std::string>(
"Slice", [](const std::string &str, int a, int b) {
const int len = BLI_strlen_utf8(str.c_str());
const int start = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a, 0, len));
const int end = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a + b, 0, len));
return str.substr(start, std::max<int>(end - start, 0));
}};
});
builder.set_matching_fn(&slice_fn);
}

View File

@ -16,8 +16,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static fn::CustomMF_SI_SO<std::string, int> str_len_fn{
"String Length", [](const std::string &a) { return BLI_strlen_utf8(a.c_str()); }};
static auto str_len_fn = fn::build_mf::SI1_SO<std::string, int>(
"String Length", [](const std::string &a) { return BLI_strlen_utf8(a.c_str()); });
builder.set_matching_fn(&str_len_fn);
}

View File

@ -14,12 +14,12 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static fn::CustomMF_SI_SI_SO<float, int, std::string> to_str_fn{
static auto to_str_fn = fn::build_mf::SI2_SO<float, int, std::string>(
"Value To String", [](float a, int b) {
std::stringstream stream;
stream << std::fixed << std::setprecision(std::max(0, b)) << a;
return stream.str();
}};
});
builder.set_matching_fn(&to_str_fn);
}

View File

@ -1325,10 +1325,10 @@ 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{
static auto multiply_fn = fn::build_mf::SI2_SO<float3, float, float3>(
"Scale",
[](const float3 &offset, const float scale) { return offset * scale; },
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_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)};

View File

@ -121,10 +121,10 @@ 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(
static auto max_zero_fn = fn::build_mf::SI1_SO<float, float>(
__func__,
[](float value) { return std::max(0.0f, value); },
fn::CustomMF_presets::AllSpanOrSingle());
fn::build_mf::exec_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);

View File

@ -171,10 +171,10 @@ 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);
static fn::CustomMF_SI_SI_SI_SO<bool, T, T, T> switch_fn{
static auto switch_fn = fn::build_mf::SI3_SO<bool, T, T, T>(
"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),

View File

@ -44,17 +44,17 @@ static int gpu_shader_clamp(GPUMaterial *mat,
static void sh_node_clamp_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> minmax_fn{
static auto minmax_fn = fn::build_mf::SI3_SO<float, float, float, float>(
"Clamp (Min Max)",
[](float value, float min, float max) { return std::min(std::max(value, min), max); }};
static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> range_fn{
[](float value, float min, float max) { return std::min(std::max(value, min), max); });
static auto range_fn = fn::build_mf::SI3_SO<float, float, float, float>(
"Clamp (Range)", [](float value, float a, float b) {
if (a < b) {
return clamp_f(value, a, b);
}
return clamp_f(value, b, a);
}};
});
int clamp_type = builder.node().custom1;
if (clamp_type == NODE_CLAMP_MINMAX) {

View File

@ -233,103 +233,74 @@ static float3 clamp_range(const float3 value, const float3 min, const float3 max
template<bool Clamp> static auto build_float_linear()
{
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>>{
return fn::build_mf::SI5_SO<float, float, float, float, float, 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) {
[](float value, float from_min, float from_max, float to_min, float to_max) -> float {
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;
return result;
},
fn::CustomMF_presets::SomeSpanOrSingle<0>()};
fn::build_mf::exec_presets::SomeSpanOrSingle<0>());
}
template<bool Clamp> static auto build_float_stepped()
{
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>>{
return fn::build_mf::SI6_SO<float, float, float, float, float, float, 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 value, float from_min, float from_max, float to_min, float to_max, float steps)
-> float {
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;
return result;
},
fn::CustomMF_presets::SomeSpanOrSingle<0>()};
fn::build_mf::exec_presets::SomeSpanOrSingle<0>());
}
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>>{
return fn::build_mf::SI5_SO<float3, float3, float3, float3, float3, 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) {
const float3 &to_max) -> float3 {
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;
return result;
},
fn::CustomMF_presets::SomeSpanOrSingle<0>()};
fn::build_mf::exec_presets::SomeSpanOrSingle<0>());
}
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>>{
return fn::build_mf::SI6_SO<float3, float3, float3, float3, float3, float3, 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) {
const float3 &steps) -> float3 {
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;
return result;
},
fn::CustomMF_presets::SomeSpanOrSingle<0>()};
fn::build_mf::exec_presets::SomeSpanOrSingle<0>());
}
static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &builder)
@ -364,48 +335,36 @@ static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &bui
break;
}
case NODE_MAP_RANGE_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>()};
static auto fn = fn::build_mf::SI5_SO<float3, float3, float3, float3, float3, float3>(
"Vector Map Range Smoothstep",
[](const float3 &value,
const float3 &from_min,
const float3 &from_max,
const float3 &to_min,
const float3 &to_max) -> float3 {
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);
return factor * (to_max - to_min) + to_min;
},
fn::build_mf::exec_presets::SomeSpanOrSingle<0>());
builder.set_matching_fn(fn);
break;
}
case NODE_MAP_RANGE_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>()};
static auto fn = fn::build_mf::SI5_SO<float3, float3, float3, float3, float3, float3>(
"Vector Map Range Smootherstep",
[](const float3 &value,
const float3 &from_min,
const float3 &from_max,
const float3 &to_min,
const float3 &to_max) -> float3 {
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);
return factor * (to_max - to_min) + to_min;
},
fn::build_mf::exec_presets::SomeSpanOrSingle<0>());
builder.set_matching_fn(fn);
break;
}
@ -438,48 +397,30 @@ static void sh_node_map_range_build_multi_function(NodeMultiFunctionBuilder &bui
break;
}
case NODE_MAP_RANGE_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>()};
static auto fn = fn::build_mf::SI5_SO<float, float, float, float, float, float>(
"Map Range Smoothstep",
[](float value, float from_min, float from_max, float to_min, float to_max)
-> float {
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);
return to_min + factor * (to_max - to_min);
},
fn::build_mf::exec_presets::SomeSpanOrSingle<0>());
builder.set_matching_fn(fn);
break;
}
case NODE_MAP_RANGE_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>()};
static auto fn = fn::build_mf::SI5_SO<float, float, float, float, float, float>(
"Map Range Smoothstep",
[](float value, float from_min, float from_max, float to_min, float to_max)
-> float {
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);
return to_min + factor * (to_max - to_min);
},
fn::build_mf::exec_presets::SomeSpanOrSingle<0>());
builder.set_matching_fn(fn);
break;
}

View File

@ -109,8 +109,8 @@ static const fn::MultiFunction *get_base_multi_function(const bNode &node)
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};
static auto fn = fn::build_mf::SI1_SO<float, float>(
info.title_case_name.c_str(), function, devi_fn);
base_fn = &fn;
});
if (base_fn != nullptr) {
@ -119,8 +119,8 @@ static const fn::MultiFunction *get_base_multi_function(const bNode &node)
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};
static auto fn = fn::build_mf::SI2_SO<float, float, float>(
info.title_case_name.c_str(), function, devi_fn);
base_fn = &fn;
});
if (base_fn != nullptr) {
@ -129,8 +129,8 @@ static const fn::MultiFunction *get_base_multi_function(const bNode &node)
try_dispatch_float_math_fl_fl_fl_to_fl(
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, devi_fn};
static auto fn = fn::build_mf::SI3_SO<float, float, float, float>(
info.title_case_name.c_str(), function, devi_fn);
base_fn = &fn;
});
if (base_fn != nullptr) {

View File

@ -412,51 +412,51 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
switch (data->data_type) {
case SOCK_FLOAT: {
if (clamp_factor) {
static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
static auto fn = fn::build_mf::SI3_SO<float, float, float, float>(
"Clamp Mix Float", [](float t, const float a, const float b) {
return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
}};
});
return &fn;
}
else {
static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
static auto fn = fn::build_mf::SI3_SO<float, float, float, float>(
"Mix Float", [](const float t, const float a, const float b) {
return math::interpolate(a, b, t);
}};
});
return &fn;
}
}
case SOCK_VECTOR: {
if (clamp_factor) {
if (uniform_factor) {
static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float, float3, float3, float3>(
"Clamp Mix Vector", [](const float t, const float3 a, const float3 b) {
return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
}};
});
return &fn;
}
else {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float3, float3>(
"Clamp Mix Vector Non Uniform", [](float3 t, const float3 a, const float3 b) {
t = math::clamp(t, 0.0f, 1.0f);
return a * (float3(1.0f) - t) + b * t;
}};
});
return &fn;
}
}
else {
if (uniform_factor) {
static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float, float3, float3, float3>(
"Mix Vector", [](const float t, const float3 a, const float3 b) {
return math::interpolate(a, b, t);
}};
});
return &fn;
}
else {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float3, float3>(
"Mix Vector Non Uniform", [](const float3 t, const float3 a, const float3 b) {
return a * (float3(1.0f) - t) + b * t;
}};
});
return &fn;
}
}

View File

@ -108,8 +108,8 @@ static int gpu_shader_combrgb(GPUMaterial *mat,
static void sh_node_combrgb_build_multi_function(NodeMultiFunctionBuilder &builder)
{
static fn::CustomMF_SI_SI_SI_SO<float, float, float, ColorGeometry4f> fn{
"Combine RGB", [](float r, float g, float b) { return ColorGeometry4f(r, g, b, 1.0f); }};
static auto fn = fn::build_mf::SI3_SO<float, float, float, ColorGeometry4f>(
"Combine RGB", [](float r, float g, float b) { return ColorGeometry4f(r, g, b, 1.0f); });
builder.set_matching_fn(fn);
}

View File

@ -125,10 +125,10 @@ 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{
static auto fn = fn::build_mf::SI3_SO<float, float, float, float3>(
"Combine Vector",
[](float x, float y, float z) { return float3(x, y, z); },
fn::CustomMF_presets::AllSpanOrSingle()};
fn::build_mf::exec_presets::AllSpanOrSingle());
builder.set_matching_fn(fn);
}

View File

@ -233,8 +233,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
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};
static auto fn = fn::build_mf::SI2_SO<float3, float3, float3>(
info.title_case_name.c_str(), function, exec_preset);
multi_fn = &fn;
});
if (multi_fn != nullptr) {
@ -243,8 +243,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
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, exec_preset};
static auto fn = fn::build_mf::SI3_SO<float3, float3, float3, float3>(
info.title_case_name.c_str(), function, exec_preset);
multi_fn = &fn;
});
if (multi_fn != nullptr) {
@ -253,8 +253,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
try_dispatch_float_math_fl3_fl3_fl_to_fl3(
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, exec_preset};
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, float3>(
info.title_case_name.c_str(), function, exec_preset);
multi_fn = &fn;
});
if (multi_fn != nullptr) {
@ -263,8 +263,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
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};
static auto fn = fn::build_mf::SI2_SO<float3, float3, float>(
info.title_case_name.c_str(), function, exec_preset);
multi_fn = &fn;
});
if (multi_fn != nullptr) {
@ -273,8 +273,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
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};
static auto fn = fn::build_mf::SI2_SO<float3, float, float3>(
info.title_case_name.c_str(), function, exec_preset);
multi_fn = &fn;
});
if (multi_fn != nullptr) {
@ -283,8 +283,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
try_dispatch_float_math_fl3_to_fl3(
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};
static auto fn = fn::build_mf::SI1_SO<float3, float3>(
info.title_case_name.c_str(), function, exec_preset);
multi_fn = &fn;
});
if (multi_fn != nullptr) {
@ -293,8 +293,8 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
try_dispatch_float_math_fl3_to_fl(
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};
static auto fn = fn::build_mf::SI1_SO<float3, float>(
info.title_case_name.c_str(), function, exec_preset);
multi_fn = &fn;
});
if (multi_fn != nullptr) {

View File

@ -104,77 +104,77 @@ static const fn::MultiFunction *get_multi_function(const bNode &node)
switch (mode) {
case NODE_VECTOR_ROTATE_TYPE_AXIS: {
if (invert) {
static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{
static auto fn = fn::build_mf::SI4_SO<float3, float3, float3, float, float3>(
"Rotate Axis",
[](const float3 &in, const float3 &center, const float3 &axis, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
});
return &fn;
}
static fn::CustomMF_SI_SI_SI_SI_SO<float3, float3, float3, float, float3> fn{
static auto fn = fn::build_mf::SI4_SO<float3, float3, float3, float, float3>(
"Rotate Axis",
[](const float3 &in, const float3 &center, const float3 &axis, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
});
return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_X: {
float3 axis = float3(1.0f, 0.0f, 0.0f);
if (invert) {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, float3>(
"Rotate X-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
});
return &fn;
}
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, float3>(
"Rotate X-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
});
return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: {
float3 axis = float3(0.0f, 1.0f, 0.0f);
if (invert) {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, float3>(
"Rotate Y-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
});
return &fn;
}
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, float3>(
"Rotate Y-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
});
return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: {
float3 axis = float3(0.0f, 0.0f, 1.0f);
if (invert) {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, float3>(
"Rotate Z-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, -angle);
}};
});
return &fn;
}
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float, float3>(
"Rotate Z-Axis", [=](const float3 &in, const float3 &center, float angle) {
return sh_node_vector_rotate_around_axis(in, center, axis, angle);
}};
});
return &fn;
}
case NODE_VECTOR_ROTATE_TYPE_EULER_XYZ: {
if (invert) {
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float3, float3>(
"Rotate Euler", [](const float3 &in, const float3 &center, const float3 &rotation) {
return sh_node_vector_rotate_euler(in, center, rotation, true);
}};
});
return &fn;
}
static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
static auto fn = fn::build_mf::SI3_SO<float3, float3, float3, float3>(
"Rotate Euler", [](const float3 &in, const float3 &center, const float3 &rotation) {
return sh_node_vector_rotate_euler(in, center, rotation, false);
}};
});
return &fn;
}
default: