diff options
author | Jacques Lucke <jacques@blender.org> | 2021-08-19 14:09:41 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2021-08-19 14:09:41 +0300 |
commit | d78a530af18df4b14a9978bf2e1a9c21594bbb0f (patch) | |
tree | d3f9988a6fa5cff46c097042d7a9b1ec73e64520 /source | |
parent | 3ebe61db9fb045182bbd30cdf21bed38690c020b (diff) |
initial procedure builder
Diffstat (limited to 'source')
3 files changed, 305 insertions, 48 deletions
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 03e7a75da1d..f25a089ae50 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC FN_multi_function_param_type.hh FN_multi_function_params.hh FN_multi_function_procedure.hh + FN_multi_function_procedure_builder.hh FN_multi_function_procedure_executor.hh FN_multi_function_signature.hh ) diff --git a/source/blender/functions/FN_multi_function_procedure_builder.hh b/source/blender/functions/FN_multi_function_procedure_builder.hh new file mode 100644 index 00000000000..d8cc84a23ab --- /dev/null +++ b/source/blender/functions/FN_multi_function_procedure_builder.hh @@ -0,0 +1,276 @@ +/* + * 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 + */ + +#include "FN_multi_function_procedure.hh" + +namespace blender::fn { + +class MFInstructionCursor { + private: + MFInstruction *instruction_ = nullptr; + /* Only used when it is a branch instruction. */ + bool branch_output_ = false; + /* Only used when instruction is null. */ + bool is_entry_ = false; + + public: + MFInstructionCursor() = default; + + MFInstructionCursor(MFCallInstruction &instruction) : instruction_(&instruction) + { + } + + MFInstructionCursor(MFDestructInstruction &instruction) : instruction_(&instruction) + { + } + + MFInstructionCursor(MFBranchInstruction &instruction, bool branch_output) + : instruction_(&instruction), branch_output_(branch_output) + { + } + + static MFInstructionCursor Entry() + { + MFInstructionCursor cursor; + cursor.is_entry_ = true; + return cursor; + } + + void insert(MFProcedure &procedure, MFInstruction *new_instruction) + { + if (instruction_ == nullptr) { + if (is_entry_) { + procedure.set_entry(*new_instruction); + } + else { + /* The cursors points at nothing, nothing to do. */ + } + } + else { + switch (instruction_->type()) { + case MFInstructionType::Call: { + static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction); + break; + } + case MFInstructionType::Branch: { + MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>( + instruction_); + if (branch_output_) { + branch_instruction.set_branch_true(new_instruction); + } + else { + branch_instruction.set_branch_false(new_instruction); + } + break; + } + case MFInstructionType::Destruct: { + static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction); + break; + } + } + } + } +}; + +struct MFProcedureBuilderBranch; + +class MFProcedureBuilder { + private: + MFProcedure *procedure_ = nullptr; + Vector<MFInstructionCursor> cursors_; + + public: + MFProcedureBuilder(MFProcedure &procedure, + MFInstructionCursor initial_cursor = MFInstructionCursor::Entry()) + : procedure_(&procedure), cursors_({initial_cursor}) + { + } + + MFProcedureBuilder(Span<MFProcedureBuilder *> builders) + : MFProcedureBuilder(*builders[0]->procedure_) + { + this->set_cursor(builders); + } + + MFProcedureBuilder(MFProcedureBuilderBranch &branch); + + void set_cursor(const MFInstructionCursor &cursor) + { + cursors_ = {cursor}; + } + + void set_cursor(Span<MFInstructionCursor> cursors) + { + cursors_ = cursors; + } + + void set_cursor_after_branch(MFProcedureBuilderBranch &branch); + + void set_cursor(Span<MFProcedureBuilder *> builders) + { + cursors_.clear(); + for (MFProcedureBuilder *builder : builders) { + cursors_.extend(builder->cursors_); + } + } + + void insert_destruct(MFVariable &variable) + { + MFDestructInstruction &instruction = procedure_->new_destruct_instruction(&variable); + this->insert_at_cursors(&instruction); + cursors_ = {MFInstructionCursor{instruction}}; + } + + void insert_destruct(Span<MFVariable *> variables) + { + for (MFVariable *variable : variables) { + this->insert_destruct(*variable); + } + } + + MFProcedureBuilderBranch insert_branch(MFVariable &condition); + + MFCallInstruction &insert_call(const MultiFunction &fn) + { + MFCallInstruction &instruction = procedure_->new_call_instruction(fn); + this->insert_at_cursors(&instruction); + cursors_ = {MFInstructionCursor{instruction}}; + return instruction; + } + + MFCallInstruction &insert_call(const MultiFunction &fn, Span<MFVariable *> variables) + { + MFCallInstruction &instruction = this->insert_call(fn); + instruction.set_params(variables); + return instruction; + } + + Vector<MFVariable *> insert_call_with_new_variables( + const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables = {}) + { + Vector<MFVariable *> output_variables; + MFCallInstruction &instruction = this->insert_call(fn); + for (const int param_index : fn.param_indices()) { + const MFParamType param_type = fn.param_type(param_index); + switch (param_type.interface_type()) { + case MFParamType::Input: + case MFParamType::Mutable: { + MFVariable *variable = input_and_mutable_variables.first(); + instruction.set_param_variable(param_index, variable); + input_and_mutable_variables = input_and_mutable_variables.drop_front(1); + break; + } + case MFParamType::Output: { + MFVariable &variable = procedure_->new_variable(param_type.data_type()); + instruction.set_param_variable(param_index, &variable); + output_variables.append(&variable); + break; + } + } + } + /* All passed in variables should have been dropped in the loop above. */ + BLI_assert(input_and_mutable_variables.is_empty()); + return output_variables; + } + + template<int OutputN> + std::array<MFVariable *, OutputN> insert_call_with_new_variables( + const MultiFunction &fn, Span<MFVariable *> input_and_mutable_variables = {}) + { + Vector<MFVariable *> output_variables = this->insert_call_with_new_variables( + fn, input_and_mutable_variables); + BLI_assert(output_variables.size() == OutputN); + + std::array<MFVariable *, OutputN> output_array; + initialized_copy_n(output_variables.data(), OutputN, output_array.data()); + return output_array; + } + + void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable) + { + procedure_->add_parameter(interface_type, variable); + } + + MFVariable &add_parameter(MFParamType param_type, std::string name = "") + { + MFVariable &variable = procedure_->new_variable(param_type.data_type(), std::move(name)); + this->add_parameter(param_type.interface_type(), variable); + return variable; + } + + MFVariable &add_input_parameter(MFDataType data_type, std::string name = "") + { + return this->add_parameter(MFParamType(MFParamType::Input, data_type), std::move(name)); + } + + template<typename T> MFVariable &add_single_input_parameter(std::string name = "") + { + return this->add_parameter(MFParamType::ForSingleInput(CPPType::get<T>()), std::move(name)); + } + + template<typename T> MFVariable &add_single_mutable_parameter(std::string name = "") + { + return this->add_parameter(MFParamType::ForMutableSingle(CPPType::get<T>()), std::move(name)); + } + + void add_output_parameter(MFVariable &variable) + { + this->add_parameter(MFParamType::Output, variable); + } + + private: + void insert_at_cursors(MFInstruction *instruction) + { + for (MFInstructionCursor &cursor : cursors_) { + cursor.insert(*procedure_, instruction); + } + } +}; + +struct MFProcedureBuilderBranch { + MFProcedureBuilder branch_true; + MFProcedureBuilder branch_false; +}; + +MFProcedureBuilder::MFProcedureBuilder(MFProcedureBuilderBranch &branch) + : MFProcedureBuilder(*branch.branch_true.procedure_) +{ + this->set_cursor_after_branch(branch); +} + +void MFProcedureBuilder::set_cursor_after_branch(MFProcedureBuilderBranch &branch) +{ + this->set_cursor({&branch.branch_false, &branch.branch_true}); +} + +MFProcedureBuilderBranch MFProcedureBuilder::insert_branch(MFVariable &condition) +{ + MFBranchInstruction &instruction = procedure_->new_branch_instruction(&condition); + this->insert_at_cursors(&instruction); + + MFProcedureBuilderBranch branch{*procedure_, *procedure_}; + branch.branch_true.set_cursor(MFInstructionCursor{instruction, true}); + branch.branch_false.set_cursor(MFInstructionCursor{instruction, false}); + return branch; +} + +} // namespace blender::fn diff --git a/source/blender/functions/tests/FN_multi_function_procedure_test.cc b/source/blender/functions/tests/FN_multi_function_procedure_test.cc index bbd974d300e..33c145f1e65 100644 --- a/source/blender/functions/tests/FN_multi_function_procedure_test.cc +++ b/source/blender/functions/tests/FN_multi_function_procedure_test.cc @@ -3,6 +3,7 @@ #include "testing/testing.h" #include "FN_multi_function_builder.hh" +#include "FN_multi_function_procedure_builder.hh" #include "FN_multi_function_procedure_executor.hh" namespace blender::fn::tests { @@ -13,26 +14,15 @@ TEST(multi_function_procedure, SimpleTest) CustomMF_SM<int> add_10_fn{"add_10", [](int &a) { a += 10; }}; MFProcedure procedure; + MFProcedureBuilder builder{procedure}; - MFVariable &var1 = procedure.new_variable(MFDataType::ForSingle<int>(), "a"); - MFVariable &var2 = procedure.new_variable(MFDataType::ForSingle<int>(), "b"); - MFVariable &var3 = procedure.new_variable(MFDataType::ForSingle<int>(), "c"); - MFVariable &var4 = procedure.new_variable(MFDataType::ForSingle<int>(), "d"); - - MFCallInstruction &add1_instr = procedure.new_call_instruction(add_fn, {&var1, &var2, &var3}); - MFCallInstruction &add2_instr = procedure.new_call_instruction(add_fn, {&var2, &var3, &var4}); - MFCallInstruction &add3_instr = procedure.new_call_instruction(add_10_fn, {&var4}); - DestructInstructionChain destruction_chain = procedure.new_destruct_instructions( - {&var1, &var2, &var3}); - - procedure.set_entry(add1_instr); - add1_instr.set_next(&add2_instr); - add2_instr.set_next(&add3_instr); - add3_instr.set_next(destruction_chain.first); - - procedure.add_parameter(MFParamType::Input, var1); - procedure.add_parameter(MFParamType::Input, var2); - procedure.add_parameter(MFParamType::Output, var4); + MFVariable *var1 = &builder.add_single_input_parameter<int>(); + MFVariable *var2 = &builder.add_single_input_parameter<int>(); + auto [var3] = builder.insert_call_with_new_variables<1>(add_fn, {var1, var2}); + auto [var4] = builder.insert_call_with_new_variables<1>(add_fn, {var2, var3}); + builder.insert_call(add_10_fn, {var4}); + builder.insert_destruct({var1, var2, var3}); + builder.add_output_parameter(*var4); MFProcedureExecutor executor{"My Procedure", procedure}; @@ -59,22 +49,17 @@ TEST(multi_function_procedure, BranchTest) CustomMF_SM<int> add_100_fn{"add_100", [](int &a) { a += 100; }}; MFProcedure procedure; - MFVariable &a_var = procedure.new_variable(MFDataType::ForSingle<int>(), "a"); - MFVariable &cond_var = procedure.new_variable(MFDataType::ForSingle<bool>(), "cond"); + MFProcedureBuilder builder{procedure}; - MFBranchInstruction &branch_instr = procedure.new_branch_instruction(&cond_var); - MFCallInstruction &add_10_instr = procedure.new_call_instruction(add_10_fn, {&a_var}); - MFCallInstruction &add_100_instr = procedure.new_call_instruction(add_100_fn, {&a_var}); - DestructInstructionChain destruction_chain = procedure.new_destruct_instructions({&cond_var}); + MFVariable *var1 = &builder.add_single_mutable_parameter<int>(); + MFVariable *var2 = &builder.add_single_input_parameter<bool>(); - procedure.set_entry(branch_instr); - branch_instr.set_branch_false(&add_10_instr); - branch_instr.set_branch_true(&add_100_instr); - add_10_instr.set_next(destruction_chain.first); - add_100_instr.set_next(destruction_chain.first); - - procedure.add_parameter(MFParamType::Mutable, a_var); - procedure.add_parameter(MFParamType::Input, cond_var); + MFProcedureBuilderBranch branch = builder.insert_branch(*var2); + branch.branch_false.insert_call(add_10_fn, {var1}); + branch.branch_true.insert_call(add_100_fn, {var1}); + builder.set_cursor_after_branch(branch); + builder.insert_call(add_10_fn, {var1}); + builder.insert_destruct({var2}); MFProcedureExecutor procedure_fn{"Condition Test", procedure}; MFParamsBuilder params(procedure_fn, 5); @@ -89,13 +74,13 @@ TEST(multi_function_procedure, BranchTest) procedure_fn.call({1, 2, 3, 4}, params, context); EXPECT_EQ(values_a[0], 1); - EXPECT_EQ(values_a[1], 15); - EXPECT_EQ(values_a[2], 103); - EXPECT_EQ(values_a[3], 106); - EXPECT_EQ(values_a[4], 12); + EXPECT_EQ(values_a[1], 25); + EXPECT_EQ(values_a[2], 113); + EXPECT_EQ(values_a[3], 116); + EXPECT_EQ(values_a[4], 22); } -TEST(multi_function_procedure, SingleTest) +TEST(multi_function_procedure, EvaluateOne) { int tot_evaluations = 0; CustomMF_SI_SO<int, int> add_10_fn{"add_10", [&](int a) { @@ -104,17 +89,12 @@ TEST(multi_function_procedure, SingleTest) }}; MFProcedure procedure; - MFVariable &in_var = procedure.new_variable(MFDataType::ForSingle<int>(), "in"); - MFVariable &out_var = procedure.new_variable(MFDataType::ForSingle<int>(), "out"); - - MFCallInstruction &add_10_instr = procedure.new_call_instruction(add_10_fn, {&in_var, &out_var}); - DestructInstructionChain destruction_chain = procedure.new_destruct_instructions({&in_var}); - - add_10_instr.set_next(destruction_chain.first); + MFProcedureBuilder builder{procedure}; - procedure.set_entry(add_10_instr); - procedure.add_parameter(MFParamType::Input, in_var); - procedure.add_parameter(MFParamType::Output, out_var); + MFVariable *var1 = &builder.add_single_input_parameter<int>(); + auto [var2] = builder.insert_call_with_new_variables<1>(add_10_fn, {var1}); + builder.insert_destruct(*var1); + builder.add_output_parameter(*var2); MFProcedureExecutor procedure_fn{"Single Test", procedure}; MFParamsBuilder params{procedure_fn, 5}; |