/** * Generates a multi-function with the following parameters: * 1. single input (SI) of type In1 * 2. single input (SI) of type In2 * 3. single output (SO) of type Out1 */ template class CustomMF_SI_SI_SO : public MultiFunction { private: using FunctionT = std::function &, const VArray &, MutableSpan)>; FunctionT function_; MFSignature signature_; public: CustomMF_SI_SI_SO(const char *name, FunctionT function) : function_(std::move(function)) { MFSignatureBuilder signature{name}; signature.single_input("In1"); signature.single_input("In2"); signature.single_output("Out1"); signature_ = signature.build(); this->set_signature(&signature_); } template CustomMF_SI_SI_SO(const char *name, ElementFuncT element_fn) : CustomMF_SI_SI_SO(name, CustomMF_SI_SI_SO::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, const VArray &in1, const VArray &in2, MutableSpan out1) { /* Devirtualization results in a 2-3x speedup for some simple functions. */ devirtualize_varray2(in1, in2, [&](const auto &in1, const auto &in2) { mask.to_best_mask_type( [&](const auto &mask) { execute_SI_SI_SO(element_fn, mask, in1, in2, out1.data()); }); }); }; } template BLI_NOINLINE static void execute_SI_SI_SO(const ElementFuncT &element_fn, MaskT mask, const In1Array &in1, const In2Array &in2, Out1 *__restrict r_out) { for (const int64_t i : mask) { new (r_out + i) Out1(element_fn(in1[i], in2[i])); } } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { const VArray &in1 = params.readonly_single_input(0); const VArray &in2 = params.readonly_single_input(1); MutableSpan out1 = params.uninitialized_single_output(2); function_(mask, in1, in2, out1); } };