Blender V4.3
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/* -------------------------------------------------------------------- */
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::ForSingleDefault(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
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);
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. */
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> r_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 r_varrays[i] = GVArray::ForEmpty(type);
295 }
296 return r_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 r_varrays[out_index] = varray;
330 break;
331 }
333 const FieldConstant &field_constant = static_cast<const FieldConstant &>(field.node());
334 r_varrays[out_index] = GVArray::ForSingleRef(
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 (r_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.linear_allocator().allocate(type.size() * array_size, type.alignment());
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 r_varrays[out_index] = GVArray::ForSpan({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 r_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.linear_allocator().allocate(type.size(), type.alignment());
441
442 if (!type.is_trivially_destructible()) {
443 /* Destruct value in the end. */
444 scope.add_destruct_call([buffer, &type]() { type.destruct(buffer); });
445 }
446
447 /* Pass output buffer to the procedure executor. */
448 mf_params.add_uninitialized_single_output({type, buffer, 1});
449
450 /* Create virtual array that can be used after the procedure has been executed below. */
451 const int out_index = constant_field_indices[i];
452 r_varrays[out_index] = GVArray::ForSingleRef(type, array_size, buffer);
453 }
454
455 procedure_executor.call(mask, mf_params, mf_context);
456 }
457
458 /* Copy data to supplied destination arrays if necessary. In some cases the evaluation above
459 * has written the computed data in the right place already. */
460 if (!dst_varrays.is_empty()) {
461 for (const int out_index : fields_to_evaluate.index_range()) {
462 GVMutableArray dst_varray = get_dst_varray(out_index);
463 if (!dst_varray) {
464 /* Caller did not provide a destination for this output. */
465 continue;
466 }
467 const GVArray &computed_varray = r_varrays[out_index];
468 BLI_assert(computed_varray.type() == dst_varray.type());
469 if (is_output_written_to_dst[out_index]) {
470 /* The result has been written into the destination provided by the caller already. */
471 continue;
472 }
473 /* Still have to copy over the data in the destination provided by the caller. */
474 if (dst_varray.is_span()) {
475 array_utils::copy(computed_varray,
476 mask,
477 dst_varray.get_internal_span().take_front(mask.min_array_size()));
478 }
479 else {
480 /* Slower materialize into a different structure. */
481 const CPPType &type = computed_varray.type();
482 threading::parallel_for(mask.index_range(), 2048, [&](const IndexRange range) {
483 BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
484 mask.slice(range).foreach_segment([&](auto segment) {
485 for (const int i : segment) {
486 computed_varray.get_to_uninitialized(i, buffer);
487 dst_varray.set_by_relocate(i, buffer);
488 }
489 });
490 });
491 }
492 r_varrays[out_index] = dst_varray;
493 }
494 }
495 return r_varrays;
496}
497
498void evaluate_constant_field(const GField &field, void *r_value)
499{
500 if (field.node().depends_on_input()) {
501 const CPPType &type = field.cpp_type();
502 type.value_initialize(r_value);
503 return;
504 }
505
506 ResourceScope scope;
507 FieldContext context;
508 Vector<GVArray> varrays = evaluate_fields(scope, {field}, IndexRange(1), context);
509 varrays[0].get_to_uninitialized(0, r_value);
510}
511
513{
514 if (field.node().depends_on_input()) {
515 return field;
516 }
517 const CPPType &type = field.cpp_type();
518 BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
519 evaluate_constant_field(field, buffer);
520 GField new_field = make_constant_field(type, buffer);
521 type.destruct(buffer);
522 return new_field;
523}
524
526{
527 static auto not_fn = mf::build::SI1_SO<bool, bool>(
528 "Not", [](bool a) { return !a; }, mf::build::exec_presets::AllSpanOrSingle());
529 auto not_op = FieldOperation::Create(not_fn, {field});
530 return Field<bool>(not_op);
531}
532
533GField make_constant_field(const CPPType &type, const void *value)
534{
535 auto constant_node = std::make_shared<FieldConstant>(type, value);
536 return GField{std::move(constant_node)};
537}
538
539GVArray FieldContext::get_varray_for_input(const FieldInput &field_input,
540 const IndexMask &mask,
541 ResourceScope &scope) const
542{
543 /* By default ask the field input to create the varray. Another field context might overwrite
544 * the context here. */
545 return field_input.get_varray_for_context(*this, mask, scope);
546}
547
548IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
549{
551}
552
554{
555 auto index_func = [](int i) { return i; };
556 return VArray<int>::ForFunc(mask.min_array_size(), index_func);
557}
558
560 const IndexMask &mask,
561 ResourceScope & /*scope*/) const
562{
563 /* TODO: Investigate a similar method to IndexRange::as_span() */
564 return get_index_varray(mask);
565}
566
568{
569 /* Some random constant hash. */
570 return 128736487678;
571}
572
574{
575 return dynamic_cast<const IndexFieldInput *>(&other) != nullptr;
576}
577
580/* -------------------------------------------------------------------- */
584/* Avoid generating the destructor in every translation unit. */
585FieldNode::~FieldNode() = default;
586
588{
589 if (field_inputs_) {
590 for (const FieldInput &field_input : field_inputs_->deduplicated_nodes) {
591 fn(field_input);
592 if (&field_input != this) {
593 field_input.for_each_field_input_recursive(fn);
594 }
595 }
596 }
597}
598
601/* -------------------------------------------------------------------- */
605FieldOperation::FieldOperation(std::shared_ptr<const mf::MultiFunction> function,
606 Vector<GField> inputs)
607 : FieldOperation(*function, std::move(inputs))
608{
609 owned_function_ = std::move(function);
610}
611
612/* Avoid generating the destructor in every translation unit. */
614
619static std::shared_ptr<const FieldInputs> combine_field_inputs(Span<GField> fields)
620{
621 /* The #FieldInputs that we try to reuse if possible. */
622 const std::shared_ptr<const FieldInputs> *field_inputs_candidate = nullptr;
623 for (const GField &field : fields) {
624 const std::shared_ptr<const FieldInputs> &field_inputs = field.node().field_inputs();
625 /* Only try to reuse non-empty #FieldInputs. */
626 if (field_inputs && !field_inputs->nodes.is_empty()) {
627 if (field_inputs_candidate == nullptr) {
628 field_inputs_candidate = &field_inputs;
629 }
630 else if ((*field_inputs_candidate)->nodes.size() < field_inputs->nodes.size()) {
631 /* Always try to reuse the #FieldInputs that has the most nodes already. */
632 field_inputs_candidate = &field_inputs;
633 }
634 }
635 }
636 if (field_inputs_candidate == nullptr) {
637 /* None of the field depends on an input. */
638 return {};
639 }
640 /* Check if all inputs are in the candidate. */
641 Vector<const FieldInput *> inputs_not_in_candidate;
642 for (const GField &field : fields) {
643 const std::shared_ptr<const FieldInputs> &field_inputs = field.node().field_inputs();
644 if (!field_inputs) {
645 continue;
646 }
647 if (&field_inputs == field_inputs_candidate) {
648 continue;
649 }
650 for (const FieldInput *field_input : field_inputs->nodes) {
651 if (!(*field_inputs_candidate)->nodes.contains(field_input)) {
652 inputs_not_in_candidate.append(field_input);
653 }
654 }
655 }
656 if (inputs_not_in_candidate.is_empty()) {
657 /* The existing #FieldInputs can be reused, because no other field has additional inputs. */
658 return *field_inputs_candidate;
659 }
660 /* Create new #FieldInputs that contains all of the inputs that the fields depend on. */
661 std::shared_ptr<FieldInputs> new_field_inputs = std::make_shared<FieldInputs>(
662 **field_inputs_candidate);
663 for (const FieldInput *field_input : inputs_not_in_candidate) {
664 new_field_inputs->nodes.add(field_input);
665 new_field_inputs->deduplicated_nodes.add(*field_input);
666 }
667 return new_field_inputs;
668}
669
671 : FieldNode(FieldNodeType::Operation), function_(&function), inputs_(std::move(inputs))
672{
674}
675
678/* -------------------------------------------------------------------- */
682FieldInput::FieldInput(const CPPType &type, std::string debug_name)
683 : FieldNode(FieldNodeType::Input), type_(&type), debug_name_(std::move(debug_name))
684{
685 std::shared_ptr<FieldInputs> field_inputs = std::make_shared<FieldInputs>();
686 field_inputs->nodes.add_new(this);
687 field_inputs->deduplicated_nodes.add_new(*this);
688 field_inputs_ = std::move(field_inputs);
689}
690
691/* Avoid generating the destructor in every translation unit. */
692FieldInput::~FieldInput() = default;
693
696/* -------------------------------------------------------------------- */
700FieldConstant::FieldConstant(const CPPType &type, const void *value)
701 : FieldNode(FieldNodeType::Constant), type_(type)
702{
703 value_ = MEM_mallocN_aligned(type.size(), type.alignment(), __func__);
704 type.copy_construct(value, value_);
705}
706
708{
709 type_.destruct(value_);
710 MEM_freeN(value_);
711}
712
713const CPPType &FieldConstant::output_cpp_type(int output_index) const
714{
715 BLI_assert(output_index == 0);
716 UNUSED_VARS_NDEBUG(output_index);
717 return type_;
718}
719
721{
722 return type_;
723}
724
726{
727 return {type_, value_};
728}
729
732/* -------------------------------------------------------------------- */
737 const VArray<bool> &selection,
738 ResourceScope &scope)
739{
740 return IndexMask::from_bools(full_mask, selection, scope.construct<IndexMaskMemory>());
741}
742
744{
745 const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
746 dst_varrays_.append(dst);
747 output_pointer_infos_.append({});
748 return field_index;
749}
750
752{
753 return this->add_with_destination(std::move(field), GVMutableArray::ForSpan(dst));
754}
755
756int FieldEvaluator::add(GField field, GVArray *varray_ptr)
757{
758 const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
759 dst_varrays_.append(nullptr);
760 output_pointer_infos_.append(OutputPointerInfo{
761 varray_ptr, [](void *dst, const GVArray &varray, ResourceScope & /*scope*/) {
762 *static_cast<GVArray *>(dst) = varray;
763 }});
764 return field_index;
765}
766
768{
769 const int field_index = fields_to_evaluate_.append_and_get_index(std::move(field));
770 dst_varrays_.append(nullptr);
771 output_pointer_infos_.append({});
772 return field_index;
773}
774
775static IndexMask evaluate_selection(const Field<bool> &selection_field,
776 const FieldContext &context,
777 IndexMask full_mask,
778 ResourceScope &scope)
779{
780 if (selection_field) {
781 VArray<bool> selection =
782 evaluate_fields(scope, {selection_field}, full_mask, context)[0].typed<bool>();
783 return index_mask_from_selection(full_mask, selection, scope);
784 }
785 return full_mask;
786}
787
789{
790 BLI_assert_msg(!is_evaluated_, "Cannot evaluate fields twice.");
791
792 selection_mask_ = evaluate_selection(selection_field_, context_, mask_, scope_);
793
794 Array<GFieldRef> fields(fields_to_evaluate_.size());
795 for (const int i : fields_to_evaluate_.index_range()) {
796 fields[i] = fields_to_evaluate_[i];
797 }
798 evaluated_varrays_ = evaluate_fields(scope_, fields, selection_mask_, context_, dst_varrays_);
799 BLI_assert(fields_to_evaluate_.size() == evaluated_varrays_.size());
800 for (const int i : fields_to_evaluate_.index_range()) {
801 OutputPointerInfo &info = output_pointer_infos_[i];
802 if (info.dst != nullptr) {
803 info.set(info.dst, evaluated_varrays_[i], scope_);
804 }
805 }
806 is_evaluated_ = true;
807}
808
810{
811 VArray<bool> varray = this->get_evaluated(field_index).typed<bool>();
812
813 if (varray.is_single()) {
814 if (varray.get_internal_single()) {
815 return IndexRange(varray.size());
816 }
817 return IndexRange(0);
818 }
819 return index_mask_from_selection(mask_, varray, scope_);
820}
821
823{
824 BLI_assert(is_evaluated_);
825 return selection_mask_;
826}
827
830} // namespace blender::fn
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define UNUSED_VARS_NDEBUG(...)
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 ForEmpty(const CPPType &type)
static GVArray ForSingleDefault(const CPPType &type, int64_t size)
static GVArray ForSingleRef(const CPPType &type, int64_t size, const void *value)
static GVArray ForSpan(GSpan span)
GMutableSpan get_internal_span() const
static GVMutableArray ForSpan(GMutableSpan span)
void * allocate(const int64_t size, const int64_t alignment)
const Value & lookup(const Key &key) const
Definition BLI_map.hh:506
ValueIterator values() const
Definition BLI_map.hh:846
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
bool remove(const Key &key)
Definition BLI_map.hh:344
bool contains(const Key &key) const
Definition BLI_map.hh:329
T & construct(Args &&...args)
void add_destruct_call(Func func)
LinearAllocator & linear_allocator()
bool contains(const Key &key) const
Definition BLI_set.hh:291
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
constexpr bool contains(const T &value) const
Definition BLI_span.hh:278
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
static VArray ForFunc(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:700
const CPPType & type() const
Definition field.cc:720
const CPPType & output_cpp_type(int output_index) const override
Definition field.cc:713
GPointer value() const
Definition field.cc:725
int add(GField field, GVArray *varray_ptr)
Definition field.cc:756
IndexMask get_evaluated_as_mask(int field_index)
Definition field.cc:809
IndexMask get_evaluated_selection_as_mask() const
Definition field.cc:822
int add_with_destination(GField field, GVMutableArray dst)
Definition field.cc:743
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:450
FieldInput(const CPPType &type, std::string debug_name="")
Definition field.cc:682
virtual GVArray get_varray_for_context(const FieldContext &context, const IndexMask &mask, ResourceScope &scope) const =0
FieldNodeType node_type() const
Definition FN_field.hh:551
virtual void for_each_field_input_recursive(FunctionRef< void(const FieldInput &)> fn) const
Definition field.cc:587
std::shared_ptr< const FieldInputs > field_inputs_
Definition FN_field.hh:76
const std::shared_ptr< const FieldInputs > & field_inputs() const
Definition FN_field.hh:561
Span< GField > inputs() const
Definition FN_field.hh:592
FieldOperation(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition field.cc:605
const mf::MultiFunction & multi_function() const
Definition FN_field.hh:597
static GVArray get_index_varray(const IndexMask &mask)
Definition field.cc:553
GVArray get_varray_for_context(const FieldContext &context, const IndexMask &mask, ResourceScope &scope) const final
Definition field.cc:559
uint64_t hash() const override
Definition field.cc:567
bool is_equal_to(const fn::FieldNode &other) const override
Definition field.cc:573
ParamType param_type(int param_index) const
const MultiFunction & construct_function(Args &&...args)
Variable & new_variable(DataType data_type, std::string name="")
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
int users
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:110
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float4 mask(const int4 mask, const float4 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:736
GField make_constant_field(const CPPType &type, const void *value)
Definition field.cc:533
static std::shared_ptr< const FieldInputs > combine_field_inputs(Span< GField > fields)
Definition field.cc:619
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
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:512
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:525
void evaluate_constant_field(const GField &field, void *r_value)
Definition field.cc:498
static IndexMask evaluate_selection(const Field< bool > &selection_field, const FieldContext &context, IndexMask full_mask, ResourceScope &scope)
Definition field.cc:775
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:95
unsigned __int64 uint64_t
Definition stdint.h:90
VectorSet< std::reference_wrapper< const FieldInput > > deduplicated_field_inputs
Definition field.cc:36
MultiValueMap< GFieldRef, GFieldRef > field_users
Definition field.cc:31