From 94b98d3f882517076b652fd16734c673fb03a3a4 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 3 Sep 2021 12:54:28 +0200 Subject: fix bug when the same field is evaluated twice --- .../blender/functions/FN_multi_function_builder.hh | 9 +++++++ source/blender/functions/intern/field.cc | 15 +++++++++--- .../functions/intern/multi_function_builder.cc | 28 ++++++++++++++++++++++ source/blender/functions/tests/FN_field_test.cc | 20 ++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) (limited to 'source/blender/functions') diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index 7a526bb640b..d13615ced07 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -417,4 +417,13 @@ class CustomMF_DefaultOutput : public MultiFunction { void call(IndexMask mask, MFParams params, MFContext context) const override; }; +class CustomMF_GenericCopy : public MultiFunction { + private: + MFSignature signature_; + + public: + CustomMF_GenericCopy(StringRef name, MFDataType data_type); + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; + } // namespace blender::fn diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 6d8fce07f5a..073908e6c1d 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -136,6 +136,7 @@ static Set find_varying_fields(const FieldGraphInfo &graph_info, } static void build_multi_function_procedure_for_fields(MFProcedure &procedure, + ResourceScope &scope, const FieldGraphInfo &graph_info, Span output_fields) { @@ -187,9 +188,15 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure, } } - /* TODO: Handle case when there are duplicates in #output_fields. */ + Set already_output_variables; for (const GField *field : output_fields) { MFVariable *variable = variable_by_field.lookup(*field); + if (!already_output_variables.add(variable)) { + /* The same variable is output twice. Create a copy to make it work. */ + const MultiFunction ©_fn = scope.construct( + __func__, "copy", variable->data_type()); + variable = builder.add_call<1>(copy_fn, {variable})[0]; + } builder.add_output_parameter(*variable); } @@ -293,7 +300,8 @@ Vector evaluate_fields(ResourceScope &scope, const int array_size = mask.min_array_size(); if (!varying_fields_to_evaluate.is_empty()) { MFProcedure procedure; - build_multi_function_procedure_for_fields(procedure, graph_info, varying_fields_to_evaluate); + build_multi_function_procedure_for_fields( + procedure, scope, graph_info, varying_fields_to_evaluate); MFProcedureExecutor procedure_executor{"Procedure", procedure}; MFParamsBuilder mf_params{procedure_executor, mask.min_array_size()}; MFContextBuilder mf_context; @@ -335,7 +343,8 @@ Vector evaluate_fields(ResourceScope &scope, } if (!constant_fields_to_evaluate.is_empty()) { MFProcedure procedure; - build_multi_function_procedure_for_fields(procedure, graph_info, constant_fields_to_evaluate); + build_multi_function_procedure_for_fields( + procedure, scope, graph_info, constant_fields_to_evaluate); MFProcedureExecutor procedure_executor{"Procedure", procedure}; MFParamsBuilder mf_params{procedure_executor, 1}; MFContextBuilder mf_context; diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc index c6b3b808130..180d1f17a54 100644 --- a/source/blender/functions/intern/multi_function_builder.cc +++ b/source/blender/functions/intern/multi_function_builder.cc @@ -123,4 +123,32 @@ void CustomMF_DefaultOutput::call(IndexMask mask, MFParams params, MFContext UNU } } +CustomMF_GenericCopy::CustomMF_GenericCopy(StringRef name, MFDataType data_type) +{ + MFSignatureBuilder signature{name}; + signature.input("Input", data_type); + signature.output("Output", data_type); + signature_ = signature.build(); + this->set_signature(&signature_); +} + +void CustomMF_GenericCopy::call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const +{ + const MFDataType data_type = this->param_type(0).data_type(); + switch (data_type.category()) { + case MFDataType::Single: { + const GVArray &inputs = params.readonly_single_input(0, "Input"); + GMutableSpan outputs = params.uninitialized_single_output(1, "Output"); + inputs.materialize_to_uninitialized(mask, outputs.data()); + break; + } + case MFDataType::Vector: { + const GVVectorArray &inputs = params.readonly_vector_input(0, "Input"); + GVectorArray &outputs = params.vector_output(1, "Output"); + outputs.extend(mask, inputs); + break; + } + } +} + } // namespace blender::fn diff --git a/source/blender/functions/tests/FN_field_test.cc b/source/blender/functions/tests/FN_field_test.cc index 67fda512c92..b02e14559b5 100644 --- a/source/blender/functions/tests/FN_field_test.cc +++ b/source/blender/functions/tests/FN_field_test.cc @@ -237,4 +237,24 @@ TEST(field, TwoFunctionsTwoOutputs) EXPECT_EQ(result_2->get(8), 36); } +TEST(field, SameFieldTwice) +{ + GField constant_field{ + std::make_shared(std::make_unique>(10)), 0}; + + FieldContext field_context; + IndexMask mask{IndexRange(2)}; + ResourceScope scope; + Vector results = evaluate_fields( + scope, {&constant_field, &constant_field}, mask, field_context); + + GVArray_Typed varray1{*results[0]}; + GVArray_Typed varray2{*results[1]}; + + EXPECT_EQ(varray1->get(0), 10); + EXPECT_EQ(varray1->get(1), 10); + EXPECT_EQ(varray2->get(0), 10); + EXPECT_EQ(varray2->get(1), 10); +} + } // namespace blender::fn::tests -- cgit v1.2.3