/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once /** \file * \ingroup fn * * This file contains several utilities to create multi-functions with less redundant code. */ #include #include "FN_multi_function.hh" namespace blender::fn { /** * 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 fn("add 10", [](int value) { return value + 10; }); */ template class CustomMF_SI_SO : public MultiFunction { private: using FunctionT = std::function &, MutableSpan)>; FunctionT function_; MFSignature signature_; public: CustomMF_SI_SO(const char *name, FunctionT function) : function_(std::move(function)) { MFSignatureBuilder signature{name}; signature.single_input("In1"); signature.single_output("Out1"); signature_ = signature.build(); this->set_signature(&signature_); } template CustomMF_SI_SO(const char *name, ElementFuncT element_fn) : CustomMF_SI_SO(name, CustomMF_SI_SO::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, const VArray &in1, MutableSpan out1) { /* Devirtualization results in a 2-3x speedup for some simple functions. */ devirtualize_varray(in1, [&](const auto &in1) { mask.foreach_index( [&](int i) { new (static_cast(&out1[i])) Out1(element_fn(in1[i])); }); }); }; } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { const VArray &in1 = params.readonly_single_input(0); MutableSpan out1 = params.uninitialized_single_output(1); function_(mask, in1, out1); } }; /** * 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.foreach_index( [&](int i) { new (static_cast(&out1[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); } }; /** * 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 class CustomMF_SI_SI_SI_SO : public MultiFunction { private: using FunctionT = std::function &, const VArray &, const VArray &, MutableSpan)>; FunctionT function_; MFSignature signature_; public: CustomMF_SI_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_input("In3"); signature.single_output("Out1"); signature_ = signature.build(); this->set_signature(&signature_); } template CustomMF_SI_SI_SI_SO(const char *name, ElementFuncT element_fn) : CustomMF_SI_SI_SI_SO(name, CustomMF_SI_SI_SI_SO::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, const VArray &in1, const VArray &in2, const VArray &in3, MutableSpan out1) { mask.foreach_index([&](int i) { new (static_cast(&out1[i])) Out1(element_fn(in1[i], in2[i], in3[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); const VArray &in3 = params.readonly_single_input(2); MutableSpan out1 = params.uninitialized_single_output(3); function_(mask, in1, in2, in3, out1); } }; /** * 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 class CustomMF_SI_SI_SI_SI_SO : public MultiFunction { private: using FunctionT = std::function &, const VArray &, const VArray &, const VArray &, MutableSpan)>; FunctionT function_; MFSignature signature_; public: CustomMF_SI_SI_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_input("In3"); signature.single_input("In4"); signature.single_output("Out1"); signature_ = signature.build(); this->set_signature(&signature_); } template CustomMF_SI_SI_SI_SI_SO(const char *name, ElementFuncT element_fn) : CustomMF_SI_SI_SI_SI_SO(name, CustomMF_SI_SI_SI_SI_SO::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, const VArray &in1, const VArray &in2, const VArray &in3, const VArray &in4, MutableSpan out1) { mask.foreach_index([&](int i) { new (static_cast(&out1[i])) Out1(element_fn(in1[i], in2[i], in3[i], in4[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); const VArray &in3 = params.readonly_single_input(2); const VArray &in4 = params.readonly_single_input(3); MutableSpan out1 = params.uninitialized_single_output(4); function_(mask, in1, in2, in3, in4, out1); } }; /** * Generates a multi-function with the following parameters: * 1. single mutable (SM) of type Mut1 */ template class CustomMF_SM : public MultiFunction { private: using FunctionT = std::function)>; FunctionT function_; MFSignature signature_; public: CustomMF_SM(const char *name, FunctionT function) : function_(std::move(function)) { MFSignatureBuilder signature{name}; signature.single_mutable("Mut1"); signature_ = signature.build(); this->set_signature(&signature_); } template CustomMF_SM(const char *name, ElementFuncT element_fn) : CustomMF_SM(name, CustomMF_SM::create_function(element_fn)) { } template static FunctionT create_function(ElementFuncT element_fn) { return [=](IndexMask mask, MutableSpan mut1) { mask.foreach_index([&](int i) { element_fn(mut1[i]); }); }; } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { MutableSpan mut1 = params.single_mutable(0); function_(mask, mut1); } }; /** * Generates a multi-function that converts between two types. */ template class CustomMF_Convert : public MultiFunction { public: CustomMF_Convert() { static MFSignature signature = create_signature(); this->set_signature(&signature); } static MFSignature create_signature() { static std::string name = CPPType::get().name() + " to " + CPPType::get().name(); MFSignatureBuilder signature{name.c_str()}; signature.single_input("Input"); signature.single_output("Output"); return signature.build(); } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { const VArray &inputs = params.readonly_single_input(0); MutableSpan outputs = params.uninitialized_single_output(1); for (int64_t i : mask) { new (static_cast(&outputs[i])) To(inputs[i]); } } }; /** * 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 * freeing the value. */ class CustomMF_GenericConstant : public MultiFunction { private: const CPPType &type_; const void *value_; MFSignature signature_; bool owns_value_; template friend class CustomMF_Constant; public: CustomMF_GenericConstant(const CPPType &type, const void *value, bool make_value_copy); ~CustomMF_GenericConstant(); void call(IndexMask mask, MFParams params, MFContext context) const override; uint64_t hash() const override; bool equals(const MultiFunction &other) const override; }; /** * A multi-function that outputs the same array every time. The array is not owned by in instance * of this function. The caller is responsible for destructing and freeing the values. */ class CustomMF_GenericConstantArray : public MultiFunction { private: GSpan array_; MFSignature signature_; public: CustomMF_GenericConstantArray(GSpan array); void call(IndexMask mask, MFParams params, MFContext context) const override; }; /** * Generates a multi-function that outputs a constant value. */ template class CustomMF_Constant : public MultiFunction { private: T value_; MFSignature signature_; public: template CustomMF_Constant(U &&value) : value_(std::forward(value)) { MFSignatureBuilder signature{"Constant"}; signature.single_output("Value"); signature_ = signature.build(); this->set_signature(&signature_); } void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override { MutableSpan output = params.uninitialized_single_output(0); mask.foreach_index([&](int i) { new (&output[i]) T(value_); }); } uint64_t hash() const override { return get_default_hash(value_); } bool equals(const MultiFunction &other) const override { const CustomMF_Constant *other1 = dynamic_cast(&other); if (other1 != nullptr) { return value_ == other1->value_; } const CustomMF_GenericConstant *other2 = dynamic_cast( &other); if (other2 != nullptr) { const CPPType &type = CPPType::get(); if (type == other2->type_) { return type.is_equal_or_false(static_cast(&value_), other2->value_); } } return false; } }; class CustomMF_DefaultOutput : public MultiFunction { private: int output_amount_; MFSignature signature_; public: CustomMF_DefaultOutput(Span input_types, Span output_types); void call(IndexMask mask, MFParams params, MFContext context) const override; }; class CustomMF_GenericCopy : public MultiFunction { private: MFSignature signature_; public: CustomMF_GenericCopy(MFDataType data_type); void call(IndexMask mask, MFParams params, MFContext context) const override; }; } // namespace blender::fn