Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2021-08-19 14:09:41 +0300
committerJacques Lucke <jacques@blender.org>2021-08-19 14:09:41 +0300
commitd78a530af18df4b14a9978bf2e1a9c21594bbb0f (patch)
treed3f9988a6fa5cff46c097042d7a9b1ec73e64520 /source
parent3ebe61db9fb045182bbd30cdf21bed38690c020b (diff)
initial procedure builder
Diffstat (limited to 'source')
-rw-r--r--source/blender/functions/CMakeLists.txt1
-rw-r--r--source/blender/functions/FN_multi_function_procedure_builder.hh276
-rw-r--r--source/blender/functions/tests/FN_multi_function_procedure_test.cc76
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};