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-09-14 15:52:44 +0300
committerJacques Lucke <jacques@blender.org>2021-09-14 15:52:44 +0300
commitfd60f6713a9d9e6f7d706b53bf1311f2f1cd9031 (patch)
treebb762d4ce5e5ad76a52d594249e8c6ec33b08312 /source/blender/functions/intern
parent90a48fa06414ccf5fc5dd6092917413180ff30d1 (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/intern')
-rw-r--r--source/blender/functions/intern/field.cc46
-rw-r--r--source/blender/functions/intern/multi_function_procedure.cc16
-rw-r--r--source/blender/functions/intern/multi_function_procedure_executor.cc31
3 files changed, 73 insertions, 20 deletions
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index 6133658d8e3..574a9e6284f 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -189,17 +189,43 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure,
field_with_index.current_input_index++;
}
else {
- /* All inputs variables are ready, now add the function call. */
- Vector<MFVariable *> input_variables;
- for (const GField &field : operation_inputs) {
- input_variables.append(variable_by_field.lookup(field));
- }
+ /* All inputs variables are ready, now gather all variables that are used by the function
+ * and call it. */
const MultiFunction &multi_function = operation.multi_function();
- Vector<MFVariable *> output_variables = builder.add_call(multi_function, input_variables);
- /* Add newly created variables to the map. */
- for (const int i : output_variables.index_range()) {
- variable_by_field.add_new({operation, i}, output_variables[i]);
+ Vector<MFVariable *> variables(multi_function.param_amount());
+
+ int param_input_index = 0;
+ int param_output_index = 0;
+ for (const int param_index : multi_function.param_indices()) {
+ const MFParamType param_type = multi_function.param_type(param_index);
+ const MFParamType::InterfaceType interface_type = param_type.interface_type();
+ if (interface_type == MFParamType::Input) {
+ const GField &input_field = operation_inputs[param_input_index];
+ variables[param_index] = variable_by_field.lookup(input_field);
+ param_input_index++;
+ }
+ else if (interface_type == MFParamType::Output) {
+ const GFieldRef output_field{operation, param_output_index};
+ const bool output_is_ignored =
+ field_tree_info.field_users.lookup(output_field).is_empty() &&
+ !output_fields.contains(output_field);
+ if (output_is_ignored) {
+ /* Ignored outputs don't need a variable. */
+ variables[param_index] = nullptr;
+ }
+ else {
+ /* Create a new variable for used outputs. */
+ MFVariable &new_variable = procedure.new_variable(param_type.data_type());
+ variables[param_index] = &new_variable;
+ variable_by_field.add_new(output_field, &new_variable);
+ }
+ param_output_index++;
+ }
+ else {
+ BLI_assert_unreachable();
+ }
}
+ builder.add_call_with_all_variables(multi_function, variables);
}
}
}
@@ -334,7 +360,7 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
build_multi_function_procedure_for_fields(
procedure, scope, field_tree_info, varying_fields_to_evaluate);
MFProcedureExecutor procedure_executor{"Procedure", procedure};
- MFParamsBuilder mf_params{procedure_executor, array_size};
+ MFParamsBuilder mf_params{procedure_executor, &mask};
MFContextBuilder mf_context;
/* Provide inputs to the procedure executor. */
diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc
index 2aa760a494f..fa95e8de71e 100644
--- a/source/blender/functions/intern/multi_function_procedure.cc
+++ b/source/blender/functions/intern/multi_function_procedure.cc
@@ -325,7 +325,14 @@ bool MFProcedure::validate_all_instruction_pointers_set() const
bool MFProcedure::validate_all_params_provided() const
{
for (const MFCallInstruction *instruction : call_instructions_) {
- for (const MFVariable *variable : instruction->params_) {
+ const MultiFunction &fn = instruction->fn();
+ for (const int param_index : fn.param_indices()) {
+ const MFParamType param_type = fn.param_type(param_index);
+ if (param_type.category() == MFParamType::SingleOutput) {
+ /* Single outputs are optional. */
+ continue;
+ }
+ const MFVariable *variable = instruction->params_[param_index];
if (variable == nullptr) {
return false;
}
@@ -351,6 +358,9 @@ bool MFProcedure::validate_same_variables_in_one_call() const
for (const int param_index : fn.param_indices()) {
const MFParamType param_type = fn.param_type(param_index);
const MFVariable *variable = instruction->params_[param_index];
+ if (variable == nullptr) {
+ continue;
+ }
for (const int other_param_index : fn.param_indices()) {
if (other_param_index == param_index) {
continue;
@@ -681,7 +691,9 @@ class MFProcedureDotExport {
if (instruction.prev().size() != 1) {
return true;
}
- if (instruction.prev()[0].type() == MFInstructionCursor::Type::Branch) {
+ if (ELEM(instruction.prev()[0].type(),
+ MFInstructionCursor::Type::Branch,
+ MFInstructionCursor::Type::Entry)) {
return true;
}
return false;
diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc
index 38b26415779..b97282accdd 100644
--- a/source/blender/functions/intern/multi_function_procedure_executor.cc
+++ b/source/blender/functions/intern/multi_function_procedure_executor.cc
@@ -978,7 +978,7 @@ static bool evaluate_as_one(const MultiFunction &fn,
return false;
}
for (VariableState *state : param_variable_states) {
- if (!state->is_one()) {
+ if (state != nullptr && !state->is_one()) {
return false;
}
}
@@ -997,8 +997,13 @@ static void execute_call_instruction(const MFCallInstruction &instruction,
for (const int param_index : fn.param_indices()) {
const MFVariable *variable = instruction.params()[param_index];
- VariableState &variable_state = variable_states.get_variable_state(*variable);
- param_variable_states[param_index] = &variable_state;
+ if (variable == nullptr) {
+ param_variable_states[param_index] = nullptr;
+ }
+ else {
+ VariableState &variable_state = variable_states.get_variable_state(*variable);
+ param_variable_states[param_index] = &variable_state;
+ }
}
/* If all inputs to the function are constant, it's enough to call the function only once instead
@@ -1008,19 +1013,29 @@ static void execute_call_instruction(const MFCallInstruction &instruction,
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);
+ VariableState *variable_state = param_variable_states[param_index];
+ if (variable_state == nullptr) {
+ params.add_ignored_single_output();
+ }
+ else {
+ 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());
+ MFParamsBuilder params(fn, &mask);
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);
+ VariableState *variable_state = param_variable_states[param_index];
+ if (variable_state == nullptr) {
+ params.add_ignored_single_output();
+ }
+ else {
+ variable_states.add_as_param(*variable_state, params, param_type, mask);
+ }
}
fn.call(mask, params, context);