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