diff options
author | Jacques Lucke <jacques@blender.org> | 2021-09-14 15:52:44 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2021-09-14 15:52:44 +0300 |
commit | fd60f6713a9d9e6f7d706b53bf1311f2f1cd9031 (patch) | |
tree | bb762d4ce5e5ad76a52d594249e8c6ec33b08312 /source/blender/functions/tests | |
parent | 90a48fa06414ccf5fc5dd6092917413180ff30d1 (diff) |
Functions: support optional outputs in multi-function
Sometimes not all outputs of a multi-function are required by the
caller. In those cases it would be a waste of compute resources
to calculate the unused values anyway. Now, the caller of a
multi-function can specify when a specific output is not used.
The called function can check if an output is unused and may
ignore it. Multi-functions can still computed unused outputs as
before if they don't want to check if a specific output is unused.
The multi-function procedure system has been updated to support
ignored outputs in call instructions. An ignored output just has no
variable assigned to it.
The field system has been updated to generate a multi-function
procedure where unused outputs are ignored.
Diffstat (limited to 'source/blender/functions/tests')
3 files changed, 72 insertions, 0 deletions
diff --git a/source/blender/functions/tests/FN_field_test.cc b/source/blender/functions/tests/FN_field_test.cc index 212b79e75d3..1c2d5c8eaad 100644 --- a/source/blender/functions/tests/FN_field_test.cc +++ b/source/blender/functions/tests/FN_field_test.cc @@ -5,6 +5,7 @@ #include "FN_cpp_type.hh" #include "FN_field.hh" #include "FN_multi_function_builder.hh" +#include "FN_multi_function_test_common.hh" namespace blender::fn::tests { @@ -275,4 +276,19 @@ TEST(field, SameFieldTwice) EXPECT_EQ(varray2->get(1), 10); } +TEST(field, IgnoredOutput) +{ + static OptionalOutputsFunction fn; + Field<int> field{std::make_shared<FieldOperation>(fn), 0}; + + FieldContext field_context; + FieldEvaluator field_evaluator{field_context, 10}; + const VArray<int> *results = nullptr; + field_evaluator.add(field, &results); + field_evaluator.evaluate(); + + EXPECT_EQ(results->get(0), 5); + EXPECT_EQ(results->get(3), 5); +} + } // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test.cc b/source/blender/functions/tests/FN_multi_function_test.cc index 204dbfab6aa..d99993b35ac 100644 --- a/source/blender/functions/tests/FN_multi_function_test.cc +++ b/source/blender/functions/tests/FN_multi_function_test.cc @@ -328,5 +328,32 @@ TEST(multi_function, CustomMF_Convert) EXPECT_EQ(outputs[2], 9); } +TEST(multi_function, IgnoredOutputs) +{ + OptionalOutputsFunction fn; + { + MFParamsBuilder params(fn, 10); + params.add_ignored_single_output("Out 1"); + params.add_ignored_single_output("Out 2"); + MFContextBuilder context; + fn.call(IndexRange(10), params, context); + } + { + Array<int> results_1(10); + Array<std::string> results_2(10, NoInitialization()); + + MFParamsBuilder params(fn, 10); + params.add_uninitialized_single_output(results_1.as_mutable_span(), "Out 1"); + params.add_uninitialized_single_output(results_2.as_mutable_span(), "Out 2"); + MFContextBuilder context; + fn.call(IndexRange(10), params, context); + + EXPECT_EQ(results_1[0], 5); + EXPECT_EQ(results_1[3], 5); + EXPECT_EQ(results_1[9], 5); + EXPECT_EQ(results_2[0], "hello, this is a long string"); + } +} + } // namespace } // namespace blender::fn::tests diff --git a/source/blender/functions/tests/FN_multi_function_test_common.hh b/source/blender/functions/tests/FN_multi_function_test_common.hh index 51c8fac8a96..2a64cc302fe 100644 --- a/source/blender/functions/tests/FN_multi_function_test_common.hh +++ b/source/blender/functions/tests/FN_multi_function_test_common.hh @@ -171,4 +171,33 @@ class SumVectorFunction : public MultiFunction { } }; +class OptionalOutputsFunction : public MultiFunction { + public: + OptionalOutputsFunction() + { + static MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static MFSignature create_signature() + { + MFSignatureBuilder signature{"Optional Outputs"}; + signature.single_output<int>("Out 1"); + signature.single_output<std::string>("Out 2"); + return signature.build(); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + if (params.single_output_is_required(0, "Out 1")) { + MutableSpan<int> values = params.uninitialized_single_output<int>(0, "Out 1"); + values.fill_indices(mask, 5); + } + MutableSpan<std::string> values = params.uninitialized_single_output<std::string>(1, "Out 2"); + for (const int i : mask) { + new (&values[i]) std::string("hello, this is a long string"); + } + } +}; + } // namespace blender::fn::tests |