Blender V5.0
field.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6#include "BLI_map.hh"
8#include "BLI_set.hh"
9#include "BLI_stack.hh"
10#include "BLI_vector_set.hh"
11
12#include "FN_field.hh"
18
19namespace blender::fn {
20
21/* -------------------------------------------------------------------- */
24
38
43{
44 FieldTreeInfo field_tree_info;
45
46 Stack<GFieldRef> fields_to_check;
47 Set<GFieldRef> handled_fields;
48
49 for (GFieldRef field : entry_fields) {
50 if (handled_fields.add(field)) {
51 fields_to_check.push(field);
52 }
53 }
54
55 while (!fields_to_check.is_empty()) {
56 GFieldRef field = fields_to_check.pop();
57 const FieldNode &field_node = field.node();
58 switch (field_node.node_type()) {
60 const FieldInput &field_input = static_cast<const FieldInput &>(field_node);
61 field_tree_info.deduplicated_field_inputs.add(field_input);
62 break;
63 }
65 const FieldOperation &operation = static_cast<const FieldOperation &>(field_node);
66 for (const GFieldRef operation_input : operation.inputs()) {
67 field_tree_info.field_users.add(operation_input, field);
68 if (handled_fields.add(operation_input)) {
69 fields_to_check.push(operation_input);
70 }
71 }
72 break;
73 }
75 /* Nothing to do. */
76 break;
77 }
78 }
79 }
80 return field_tree_info;
81}
82
87 ResourceScope &scope,
88 const IndexMask &mask,
89 const FieldContext &context,
90 const Span<std::reference_wrapper<const FieldInput>> field_inputs)
91{
92 Vector<GVArray> field_context_inputs;
93 for (const FieldInput &field_input : field_inputs) {
94 GVArray varray = context.get_varray_for_input(field_input, mask, scope);
95 if (!varray) {
96 const CPPType &type = field_input.cpp_type();
97 varray = GVArray::from_single_default(type, mask.min_array_size());
98 }
99 field_context_inputs.append(varray);
100 }
101 return field_context_inputs;
102}
103
109 Span<GVArray> field_context_inputs)
110{
111 Set<GFieldRef> found_fields;
112 Stack<GFieldRef> fields_to_check;
113
114 /* The varying fields are the ones that depend on inputs that are not constant. Therefore we
115 * start the tree search at the non-constant input fields and traverse through all fields that
116 * depend on them. */
117 for (const int i : field_context_inputs.index_range()) {
118 const GVArray &varray = field_context_inputs[i];
119 if (varray.is_single()) {
120 continue;
121 }
122 const FieldInput &field_input = field_tree_info.deduplicated_field_inputs[i];
123 const GFieldRef field_input_field{field_input, 0};
124 const Span<GFieldRef> users = field_tree_info.field_users.lookup(field_input_field);
125 for (const GFieldRef &field : users) {
126 if (found_fields.add(field)) {
127 fields_to_check.push(field);
128 }
129 }
130 }
131 while (!fields_to_check.is_empty()) {
132 GFieldRef field = fields_to_check.pop();
133 const Span<GFieldRef> users = field_tree_info.field_users.lookup(field);
134 for (GFieldRef field : users) {
135 if (found_fields.add(field)) {
136 fields_to_check.push(field);
137 }
138 }
139 }
140 return found_fields;
141}
142
146static void build_multi_function_procedure_for_fields(mf::Procedure &procedure,
147 ResourceScope &scope,
148 const FieldTreeInfo &field_tree_info,
149 Span<GFieldRef> output_fields)
150{
151 mf::ProcedureBuilder builder{procedure};
152 /* Every input, intermediate and output field corresponds to a variable in the procedure. */
153 Map<GFieldRef, mf::Variable *> variable_by_field;
154
155 /* Start by adding the field inputs as parameters to the procedure. */
156 for (const FieldInput &field_input : field_tree_info.deduplicated_field_inputs) {
157 mf::Variable &variable = builder.add_input_parameter(
158 mf::DataType::ForSingle(field_input.cpp_type()), field_input.debug_name());
159 variable_by_field.add_new({field_input, 0}, &variable);
160 }
161
162 /* Utility struct that is used to do proper depth first search traversal of the tree below. */
163 struct FieldWithIndex {
164 GFieldRef field;
165 int current_input_index = 0;
166 };
167
168 for (GFieldRef field : output_fields) {
169 /* We start a new stack for each output field to make sure that a field pushed later to the
170 * stack does never depend on a field that was pushed before. */
171 Stack<FieldWithIndex> fields_to_check;
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)) {
177 /* The field has been handled already. */
178 fields_to_check.pop();
179 continue;
180 }
181 const FieldNode &field_node = field.node();
182 switch (field_node.node_type()) {
184 /* Field inputs should already be handled above. */
185 break;
186 }
188 const FieldOperation &operation_node = static_cast<const FieldOperation &>(field.node());
189 const Span<GField> operation_inputs = operation_node.inputs();
190
191 if (field_with_index.current_input_index < operation_inputs.size()) {
192 /* Not all inputs are handled yet. Push the next input field to the stack and increment
193 * the input index. */
194 fields_to_check.push({operation_inputs[field_with_index.current_input_index]});
195 field_with_index.current_input_index++;
196 }
197 else {
198 /* All inputs variables are ready, now gather all variables that are used by the
199 * function and call it. */
200 const mf::MultiFunction &multi_function = operation_node.multi_function();
201 Vector<mf::Variable *> variables(multi_function.param_amount());
202
203 int param_input_index = 0;
204 int param_output_index = 0;
205 for (const int param_index : multi_function.param_indices()) {
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);
211 param_input_index++;
212 }
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) {
219 /* Ignored outputs don't need a variable. */
220 variables[param_index] = nullptr;
221 }
222 else {
223 /* Create a new variable for used outputs. */
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);
227 }
228 param_output_index++;
229 }
230 else {
232 }
233 }
234 builder.add_call_with_all_variables(multi_function, variables);
235 }
236 break;
237 }
239 const FieldConstant &constant_node = static_cast<const FieldConstant &>(field_node);
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);
244 break;
245 }
246 }
247 }
248 }
249
250 /* Add output parameters to the procedure. */
251 Set<mf::Variable *> already_output_variables;
252 for (const GFieldRef &field : output_fields) {
253 mf::Variable *variable = variable_by_field.lookup(field);
254 if (!already_output_variables.add(variable)) {
255 /* One variable can be output at most once. To output the same value twice, we have to make
256 * a copy first. */
257 const mf::MultiFunction &copy_fn = scope.construct<mf::CustomMF_GenericCopy>(
258 variable->data_type());
259 variable = builder.add_call<1>(copy_fn, {variable})[0];
260 }
261 builder.add_output_parameter(*variable);
262 }
263
264 /* Remove the variables that should not be destructed from the map. */
265 for (const GFieldRef &field : output_fields) {
266 variable_by_field.remove(field);
267 }
268 /* Add destructor calls for the remaining variables. */
269 for (mf::Variable *variable : variable_by_field.values()) {
270 builder.add_destruct(*variable);
271 }
272
273 mf::ReturnInstruction &return_instr = builder.add_return();
274
275 mf::procedure_optimization::move_destructs_up(procedure, return_instr);
276
277 // std::cout << procedure.to_dot() << "\n";
278 BLI_assert(procedure.validate());
279}
280
282 Span<GFieldRef> fields_to_evaluate,
283 const IndexMask &mask,
284 const FieldContext &context,
285 Span<GVMutableArray> dst_varrays)
286{
287 Vector<GVArray> varrays(fields_to_evaluate.size());
288 Array<bool> is_output_written_to_dst(fields_to_evaluate.size(), false);
289 const int array_size = mask.min_array_size();
290
291 if (mask.is_empty()) {
292 for (const int i : fields_to_evaluate.index_range()) {
293 const CPPType &type = fields_to_evaluate[i].cpp_type();
294 varrays[i] = GVArray::from_empty(type);
295 }
296 return varrays;
297 }
298
299 /* Destination arrays are optional. Create a small utility method to access them. */
300 auto get_dst_varray = [&](int index) -> GVMutableArray {
301 if (dst_varrays.is_empty()) {
302 return {};
303 }
304 const GVMutableArray &varray = dst_varrays[index];
305 if (!varray) {
306 return {};
307 }
308 BLI_assert(varray.size() >= array_size);
309 return varray;
310 };
311
312 /* Traverse the field tree and prepare some data that is used in later steps. */
313 FieldTreeInfo field_tree_info = preprocess_field_tree(fields_to_evaluate);
314
315 /* Get inputs that will be passed into the field when evaluated. */
316 Vector<GVArray> field_context_inputs = get_field_context_inputs(
317 scope, mask, context, field_tree_info.deduplicated_field_inputs);
318
319 /* Finish fields that don't need any processing directly. */
320 for (const int out_index : fields_to_evaluate.index_range()) {
321 const GFieldRef &field = fields_to_evaluate[out_index];
322 const FieldNode &field_node = field.node();
323 switch (field_node.node_type()) {
325 const FieldInput &field_input = static_cast<const FieldInput &>(field.node());
326 const int field_input_index = field_tree_info.deduplicated_field_inputs.index_of(
327 field_input);
328 const GVArray &varray = field_context_inputs[field_input_index];
329 varrays[out_index] = varray;
330 break;
331 }
333 const FieldConstant &field_constant = static_cast<const FieldConstant &>(field.node());
334 varrays[out_index] = GVArray::from_single_ref(
335 field_constant.type(), mask.min_array_size(), field_constant.value().get());
336 break;
337 }
339 break;
340 }
341 }
342 }
343
344 Set<GFieldRef> varying_fields = find_varying_fields(field_tree_info, field_context_inputs);
345
346 /* Separate fields into two categories. Those that are constant and need to be evaluated only
347 * once, and those that need to be evaluated for every index. */
348 Vector<GFieldRef> varying_fields_to_evaluate;
349 Vector<int> varying_field_indices;
350 Vector<GFieldRef> constant_fields_to_evaluate;
351 Vector<int> constant_field_indices;
352 for (const int i : fields_to_evaluate.index_range()) {
353 if (varrays[i]) {
354 /* Already done. */
355 continue;
356 }
357 GFieldRef field = fields_to_evaluate[i];
358 if (varying_fields.contains(field)) {
359 varying_fields_to_evaluate.append(field);
360 varying_field_indices.append(i);
361 }
362 else {
363 constant_fields_to_evaluate.append(field);
364 constant_field_indices.append(i);
365 }
366 }
367
368 /* Evaluate varying fields if necessary. */
369 if (!varying_fields_to_evaluate.is_empty()) {
370 /* Build the procedure for those fields. */
371 mf::Procedure procedure;
373 procedure, scope, field_tree_info, varying_fields_to_evaluate);
374 mf::ProcedureExecutor procedure_executor{procedure};
375
376 mf::ParamsBuilder mf_params{procedure_executor, &mask};
377 mf::ContextBuilder mf_context;
378
379 /* Provide inputs to the procedure executor. */
380 for (const GVArray &varray : field_context_inputs) {
381 mf_params.add_readonly_single_input(varray);
382 }
383
384 for (const int i : varying_fields_to_evaluate.index_range()) {
385 const GFieldRef &field = varying_fields_to_evaluate[i];
386 const CPPType &type = field.cpp_type();
387 const int out_index = varying_field_indices[i];
388
389 /* Try to get an existing virtual array that the result should be written into. */
390 GVMutableArray dst_varray = get_dst_varray(out_index);
391 void *buffer;
392 if (!dst_varray || !dst_varray.is_span()) {
393 /* Allocate a new buffer for the computed result. */
394 buffer = scope.allocator().allocate_array(type, array_size);
395
396 if (!type.is_trivially_destructible) {
397 /* Destruct values in the end. */
398 scope.add_destruct_call(
399 [buffer, mask, &type]() { type.destruct_indices(buffer, mask); });
400 }
401
402 varrays[out_index] = GVArray::from_span({type, buffer, array_size});
403 }
404 else {
405 /* Write the result into the existing span. */
406 buffer = dst_varray.get_internal_span().data();
407
408 varrays[out_index] = dst_varray;
409 is_output_written_to_dst[out_index] = true;
410 }
411
412 /* Pass output buffer to the procedure executor. */
413 const GMutableSpan span{type, buffer, array_size};
414 mf_params.add_uninitialized_single_output(span);
415 }
416
417 procedure_executor.call_auto(mask, mf_params, mf_context);
418 }
419
420 /* Evaluate constant fields if necessary. */
421 if (!constant_fields_to_evaluate.is_empty()) {
422 /* Build the procedure for those fields. */
423 mf::Procedure procedure;
425 procedure, scope, field_tree_info, constant_fields_to_evaluate);
426 mf::ProcedureExecutor procedure_executor{procedure};
427 const IndexMask mask(1);
428 mf::ParamsBuilder mf_params{procedure_executor, &mask};
429 mf::ContextBuilder mf_context;
430
431 /* Provide inputs to the procedure executor. */
432 for (const GVArray &varray : field_context_inputs) {
433 mf_params.add_readonly_single_input(varray);
434 }
435
436 for (const int i : constant_fields_to_evaluate.index_range()) {
437 const GFieldRef &field = constant_fields_to_evaluate[i];
438 const CPPType &type = field.cpp_type();
439 /* Allocate memory where the computed value will be stored in. */
440 void *buffer = scope.allocate_owned(type);
441
442 /* Pass output buffer to the procedure executor. */
443 mf_params.add_uninitialized_single_output({type, buffer, 1});
444
445 /* Create virtual array that can be used after the procedure has been executed below. */
446 const int out_index = constant_field_indices[i];
447 varrays[out_index] = GVArray::from_single_ref(type, array_size, buffer);
448 }
449
450 procedure_executor.call(mask, mf_params, mf_context);
451 }
452
453 /* Copy data to supplied destination arrays if necessary. In some cases the evaluation above
454 * has written the computed data in the right place already. */
455 if (!dst_varrays.is_empty()) {
456 for (const int out_index : fields_to_evaluate.index_range()) {
457 GVMutableArray dst_varray = get_dst_varray(out_index);
458 if (!dst_varray) {
459 /* Caller did not provide a destination for this output. */
460 continue;
461 }
462 const GVArray &computed_varray = varrays[out_index];
463 BLI_assert(computed_varray.type() == dst_varray.type());
464 if (is_output_written_to_dst[out_index]) {
465 /* The result has been written into the destination provided by the caller already. */
466 continue;
467 }
468 /* Still have to copy over the data in the destination provided by the caller. */
469 if (dst_varray.is_span()) {
470 array_utils::copy(computed_varray,
471 mask,
472 dst_varray.get_internal_span().take_front(mask.min_array_size()));
473 }
474 else {
475 /* Slower materialize into a different structure. */
476 const CPPType &type = computed_varray.type();
477 threading::parallel_for(mask.index_range(), 2048, [&](const IndexRange range) {
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);
483 }
484 });
485 });
486 }
487 varrays[out_index] = dst_varray;
488 }
489 }
490 return varrays;
491}
492
493void evaluate_constant_field(const GField &field, void *r_value)
494{
495 if (field.node().depends_on_input()) {
496 const CPPType &type = field.cpp_type();
497 type.value_initialize(r_value);
498 return;
499 }
500
501 ResourceScope scope;
502 FieldContext context;
503 Vector<GVArray> varrays = evaluate_fields(scope, {field}, IndexRange(1), context);
504 varrays[0].get_to_uninitialized(0, r_value);
505}
506
508{
509 if (field.node().depends_on_input()) {
510 return field;
511 }
512 const CPPType &type = field.cpp_type();
513 BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
514 evaluate_constant_field(field, buffer);
515 GField new_field = make_constant_field(type, buffer);
516 type.destruct(buffer);
517 return new_field;
518}
519
521{
522 static auto not_fn = mf::build::SI1_SO<bool, bool>(
523 "Not", [](bool a) { return !a; }, mf::build::exec_presets::AllSpanOrSingle());
524 auto not_op = FieldOperation::from(not_fn, {field});
525 return Field<bool>(not_op);
526}
527
528GField make_constant_field(const CPPType &type, const void *value)
529{
530 auto constant_node = std::make_shared<FieldConstant>(type, value);
531 return GField{std::move(constant_node)};
532}
533
535 const IndexMask &mask,
536 ResourceScope &scope) const
537{
538 /* By default ask the field input to create the varray. Another field context might overwrite
539 * the context here. */
540 return field_input.get_varray_for_context(*this, mask, scope);
541}
542
547
549{
550 auto index_func = [](int i) { return i; };
551 return VArray<int>::from_func(mask.min_array_size(), index_func);
552}
553
555 const IndexMask &mask,
556 ResourceScope & /*scope*/) const
557{
558 /* TODO: Investigate a similar method to IndexRange::as_span() */
559 return get_index_varray(mask);
560}
561
563{
564 /* Some random constant hash. */
565 return 128736487678;
566}
567
569{
570 return dynamic_cast<const IndexFieldInput *>(&other) != nullptr;
571}
572
574
575/* -------------------------------------------------------------------- */
578
579/* Avoid generating the destructor in every translation unit. */
580FieldNode::~FieldNode() = default;
581
583{
584 if (field_inputs_) {
585 for (const FieldInput &field_input : field_inputs_->deduplicated_nodes) {
586 fn(field_input);
587 if (&field_input != this) {
589 }
590 }
591 }
592}
593
595
596/* -------------------------------------------------------------------- */
599
600FieldOperation::FieldOperation(std::shared_ptr<const mf::MultiFunction> function,
602 : FieldOperation(*function, std::move(inputs))
603{
604 owned_function_ = std::move(function);
605}
606
607/* Avoid generating the destructor in every translation unit. */
609
614static std::shared_ptr<const FieldInputs> combine_field_inputs(Span<GField> fields)
615{
616 /* The #FieldInputs that we try to reuse if possible. */
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();
620 /* Only try to reuse non-empty #FieldInputs. */
621 if (field_inputs && !field_inputs->nodes.is_empty()) {
622 if (field_inputs_candidate == nullptr) {
623 field_inputs_candidate = &field_inputs;
624 }
625 else if ((*field_inputs_candidate)->nodes.size() < field_inputs->nodes.size()) {
626 /* Always try to reuse the #FieldInputs that has the most nodes already. */
627 field_inputs_candidate = &field_inputs;
628 }
629 }
630 }
631 if (field_inputs_candidate == nullptr) {
632 /* None of the field depends on an input. */
633 return {};
634 }
635 /* Check if all inputs are in the candidate. */
636 Vector<const FieldInput *> inputs_not_in_candidate;
637 for (const GField &field : fields) {
638 const std::shared_ptr<const FieldInputs> &field_inputs = field.node().field_inputs();
639 if (!field_inputs) {
640 continue;
641 }
642 if (&field_inputs == field_inputs_candidate) {
643 continue;
644 }
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);
648 }
649 }
650 }
651 if (inputs_not_in_candidate.is_empty()) {
652 /* The existing #FieldInputs can be reused, because no other field has additional inputs. */
653 return *field_inputs_candidate;
654 }
655 /* Create new #FieldInputs that contains all of the inputs that the fields depend on. */
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);
661 }
662 return new_field_inputs;
663}
664
665FieldOperation::FieldOperation(const mf::MultiFunction &function, Vector<GField> inputs)
666 : FieldNode(FieldNodeType::Operation), function_(&function), inputs_(std::move(inputs))
667{
669}
670
672
673/* -------------------------------------------------------------------- */
676
679{
680 std::shared_ptr<FieldInputs> field_inputs = std::make_shared<FieldInputs>();
681 field_inputs->nodes.add_new(this);
682 field_inputs->deduplicated_nodes.add_new(*this);
683 field_inputs_ = std::move(field_inputs);
684}
685
686/* Avoid generating the destructor in every translation unit. */
687FieldInput::~FieldInput() = default;
688
690
691/* -------------------------------------------------------------------- */
694
697{
698 value_ = MEM_mallocN_aligned(type.size, type.alignment, __func__);
699 type.copy_construct(value, value_);
700}
701
703{
704 type_.destruct(value_);
705 MEM_freeN(value_);
706}
707
708const CPPType &FieldConstant::output_cpp_type(int output_index) const
709{
710 BLI_assert(output_index == 0);
711 UNUSED_VARS_NDEBUG(output_index);
712 return type_;
713}
714
716{
717 return type_;
718}
719
721{
722 return {type_, value_};
723}
724
726
727/* -------------------------------------------------------------------- */
730
732 const VArray<bool> &selection,
733 ResourceScope &scope)
734{
735 return IndexMask::from_bools(full_mask, selection, scope.construct<IndexMaskMemory>());
736}
737
739{
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({});
743 return field_index;
744}
745
747{
748 return this->add_with_destination(std::move(field), GVMutableArray::from_span(dst));
749}
750
751int FieldEvaluator::add(GField field, GVArray *varray_ptr)
752{
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{
756 varray_ptr, [](void *dst, const GVArray &varray, ResourceScope & /*scope*/) {
757 *static_cast<GVArray *>(dst) = varray;
758 }});
759 return field_index;
760}
761
763{
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({});
767 return field_index;
768}
769
770static IndexMask evaluate_selection(const Field<bool> &selection_field,
771 const FieldContext &context,
772 const IndexMask &full_mask,
773 ResourceScope &scope)
774{
775 if (selection_field) {
776 VArray<bool> selection =
777 evaluate_fields(scope, {selection_field}, full_mask, context)[0].typed<bool>();
778 return index_mask_from_selection(full_mask, selection, scope);
779 }
780 return full_mask;
781}
782
784{
785 BLI_assert_msg(!is_evaluated_, "Cannot evaluate fields twice.");
786
787 selection_mask_ = evaluate_selection(selection_field_, context_, mask_, scope_);
788
789 Array<GFieldRef> fields(fields_to_evaluate_.size());
790 for (const int i : fields_to_evaluate_.index_range()) {
791 fields[i] = fields_to_evaluate_[i];
792 }
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_);
799 }
800 }
801 is_evaluated_ = true;
802}
803
805{
806 VArray<bool> varray = this->get_evaluated(field_index).typed<bool>();
807
808 if (varray.is_single()) {
809 if (varray.get_internal_single()) {
810 return IndexRange(varray.size());
811 }
812 return IndexRange(0);
813 }
814 return index_mask_from_selection(mask_, varray, scope_);
815}
816
818{
819 BLI_assert(is_evaluated_);
820 return selection_mask_;
821}
822
824
825} // namespace blender::fn
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define UNUSED_VARS_NDEBUG(...)
unsigned long long int uint64_t
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 void * get() const
static GVArray from_empty(const CPPType &type)
static GVArray from_single_default(const CPPType &type, int64_t size)
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 &
Definition BLI_map.hh:884
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
bool remove(const Key &key)
Definition BLI_map.hh:368
bool contains(const Key &key) const
Definition BLI_map.hh:353
T & construct(Args &&...args)
void add_destruct_call(Func func)
void * allocate_owned(const CPPType &type)
LinearAllocator & allocator()
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
static VArray from_func(const int64_t size, GetFunc get_func)
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
FieldConstant(const CPPType &type, const void *value)
Definition field.cc:695
const CPPType & type() const
Definition field.cc:715
const CPPType & output_cpp_type(int output_index) const override
Definition field.cc:708
~FieldConstant() override
Definition field.cc:702
GPointer value() const
Definition field.cc:720
virtual GVArray get_varray_for_input(const FieldInput &field_input, const IndexMask &mask, ResourceScope &scope) const
Definition field.cc:534
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
IndexMask get_evaluated_as_mask(int field_index)
Definition field.cc:804
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:817
int add_with_destination(GField field, GVMutableArray dst)
Definition field.cc:738
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
FieldInput(const CPPType &type, std::string debug_name="")
Definition field.cc:677
std::string debug_name_
Definition FN_field.hh:271
const CPPType * type_
Definition FN_field.hh:270
const CPPType & cpp_type() const
Definition FN_field.hh:632
virtual GVArray get_varray_for_context(const FieldContext &context, const IndexMask &mask, ResourceScope &scope) const =0
blender::StringRef debug_name() const
Definition FN_field.hh:627
FieldNodeType node_type() const
Definition FN_field.hh:549
virtual void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
Definition field.cc:582
std::shared_ptr< const FieldInputs > field_inputs_
Definition FN_field.hh:74
FieldNode(FieldNodeType node_type)
Definition FN_field.hh:547
bool depends_on_input() const
Definition FN_field.hh:554
const std::shared_ptr< const FieldInputs > & field_inputs() const
Definition FN_field.hh:559
Span< GField > inputs() const
Definition FN_field.hh:590
FieldOperation(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition field.cc:600
static std::shared_ptr< FieldOperation > from(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
const mf::MultiFunction & multi_function() const
Definition FN_field.hh:595
const CPPType & cpp_type() const
Definition FN_field.hh:130
const FieldNode & node() const
Definition FN_field.hh:135
static GVArray get_index_varray(const IndexMask &mask)
Definition field.cc:548
GVArray get_varray_for_context(const FieldContext &context, const IndexMask &mask, ResourceScope &scope) const final
Definition field.cc:554
uint64_t hash() const override
Definition field.cc:562
bool is_equal_to(const fn::FieldNode &other) const override
Definition field.cc:568
int users
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:138
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
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)
Definition field.cc:731
GField make_constant_field(const CPPType &type, const void *value)
Definition field.cc:528
static std::shared_ptr< const FieldInputs > combine_field_inputs(Span< GField > fields)
Definition field.cc:614
static FieldTreeInfo preprocess_field_tree(Span< GFieldRef > entry_fields)
Definition field.cc:42
static void build_multi_function_procedure_for_fields(mf::Procedure &procedure, ResourceScope &scope, const FieldTreeInfo &field_tree_info, Span< GFieldRef > output_fields)
Definition field.cc:146
static IndexMask evaluate_selection(const Field< bool > &selection_field, const FieldContext &context, const IndexMask &full_mask, ResourceScope &scope)
Definition field.cc:770
Vector< GVArray > evaluate_fields(ResourceScope &scope, Span< GFieldRef > fields_to_evaluate, const IndexMask &mask, const FieldContext &context, Span< GVMutableArray > dst_varrays={})
Definition field.cc:281
GField make_field_constant_if_possible(GField field)
Definition field.cc:507
static Set< GFieldRef > find_varying_fields(const FieldTreeInfo &field_tree_info, Span< GVArray > field_context_inputs)
Definition field.cc:108
Field< bool > invert_boolean_field(const Field< bool > &field)
Definition field.cc:520
void evaluate_constant_field(const GField &field, void *r_value)
Definition field.cc:493
static Vector< GVArray > get_field_context_inputs(ResourceScope &scope, const IndexMask &mask, const FieldContext &context, const Span< std::reference_wrapper< const FieldInput > > field_inputs)
Definition field.cc:86
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VectorSet< std::reference_wrapper< const FieldInput > > deduplicated_field_inputs
Definition field.cc:36
MultiValueMap< GFieldRef, GFieldRef > field_users
Definition field.cc:31
i
Definition text_draw.cc:230