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
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2021-08-19 12:22:57 +0300
committerJacques Lucke <jacques@blender.org>2021-08-19 12:22:57 +0300
commit3ebe61db9fb045182bbd30cdf21bed38690c020b (patch)
treefd2ba1a4b6c7cb90867a9e8df75c689450e2e7e1 /source/blender
parent86c2f139c698288eecb2d7d84038063b03065601 (diff)
support evaluation on one
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/functions/FN_multi_function_procedure.hh6
-rw-r--r--source/blender/functions/intern/multi_function_procedure.cc16
-rw-r--r--source/blender/functions/intern/multi_function_procedure_executor.cc194
-rw-r--r--source/blender/functions/tests/FN_multi_function_procedure_test.cc19
4 files changed, 196 insertions, 39 deletions
diff --git a/source/blender/functions/FN_multi_function_procedure.hh b/source/blender/functions/FN_multi_function_procedure.hh
index 7e519437534..0495a5d8ada 100644
--- a/source/blender/functions/FN_multi_function_procedure.hh
+++ b/source/blender/functions/FN_multi_function_procedure.hh
@@ -130,6 +130,11 @@ class MFDestructInstruction : public MFInstruction {
void set_next(MFInstruction *instruction);
};
+struct DestructInstructionChain {
+ MFDestructInstruction *first = nullptr;
+ MFDestructInstruction *last = nullptr;
+};
+
class MFProcedure : NonCopyable, NonMovable {
private:
LinearAllocator<> allocator_;
@@ -150,6 +155,7 @@ class MFProcedure : NonCopyable, NonMovable {
Span<MFVariable *> param_variables);
MFBranchInstruction &new_branch_instruction(MFVariable *condition_variable = nullptr);
MFDestructInstruction &new_destruct_instruction(MFVariable *variable = nullptr);
+ DestructInstructionChain new_destruct_instructions(Span<MFVariable *> variables);
void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable);
diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc
index 5e65fff4804..5741d6bdfe3 100644
--- a/source/blender/functions/intern/multi_function_procedure.cc
+++ b/source/blender/functions/intern/multi_function_procedure.cc
@@ -158,6 +158,22 @@ MFDestructInstruction &MFProcedure::new_destruct_instruction(MFVariable *variabl
return instruction;
}
+DestructInstructionChain MFProcedure::new_destruct_instructions(Span<MFVariable *> variables)
+{
+ DestructInstructionChain chain;
+ for (MFVariable *variable : variables) {
+ MFDestructInstruction &instruction = this->new_destruct_instruction(variable);
+ if (chain.first == nullptr) {
+ chain.first = chain.last = &instruction;
+ }
+ else {
+ chain.last->set_next(&instruction);
+ chain.last = &instruction;
+ }
+ }
+ return chain;
+}
+
void MFProcedure::add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable)
{
params_.append({interface_type, &variable});
diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc
index 06f33e320d2..bce0d48afee 100644
--- a/source/blender/functions/intern/multi_function_procedure_executor.cc
+++ b/source/blender/functions/intern/multi_function_procedure_executor.cc
@@ -146,7 +146,7 @@ class ValueAllocator : NonCopyable, NonMovable {
}
}
- VariableState *obtain_variable_state(VariableValue &value, int tot_initialized);
+ template<typename... Args> VariableState *obtain_variable_state(Args &&...args);
void release_variable_state(VariableState *state);
@@ -255,10 +255,14 @@ class VariableState : NonCopyable, NonMovable {
private:
VariableValue *value_;
int tot_initialized_;
+ /* This a non-owning pointer to either span buffer or #GVectorArray or null. */
+ void *caller_provided_storage_ = nullptr;
public:
- VariableState(VariableValue &value, int tot_initialized)
- : value_(&value), tot_initialized_(tot_initialized)
+ VariableState(VariableValue &value, int tot_initialized, void *caller_provided_storage = nullptr)
+ : value_(&value),
+ tot_initialized_(tot_initialized),
+ caller_provided_storage_(caller_provided_storage)
{
}
@@ -276,11 +280,11 @@ class VariableState : NonCopyable, NonMovable {
case ValueType::GVArray:
return this->value_as<VariableValue_GVArray>()->data.is_single();
case ValueType::Span:
- return false;
+ return tot_initialized_ == 0;
case ValueType::GVVectorArray:
return this->value_as<VariableValue_GVVectorArray>()->data.is_single_vector();
case ValueType::GVectorArray:
- return false;
+ return tot_initialized_ == 0;
case ValueType::OneSingle:
return true;
case ValueType::OneVector:
@@ -290,6 +294,17 @@ class VariableState : NonCopyable, NonMovable {
return false;
}
+ bool is_fully_initialized(const IndexMask full_mask)
+ {
+ return tot_initialized_ == full_mask.size();
+ }
+
+ bool is_fully_uninitialized(const IndexMask full_mask)
+ {
+ UNUSED_VARS(full_mask);
+ return tot_initialized_ == 0;
+ }
+
void add_as_input(MFParamsBuilder &params, IndexMask mask, const MFDataType &data_type) const
{
/* Sanity check to make sure that enough values are initialized. */
@@ -344,7 +359,14 @@ class VariableState : NonCopyable, NonMovable {
switch (data_type.category()) {
case MFDataType::Single: {
const CPPType &type = data_type.single_type();
- VariableValue_Span *new_value = value_allocator.obtain_Span(type, array_size);
+ VariableValue_Span *new_value = nullptr;
+ if (caller_provided_storage_ == nullptr) {
+ new_value = value_allocator.obtain_Span(type, array_size);
+ }
+ else {
+ /* Reuse the storage provided caller when possible. */
+ new_value = value_allocator.obtain_Span_not_owned(caller_provided_storage_);
+ }
if (value_->type == ValueType::GVArray) {
/* Fill new buffer with data from virtual array. */
this->value_as<VariableValue_GVArray>()->data.materialize_to_uninitialized(
@@ -366,8 +388,14 @@ class VariableState : NonCopyable, NonMovable {
}
case MFDataType::Vector: {
const CPPType &type = data_type.vector_base_type();
- VariableValue_GVectorArray *new_value = value_allocator.obtain_GVectorArray(type,
- array_size);
+ VariableValue_GVectorArray *new_value = nullptr;
+ if (caller_provided_storage_ == nullptr) {
+ new_value = value_allocator.obtain_GVectorArray(type, array_size);
+ }
+ else {
+ new_value = value_allocator.obtain_GVectorArray_not_owned(
+ *(GVectorArray *)caller_provided_storage_);
+ }
if (value_->type == ValueType::GVVectorArray) {
/* Fill new vector array with data from virtual vector array. */
new_value->data.extend(full_mask, this->value_as<VariableValue_GVVectorArray>()->data);
@@ -500,6 +528,10 @@ class VariableState : NonCopyable, NonMovable {
new_value->data);
new_value->is_initialized = true;
}
+ else if (value_->type == ValueType::Span) {
+ BLI_assert(tot_initialized_ == 0);
+ /* Nothing to do, the single value is uninitialized already. */
+ }
else {
BLI_assert_unreachable();
}
@@ -515,6 +547,10 @@ class VariableState : NonCopyable, NonMovable {
this->value_as<VariableValue_GVVectorArray>()->data;
new_value->data.extend(IndexRange(1), old_vector_array);
}
+ else if (value_->type == ValueType::GVectorArray) {
+ BLI_assert(tot_initialized_ == 0);
+ /* Nothing to do. */
+ }
else {
BLI_assert_unreachable();
}
@@ -567,6 +603,8 @@ class VariableState : NonCopyable, NonMovable {
BLI_assert(!value_typed->is_initialized);
params.add_uninitialized_single_output(
GMutableSpan{data_type.single_type(), value_typed->data, 1});
+ /* It becomes initialized when the multi-function is called. */
+ value_typed->is_initialized = true;
break;
}
case ValueType::OneVector: {
@@ -710,9 +748,9 @@ class VariableState : NonCopyable, NonMovable {
}
};
-VariableState *ValueAllocator::obtain_variable_state(VariableValue &value, int tot_initialized)
+template<typename... Args> VariableState *ValueAllocator::obtain_variable_state(Args &&...args)
{
- return new VariableState(value, tot_initialized);
+ return new VariableState(std::forward<Args>(args)...);
}
void ValueAllocator::release_variable_state(VariableState *state)
@@ -720,18 +758,18 @@ void ValueAllocator::release_variable_state(VariableState *state)
delete state;
}
-class VariableStateContainer {
+class VariableStates {
private:
ValueAllocator value_allocator_;
Map<const MFVariable *, VariableState *> variable_states_;
IndexMask full_mask_;
public:
- VariableStateContainer(IndexMask full_mask) : full_mask_(full_mask)
+ VariableStates(IndexMask full_mask) : full_mask_(full_mask)
{
}
- ~VariableStateContainer()
+ ~VariableStates()
{
for (auto &&item : variable_states_.items()) {
const MFVariable *variable = item.key;
@@ -740,6 +778,11 @@ class VariableStateContainer {
}
}
+ ValueAllocator &value_allocator()
+ {
+ return value_allocator_;
+ }
+
void add_initial_variable_states(const MFProcedureExecutor &fn,
const MFProcedure &procedure,
MFParams &params)
@@ -748,10 +791,13 @@ class VariableStateContainer {
MFParamType param_type = fn.param_type(param_index);
const MFVariable *variable = procedure.params()[param_index].second;
- auto add_state = [&](VariableValue *value, bool input_is_initialized) {
+ auto add_state = [&](VariableValue *value,
+ bool input_is_initialized,
+ void *caller_provided_storage = nullptr) {
const int tot_initialized = input_is_initialized ? full_mask_.size() : 0;
variable_states_.add_new(variable,
- value_allocator_.obtain_variable_state(*value, tot_initialized));
+ value_allocator_.obtain_variable_state(
+ *value, tot_initialized, caller_provided_storage));
};
switch (param_type.category()) {
@@ -767,47 +813,67 @@ class VariableStateContainer {
}
case MFParamType::SingleOutput: {
GMutableSpan data = params.uninitialized_single_output(param_index);
- add_state(value_allocator_.obtain_Span_not_owned(data.data()), false);
+ add_state(value_allocator_.obtain_Span_not_owned(data.data()), false, data.data());
break;
}
case MFParamType::VectorOutput: {
GVectorArray &data = params.vector_output(param_index);
- add_state(value_allocator_.obtain_GVectorArray_not_owned(data), false);
+ add_state(value_allocator_.obtain_GVectorArray_not_owned(data), false, &data);
break;
}
case MFParamType::SingleMutable: {
GMutableSpan data = params.single_mutable(param_index);
- add_state(value_allocator_.obtain_Span_not_owned(data.data()), true);
+ add_state(value_allocator_.obtain_Span_not_owned(data.data()), true, data.data());
break;
}
case MFParamType::VectorMutable: {
GVectorArray &data = params.vector_mutable(param_index);
- add_state(value_allocator_.obtain_GVectorArray_not_owned(data), true);
+ add_state(value_allocator_.obtain_GVectorArray_not_owned(data), true, &data);
break;
}
}
}
}
- void load_param(MFParamsBuilder &params,
- const MFVariable &variable,
- const MFParamType &param_type,
- const IndexMask &mask)
+ void add_as_param(VariableState &variable_state,
+ MFParamsBuilder &params,
+ const MFParamType &param_type,
+ const IndexMask &mask)
{
- VariableState &variable_state = this->get_variable_state(variable);
+ const MFDataType data_type = param_type.data_type();
switch (param_type.interface_type()) {
case MFParamType::Input: {
- variable_state.add_as_input(params, mask, variable.data_type());
+ variable_state.add_as_input(params, mask, data_type);
break;
}
case MFParamType::Mutable: {
- variable_state.add_as_mutable(
- params, mask, full_mask_, variable.data_type(), value_allocator_);
+ variable_state.add_as_mutable(params, mask, full_mask_, data_type, value_allocator_);
break;
}
case MFParamType::Output: {
- variable_state.add_as_output(
- params, mask, full_mask_, variable.data_type(), value_allocator_);
+ variable_state.add_as_output(params, mask, full_mask_, data_type, value_allocator_);
+ break;
+ }
+ }
+ }
+
+ void add_as_param__one(VariableState &variable_state,
+ MFParamsBuilder &params,
+ const MFParamType &param_type,
+ const IndexMask &mask)
+ {
+ const MFDataType data_type = param_type.data_type();
+ switch (param_type.interface_type()) {
+ case MFParamType::Input: {
+ variable_state.add_as_input__one(params, data_type);
+ break;
+ }
+ case MFParamType::Mutable: {
+ variable_state.add_as_mutable__one(params, data_type, value_allocator_);
+ break;
+ }
+ case MFParamType::Output: {
+ variable_state.add_as_output__one(params, mask, data_type, value_allocator_);
break;
}
}
@@ -843,21 +909,57 @@ class VariableStateContainer {
}
};
+static bool evaluate_as_one(const MultiFunction &fn, Span<VariableState *> param_variable_states)
+{
+ if (fn.depends_on_context()) {
+ return false;
+ }
+ for (VariableState *state : param_variable_states) {
+ if (!state->is_one()) {
+ return false;
+ }
+ }
+ return true;
+}
+
static void execute_call_instruction(const MFCallInstruction &instruction,
IndexMask mask,
- VariableStateContainer &variable_states,
+ VariableStates &variable_states,
const MFContext &context)
{
const MultiFunction &fn = instruction.fn();
- MFParamsBuilder params(fn, mask.min_array_size());
+
+ Vector<VariableState *> param_variable_states;
+ param_variable_states.resize(fn.param_amount());
for (const int param_index : fn.param_indices()) {
- const MFParamType param_type = fn.param_type(param_index);
const MFVariable *variable = instruction.params()[param_index];
- variable_states.load_param(params, *variable, param_type, mask);
+ VariableState &variable_state = variable_states.get_variable_state(*variable);
+ param_variable_states[param_index] = &variable_state;
}
- fn.call(mask, params, context);
+ if (evaluate_as_one(fn, param_variable_states)) {
+ MFParamsBuilder params(fn, 1);
+
+ for (const int param_index : fn.param_indices()) {
+ const MFParamType param_type = fn.param_type(param_index);
+ VariableState &variable_state = *param_variable_states[param_index];
+ variable_states.add_as_param__one(variable_state, params, param_type, mask);
+ }
+
+ fn.call(IndexRange(1), params, context);
+ }
+ else {
+ MFParamsBuilder params(fn, mask.min_array_size());
+
+ for (const int param_index : fn.param_indices()) {
+ const MFParamType param_type = fn.param_type(param_index);
+ VariableState &variable_state = *param_variable_states[param_index];
+ variable_states.add_as_param(variable_state, params, param_type, mask);
+ }
+
+ fn.call(mask, params, context);
+ }
}
struct NextInstructionInfo {
@@ -956,7 +1058,7 @@ void MFProcedureExecutor::call(IndexMask full_mask, MFParams params, MFContext c
LinearAllocator<> allocator;
- VariableStateContainer variable_states{full_mask};
+ VariableStates variable_states{full_mask};
variable_states.add_initial_variable_states(*this, procedure_, params);
InstructionScheduler scheduler;
@@ -993,8 +1095,28 @@ void MFProcedureExecutor::call(IndexMask full_mask, MFParams params, MFContext c
break;
}
}
+ }
- /* TODO: Make sure outputs are copied into the right place. */
+ for (const int param_index : this->param_indices()) {
+ const MFParamType param_type = this->param_type(param_index);
+ const MFVariable *variable = procedure_.params()[param_index].second;
+ VariableState &variable_state = variable_states.get_variable_state(*variable);
+ switch (param_type.interface_type()) {
+ case MFParamType::Input: {
+ /* Input variables must be destructed in the end. */
+ BLI_assert(variable_state.is_fully_uninitialized(full_mask));
+ break;
+ }
+ case MFParamType::Mutable:
+ case MFParamType::Output: {
+ /* Mutable and output variables must be initialized in the end. */
+ BLI_assert(variable_state.is_fully_initialized(full_mask));
+ /* Make sure that the data is in the memory provided by the caller. */
+ variable_state.ensure_is_mutable(
+ full_mask, param_type.data_type(), variable_states.value_allocator());
+ break;
+ }
+ }
}
}
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 fdc575182d1..bbd974d300e 100644
--- a/source/blender/functions/tests/FN_multi_function_procedure_test.cc
+++ b/source/blender/functions/tests/FN_multi_function_procedure_test.cc
@@ -22,12 +22,13 @@ TEST(multi_function_procedure, SimpleTest)
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});
- MFDestructInstruction &destruct_instr = procedure.new_destruct_instruction(&var3);
+ 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(&destruct_instr);
+ add3_instr.set_next(destruction_chain.first);
procedure.add_parameter(MFParamType::Input, var1);
procedure.add_parameter(MFParamType::Input, var2);
@@ -64,10 +65,13 @@ TEST(multi_function_procedure, BranchTest)
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});
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);
@@ -93,13 +97,20 @@ TEST(multi_function_procedure, BranchTest)
TEST(multi_function_procedure, SingleTest)
{
- CustomMF_SI_SO<int, int> add_10_fn{"add_10", [](int a) { return a + 10; }};
+ int tot_evaluations = 0;
+ CustomMF_SI_SO<int, int> add_10_fn{"add_10", [&](int a) {
+ tot_evaluations++;
+ return a + 10;
+ }};
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);
procedure.set_entry(add_10_instr);
procedure.add_parameter(MFParamType::Input, in_var);
@@ -120,6 +131,8 @@ TEST(multi_function_procedure, SingleTest)
EXPECT_EQ(values_out[2], 3);
EXPECT_EQ(values_out[3], 11);
EXPECT_EQ(values_out[4], 11);
+ /* We expect only one evaluation, because the input is constant. */
+ EXPECT_EQ(tot_evaluations, 1);
}
} // namespace blender::fn::tests