50 if (handled_fields.
add(field)) {
51 fields_to_check.
push(field);
55 while (!fields_to_check.
is_empty()) {
67 field_tree_info.
field_users.add(operation_input, field);
68 if (handled_fields.
add(operation_input)) {
69 fields_to_check.
push(operation_input);
80 return field_tree_info;
90 const Span<std::reference_wrapper<const FieldInput>> field_inputs)
93 for (
const FieldInput &field_input : field_inputs) {
94 GVArray varray = context.get_varray_for_input(field_input,
mask, scope);
96 const CPPType &type = field_input.cpp_type();
99 field_context_inputs.
append(varray);
101 return field_context_inputs;
117 for (
const int i : field_context_inputs.
index_range()) {
118 const GVArray &varray = field_context_inputs[
i];
123 const GFieldRef field_input_field{field_input, 0};
126 if (found_fields.
add(field)) {
127 fields_to_check.
push(field);
131 while (!fields_to_check.
is_empty()) {
135 if (found_fields.
add(field)) {
136 fields_to_check.
push(field);
151 mf::ProcedureBuilder builder{procedure};
157 mf::Variable &variable = builder.add_input_parameter(
159 variable_by_field.
add_new({field_input, 0}, &variable);
163 struct FieldWithIndex {
165 int current_input_index = 0;
172 fields_to_check.
push({field, 0});
173 while (!fields_to_check.
is_empty()) {
174 FieldWithIndex &field_with_index = fields_to_check.
peek();
175 const GFieldRef &field = field_with_index.field;
176 if (variable_by_field.
contains(field)) {
178 fields_to_check.
pop();
191 if (field_with_index.current_input_index < operation_inputs.
size()) {
194 fields_to_check.
push({operation_inputs[field_with_index.current_input_index]});
195 field_with_index.current_input_index++;
203 int param_input_index = 0;
204 int param_output_index = 0;
206 const mf::ParamType param_type =
multi_function.param_type(param_index);
207 const mf::ParamType::InterfaceType interface_type = param_type.interface_type();
208 if (interface_type == mf::ParamType::Input) {
209 const GField &input_field = operation_inputs[param_input_index];
210 variables[param_index] = variable_by_field.
lookup(input_field);
213 else if (interface_type == mf::ParamType::Output) {
214 const GFieldRef output_field{operation_node, param_output_index};
215 const bool output_is_ignored =
216 field_tree_info.
field_users.lookup(output_field).is_empty() &&
217 !output_fields.
contains(output_field);
218 if (output_is_ignored) {
220 variables[param_index] =
nullptr;
224 mf::Variable &new_variable = procedure.new_variable(param_type.data_type());
225 variables[param_index] = &new_variable;
226 variable_by_field.
add_new(output_field, &new_variable);
228 param_output_index++;
240 const mf::MultiFunction &
fn = procedure.construct_function<mf::CustomMF_GenericConstant>(
241 constant_node.
type(), constant_node.
value().
get(),
false);
242 mf::Variable &new_variable = *builder.add_call<1>(
fn)[0];
243 variable_by_field.
add_new(field, &new_variable);
252 for (
const GFieldRef &field : output_fields) {
253 mf::Variable *variable = variable_by_field.
lookup(field);
254 if (!already_output_variables.
add(variable)) {
257 const mf::MultiFunction ©_fn = scope.
construct<mf::CustomMF_GenericCopy>(
258 variable->data_type());
259 variable = builder.add_call<1>(copy_fn, {variable})[0];
261 builder.add_output_parameter(*variable);
265 for (
const GFieldRef &field : output_fields) {
266 variable_by_field.
remove(field);
269 for (mf::Variable *variable : variable_by_field.
values()) {
270 builder.add_destruct(*variable);
273 mf::ReturnInstruction &return_instr = builder.add_return();
275 mf::procedure_optimization::move_destructs_up(procedure, return_instr);
288 Array<bool> is_output_written_to_dst(fields_to_evaluate.
size(),
false);
289 const int array_size =
mask.min_array_size();
291 if (
mask.is_empty()) {
293 const CPPType &type = fields_to_evaluate[
i].cpp_type();
320 for (
const int out_index : fields_to_evaluate.
index_range()) {
321 const GFieldRef &field = fields_to_evaluate[out_index];
328 const GVArray &varray = field_context_inputs[field_input_index];
329 varrays[out_index] = varray;
335 field_constant.
type(),
mask.min_array_size(), field_constant.
value().
get());
358 if (varying_fields.
contains(field)) {
359 varying_fields_to_evaluate.
append(field);
360 varying_field_indices.
append(
i);
363 constant_fields_to_evaluate.
append(field);
364 constant_field_indices.
append(
i);
369 if (!varying_fields_to_evaluate.
is_empty()) {
371 mf::Procedure procedure;
373 procedure, scope, field_tree_info, varying_fields_to_evaluate);
374 mf::ProcedureExecutor procedure_executor{procedure};
376 mf::ParamsBuilder mf_params{procedure_executor, &
mask};
377 mf::ContextBuilder mf_context;
380 for (
const GVArray &varray : field_context_inputs) {
381 mf_params.add_readonly_single_input(varray);
384 for (
const int i : varying_fields_to_evaluate.
index_range()) {
385 const GFieldRef &field = varying_fields_to_evaluate[
i];
387 const int out_index = varying_field_indices[
i];
392 if (!dst_varray || !dst_varray.
is_span()) {
408 varrays[out_index] = dst_varray;
409 is_output_written_to_dst[out_index] =
true;
414 mf_params.add_uninitialized_single_output(span);
417 procedure_executor.call_auto(
mask, mf_params, mf_context);
421 if (!constant_fields_to_evaluate.
is_empty()) {
423 mf::Procedure procedure;
425 procedure, scope, field_tree_info, constant_fields_to_evaluate);
426 mf::ProcedureExecutor procedure_executor{procedure};
428 mf::ParamsBuilder mf_params{procedure_executor, &
mask};
429 mf::ContextBuilder mf_context;
432 for (
const GVArray &varray : field_context_inputs) {
433 mf_params.add_readonly_single_input(varray);
436 for (
const int i : constant_fields_to_evaluate.
index_range()) {
437 const GFieldRef &field = constant_fields_to_evaluate[
i];
443 mf_params.add_uninitialized_single_output({type, buffer, 1});
446 const int out_index = constant_field_indices[
i];
450 procedure_executor.call(
mask, mf_params, mf_context);
456 for (
const int out_index : fields_to_evaluate.
index_range()) {
462 const GVArray &computed_varray = varrays[out_index];
464 if (is_output_written_to_dst[out_index]) {
478 BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
479 mask.slice(range).foreach_segment([&](auto segment) {
480 for (const int i : segment) {
481 computed_varray.get_to_uninitialized(i, buffer);
482 dst_varray.set_by_relocate(i, buffer);
487 varrays[out_index] = dst_varray;
504 varrays[0].get_to_uninitialized(0, r_value);
522 static auto not_fn = mf::build::SI1_SO<bool, bool>(
523 "Not", [](
bool a) {
return !a; }, mf::build::exec_presets::AllSpanOrSingle());
530 auto constant_node = std::make_shared<FieldConstant>(type, value);
531 return GField{std::move(constant_node)};
550 auto index_func = [](
int i) {
return i; };
587 if (&field_input !=
this) {
604 owned_function_ = std::move(function);
617 const std::shared_ptr<const FieldInputs> *field_inputs_candidate =
nullptr;
618 for (
const GField &field : fields) {
619 const std::shared_ptr<const FieldInputs> &field_inputs = field.node().field_inputs();
621 if (field_inputs && !field_inputs->nodes.is_empty()) {
622 if (field_inputs_candidate ==
nullptr) {
623 field_inputs_candidate = &field_inputs;
625 else if ((*field_inputs_candidate)->nodes.size() < field_inputs->nodes.size()) {
627 field_inputs_candidate = &field_inputs;
631 if (field_inputs_candidate ==
nullptr) {
637 for (
const GField &field : fields) {
638 const std::shared_ptr<const FieldInputs> &field_inputs = field.node().field_inputs();
642 if (&field_inputs == field_inputs_candidate) {
645 for (
const FieldInput *field_input : field_inputs->nodes) {
646 if (!(*field_inputs_candidate)->nodes.contains(field_input)) {
647 inputs_not_in_candidate.
append(field_input);
651 if (inputs_not_in_candidate.
is_empty()) {
653 return *field_inputs_candidate;
656 std::shared_ptr<FieldInputs> new_field_inputs = std::make_shared<FieldInputs>(
657 **field_inputs_candidate);
658 for (
const FieldInput *field_input : inputs_not_in_candidate) {
659 new_field_inputs->nodes.add(field_input);
660 new_field_inputs->deduplicated_nodes.add(*field_input);
662 return new_field_inputs;
680 std::shared_ptr<FieldInputs>
field_inputs = std::make_shared<FieldInputs>();
704 type_.destruct(value_);
722 return {type_, value_};
740 const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
741 dst_varrays_.append(dst);
742 output_pointer_infos_.append({});
753 const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
754 dst_varrays_.append(
nullptr);
755 output_pointer_infos_.append(OutputPointerInfo{
757 *
static_cast<GVArray *
>(dst) = varray;
764 const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
765 dst_varrays_.append(
nullptr);
766 output_pointer_infos_.append({});
775 if (selection_field) {
777 evaluate_fields(scope, {selection_field}, full_mask, context)[0].typed<bool>();
790 for (
const int i : fields_to_evaluate_.index_range()) {
791 fields[
i] = fields_to_evaluate_[
i];
793 evaluated_varrays_ =
evaluate_fields(scope_, fields, selection_mask_, context_, dst_varrays_);
794 BLI_assert(fields_to_evaluate_.size() == evaluated_varrays_.size());
795 for (
const int i : fields_to_evaluate_.index_range()) {
796 OutputPointerInfo &info = output_pointer_infos_[
i];
797 if (info.dst !=
nullptr) {
798 info.set(info.dst, evaluated_varrays_[
i], scope_);
801 is_evaluated_ =
true;
820 return selection_mask_;
#define BLI_assert_unreachable()
#define BLI_assert_msg(a, msg)
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define UNUSED_VARS_NDEBUG(...)
unsigned long long int uint64_t
bool is_trivially_destructible
void destruct_indices(void *ptr, const IndexMask &mask) const
void destruct(void *ptr) const
void value_initialize(void *ptr) const
GMutableSpan take_front(const int64_t n) const
const CPPType & type() const
static GVArray from_empty(const CPPType &type)
static GVArray from_single_default(const CPPType &type, int64_t size)
VArray< T > typed() const
static GVArray from_span(GSpan span)
static GVArray from_single_ref(const CPPType &type, int64_t size, const void *value)
static GVMutableArray from_span(GMutableSpan span)
GMutableSpan get_internal_span() const
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
MutableSpan< T > allocate_array(int64_t size)
ValueIterator values() const &
const Value & lookup(const Key &key) const
void add_new(const Key &key, const Value &value)
bool remove(const Key &key)
bool contains(const Key &key) const
T & construct(Args &&...args)
void add_destruct_call(Func func)
void * allocate_owned(const CPPType &type)
LinearAllocator & allocator()
bool contains(const Key &key) const
constexpr int64_t size() const
constexpr IndexRange index_range() const
constexpr bool is_empty() const
constexpr bool contains(const T &value) const
void push(const T &value)
T get_internal_single() const
static VArray from_func(const int64_t size, GetFunc get_func)
void append(const T &value)
IndexRange index_range() const
FieldConstant(const CPPType &type, const void *value)
const CPPType & type() const
const CPPType & output_cpp_type(int output_index) const override
~FieldConstant() override
virtual GVArray get_varray_for_input(const FieldInput &field_input, const IndexMask &mask, ResourceScope &scope) const
int add(GField field, GVArray *varray_ptr)
IndexMask get_evaluated_as_mask(int field_index)
IndexMask get_evaluated_selection_as_mask() const
int add_with_destination(GField field, GVMutableArray dst)
const GVArray & get_evaluated(const int field_index) const
FieldNodeType node_type() const
virtual void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
std::shared_ptr< const FieldInputs > field_inputs_
FieldNode(FieldNodeType node_type)
bool depends_on_input() const
const std::shared_ptr< const FieldInputs > & field_inputs() const
Span< GField > inputs() const
~FieldOperation() override
FieldOperation(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
static std::shared_ptr< FieldOperation > from(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
const mf::MultiFunction & multi_function() const
const CPPType & cpp_type() const
const FieldNode & node() const
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
void MEM_freeN(void *vmemh)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
static IndexMask index_mask_from_selection(const IndexMask full_mask, const VArray< bool > &selection, ResourceScope &scope)
GField make_constant_field(const CPPType &type, const void *value)
static std::shared_ptr< const FieldInputs > combine_field_inputs(Span< GField > fields)
static FieldTreeInfo preprocess_field_tree(Span< GFieldRef > entry_fields)
static void build_multi_function_procedure_for_fields(mf::Procedure &procedure, ResourceScope &scope, const FieldTreeInfo &field_tree_info, Span< GFieldRef > output_fields)
static IndexMask evaluate_selection(const Field< bool > &selection_field, const FieldContext &context, const IndexMask &full_mask, ResourceScope &scope)
Vector< GVArray > evaluate_fields(ResourceScope &scope, Span< GFieldRef > fields_to_evaluate, const IndexMask &mask, const FieldContext &context, Span< GVMutableArray > dst_varrays={})
GField make_field_constant_if_possible(GField field)
static Set< GFieldRef > find_varying_fields(const FieldTreeInfo &field_tree_info, Span< GVArray > field_context_inputs)
Field< bool > invert_boolean_field(const Field< bool > &field)
void evaluate_constant_field(const GField &field, void *r_value)
static Vector< GVArray > get_field_context_inputs(ResourceScope &scope, const IndexMask &mask, const FieldContext &context, const Span< std::reference_wrapper< const FieldInput > > field_inputs)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
VectorSet< std::reference_wrapper< const FieldInput > > deduplicated_field_inputs
MultiValueMap< GFieldRef, GFieldRef > field_users