Blender V5.0
geometry_nodes_closure_zone.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <fmt/format.h>
6
9
12#include "BKE_node_runtime.hh"
15
16#include "NOD_geo_closure.hh"
19
21
23
25
26namespace blender::nodes {
27
28using bke::node_tree_reference_lifetimes::ReferenceSetInfo;
30
39 private:
44 const lf::FunctionNode *body_node_;
45
46 public:
48 : body_node_(&body_node)
49 {
50 }
51
53 const lf::Context &context) const override
54 {
55 const GeoNodesUserData &user_data = *dynamic_cast<GeoNodesUserData *>(context.user_data);
56 const ComputeContextHash &context_hash = user_data.compute_context->hash();
57 if (!user_data.call_data->side_effect_nodes) {
58 /* There are no requested side effect nodes at all. */
59 return {};
60 }
61 const Span<const lf::FunctionNode *> side_effect_nodes_in_closure =
62 user_data.call_data->side_effect_nodes->nodes_by_context.lookup(context_hash);
63 if (side_effect_nodes_in_closure.is_empty()) {
64 /* The closure does not have any side effect nodes, so the wrapper also does not have any. */
65 return {};
66 }
67 return {body_node_};
68 }
69};
70
76 private:
77 const bNodeTree &btree_;
78 const bke::bNodeTreeZone &zone_;
79 const bNode &output_bnode_;
80 const ZoneBuildInfo &zone_info_;
81 const ZoneBodyFunction &body_fn_;
82 std::shared_ptr<ClosureSignature> closure_signature_;
83
84 public:
86 const bke::bNodeTreeZone &zone,
87 ZoneBuildInfo &zone_info,
88 const ZoneBodyFunction &body_fn)
89 : btree_(btree),
90 zone_(zone),
91 output_bnode_(*zone.output_node()),
92 zone_info_(zone_info),
93 body_fn_(body_fn)
94 {
95 debug_name_ = "Closure Zone";
96
97 initialize_zone_wrapper(zone, zone_info, body_fn, false, inputs_, outputs_);
98 for (const auto item : body_fn.indices.inputs.reference_sets.items()) {
99 const ReferenceSetInfo &reference_set =
100 btree.runtime->reference_lifetimes_info->reference_sets[item.key];
101 if (reference_set.type == ReferenceSetType::ClosureInputReferenceSet) {
102 BLI_assert(&reference_set.socket->owner_node() != zone_.input_node());
103 }
104 if (reference_set.type == ReferenceSetType::ClosureOutputData) {
105 if (&reference_set.socket->owner_node() == zone_.output_node()) {
106 /* This reference set comes from the caller of the closure and is not captured at the
107 * place where the closure is created. */
108 continue;
109 }
110 }
112 item.key,
113 inputs_.append_and_get_index_as("Reference Set",
115 }
116
117 /* All border links are used. */
118 for (const int i : zone_.border_links.index_range()) {
120 }
121
122 const auto &storage = *static_cast<const NodeClosureOutput *>(output_bnode_.storage);
123
124 closure_signature_ = std::make_shared<ClosureSignature>();
125
126 for (const int i : IndexRange(storage.input_items.items_num)) {
127 const bNodeSocket &bsocket = zone_.input_node()->output_socket(i);
128 closure_signature_->inputs.add({bsocket.name, bsocket.typeinfo});
129 }
130 for (const int i : IndexRange(storage.output_items.items_num)) {
131 const bNodeSocket &bsocket = zone_.output_node()->input_socket(i);
132 closure_signature_->outputs.add({bsocket.name, bsocket.typeinfo});
133 }
134 }
135
136 void execute_impl(lf::Params &params, const lf::Context &context) const override
137 {
138 auto &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
139
140 /* All border links are captured currently. */
141 for (const int i : zone_.border_links.index_range()) {
142 params.set_output(zone_info_.indices.outputs.border_link_usages[i], true);
143 }
144
145 const auto &storage = *static_cast<const NodeClosureOutput *>(output_bnode_.storage);
146
147 std::unique_ptr<ResourceScope> closure_scope = std::make_unique<ResourceScope>();
148
149 lf::Graph &lf_graph = closure_scope->construct<lf::Graph>("Closure Graph");
150 lf::FunctionNode &lf_body_node = lf_graph.add_function(*body_fn_.function);
151 ClosureFunctionIndices closure_indices;
152 Vector<bke::SocketValueVariant> default_input_values;
153
154 for (const int i : IndexRange(storage.input_items.items_num)) {
155 const NodeClosureInputItem &item = storage.input_items.items[i];
156 const bNodeSocket &bsocket = zone_.input_node()->output_socket(i);
157
158 lf::GraphInputSocket &lf_graph_input = lf_graph.add_input(
160 lf_graph.add_link(lf_graph_input, lf_body_node.input(body_fn_.indices.inputs.main[i]));
161
162 lf::GraphOutputSocket &lf_graph_input_usage = lf_graph.add_output(
163 CPPType::get<bool>(), "Usage: " + StringRef(item.name));
164 lf_graph.add_link(lf_body_node.output(body_fn_.indices.outputs.input_usages[i]),
165 lf_graph_input_usage);
166
167 default_input_values.append(*bsocket.typeinfo->geometry_nodes_default_value);
168 }
169 closure_indices.inputs.main = lf_graph.graph_inputs().index_range().take_back(
170 storage.input_items.items_num);
171 closure_indices.outputs.input_usages = lf_graph.graph_outputs().index_range().take_back(
172 storage.input_items.items_num);
173
174 for (const int i : IndexRange(storage.output_items.items_num)) {
175 const NodeClosureOutputItem &item = storage.output_items.items[i];
176
177 lf::GraphOutputSocket &lf_graph_output = lf_graph.add_output(
179 lf_graph.add_link(lf_body_node.output(body_fn_.indices.outputs.main[i]), lf_graph_output);
180
181 lf::GraphInputSocket &lf_graph_output_usage = lf_graph.add_input(
182 CPPType::get<bool>(), "Usage: " + StringRef(item.name));
183 lf_graph.add_link(lf_graph_output_usage,
184 lf_body_node.input(body_fn_.indices.inputs.output_usages[i]));
185 }
186 closure_indices.outputs.main = lf_graph.graph_outputs().index_range().take_back(
187 storage.output_items.items_num);
188 closure_indices.inputs.output_usages = lf_graph.graph_inputs().index_range().take_back(
189 storage.output_items.items_num);
190
191 for (const int i : zone_.border_links.index_range()) {
192 bke::SocketValueVariant *input_ptr = params.try_get_input_data_ptr<bke::SocketValueVariant>(
193 zone_info_.indices.inputs.border_links[i]);
194 bke::SocketValueVariant &stored_ptr = closure_scope->construct<bke::SocketValueVariant>(
195 std::move(*input_ptr));
196 lf_body_node.input(body_fn_.indices.inputs.border_links[i]).set_default_value(&stored_ptr);
197 }
198
199 for (const auto &item : body_fn_.indices.inputs.reference_sets.items()) {
200 const ReferenceSetInfo &reference_set =
201 btree_.runtime->reference_lifetimes_info->reference_sets[item.key];
202 if (reference_set.type == ReferenceSetType::ClosureOutputData) {
203 const bNodeSocket &socket = *reference_set.socket;
204 const bNode &node = socket.owner_node();
205 if (&node == zone_.output_node()) {
206 /* This reference set is passed in by the code that invokes the closure. */
207 lf::GraphInputSocket &lf_graph_input = lf_graph.add_input(
209 StringRef("Reference Set: ") + reference_set.socket->name);
210 lf_graph.add_link(
211 lf_graph_input,
212 lf_body_node.input(body_fn_.indices.inputs.reference_sets.lookup(item.key)));
213 closure_indices.inputs.output_data_reference_sets.add_new(reference_set.socket->index(),
214 lf_graph_input.index());
215 continue;
216 }
217 }
218
219 auto &input_reference_set = *params.try_get_input_data_ptr<bke::GeometryNodesReferenceSet>(
220 zone_info_.indices.inputs.reference_sets.lookup(item.key));
221 auto &stored = closure_scope->construct<bke::GeometryNodesReferenceSet>(
222 std::move(input_reference_set));
223 lf_body_node.input(body_fn_.indices.inputs.reference_sets.lookup(item.key))
224 .set_default_value(&stored);
225 }
226
227 const bNodeTree &btree_orig = *DEG_get_original(&btree_);
228 if (btree_orig.runtime->logged_zone_graphs) {
229 std::lock_guard lock{btree_orig.runtime->logged_zone_graphs->mutex};
230 btree_orig.runtime->logged_zone_graphs->graph_by_zone_id.lookup_or_add_cb(
231 output_bnode_.identifier, [&]() { return lf_graph.to_dot(); });
232 }
233
234 lf_graph.update_node_indices();
235
236 const auto &side_effect_provider =
237 closure_scope->construct<ClosureIntermediateGraphSideEffectProvider>(lf_body_node);
238 lf::GraphExecutor &lf_graph_executor = closure_scope->construct<lf::GraphExecutor>(
239 lf_graph, nullptr, &side_effect_provider, nullptr);
240 ClosureSourceLocation source_location{
241 &btree_,
242 output_bnode_.identifier,
243 user_data.compute_context->hash(),
244 };
245 ClosurePtr closure{MEM_new<Closure>(__func__,
246 closure_signature_,
247 std::move(closure_scope),
248 lf_graph_executor,
249 closure_indices,
250 std::move(default_input_values),
251 source_location,
252 std::make_shared<ClosureEvalLog>())};
253
254 params.set_output(zone_info_.indices.outputs.main[0],
255 bke::SocketValueVariant::From(std::move(closure)));
256 }
257};
258
263 std::optional<lf::GraphExecutor> graph_executor;
264 std::optional<ClosureIntermediateGraphSideEffectProvider> side_effect_provider;
265 void *graph_executor_storage = nullptr;
266};
267
274 private:
275 const bNodeTree &btree_;
276 const bNode &bnode_;
278
279 public:
281 : btree_(bnode.owner_tree()), bnode_(bnode)
282 {
283 debug_name_ = bnode.name;
284 for (const int i : bnode.input_sockets().index_range().drop_back(1)) {
285 const bNodeSocket &bsocket = bnode.input_socket(i);
286 indices_.inputs.main.append(inputs_.append_and_get_index_as(
287 bsocket.name, CPPType::get<bke::SocketValueVariant>(), lf::ValueUsage::Maybe));
288 indices_.outputs.input_usages.append(
289 outputs_.append_and_get_index_as("Usage", CPPType::get<bool>()));
290 }
291 /* The closure input is always used. */
292 inputs_[indices_.inputs.main[0]].usage = lf::ValueUsage::Used;
293 for (const int i : bnode.output_sockets().index_range().drop_back(1)) {
294 const bNodeSocket &bsocket = bnode.output_socket(i);
295 indices_.outputs.main.append(
296 outputs_.append_and_get_index_as(bsocket.name, CPPType::get<bke::SocketValueVariant>()));
297 indices_.inputs.output_usages.append(
298 inputs_.append_and_get_index_as("Usage", CPPType::get<bool>(), lf::ValueUsage::Maybe));
299 if (bke::node_tree_reference_lifetimes::can_contain_referenced_data(
300 eNodeSocketDatatype(bsocket.type)))
301 {
302 const int input_i = inputs_.append_and_get_index_as(
303 "Reference Set",
304 CPPType::get<bke::GeometryNodesReferenceSet>(),
305 lf::ValueUsage::Maybe);
306 indices_.inputs.reference_set_by_output.add(i, input_i);
307 }
308 }
309 }
310
312 {
313 return indices_;
314 }
315
316 void *init_storage(LinearAllocator<> &allocator) const override
317 {
318 return allocator.construct<EvaluateClosureEvalStorage>().release();
319 }
320
321 void destruct_storage(void *storage) const override
322 {
323 auto *s = static_cast<EvaluateClosureEvalStorage *>(storage);
324 if (s->graph_executor_storage) {
325 s->graph_executor->destruct_storage(s->graph_executor_storage);
326 }
327 std::destroy_at(s);
328 }
329
330 void execute_impl(lf::Params &params, const lf::Context &context) const override
331 {
332 const ScopedNodeTimer node_timer{context, bnode_};
333
334 auto &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
335 auto &eval_storage = *static_cast<EvaluateClosureEvalStorage *>(context.storage);
336 auto local_user_data = *static_cast<GeoNodesLocalUserData *>(context.local_user_data);
337
338 if (!eval_storage.graph_executor) {
339 if (this->is_recursive_call(user_data)) {
340 if (geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(
341 user_data))
342 {
343 tree_logger->node_warnings.append(
344 *tree_logger->allocator,
345 {bnode_.identifier,
346 {NodeWarningType::Error, TIP_("Recursive closure is not allowed")}});
347 }
349 return;
350 }
351
352 eval_storage.closure = params.extract_input<bke::SocketValueVariant>(indices_.inputs.main[0])
354 if (eval_storage.closure) {
355 this->generate_closure_compatibility_warnings(*eval_storage.closure, context);
356 this->initialize_execution_graph(eval_storage);
357
358 const bNodeTree &btree_orig = *DEG_get_original(&btree_);
359 ClosureEvalLocation eval_location{
360 btree_orig.id.session_uid, bnode_.identifier, user_data.compute_context->hash()};
361 eval_storage.closure->log_evaluation(eval_location);
362 }
363 else {
364 /* If no closure is provided, the Evaluate Closure node behaves as if it was muted. So some
365 * values may be passed through if there are internal links. */
366 this->initialize_pass_through_graph(eval_storage);
367 }
368 }
369
370 const std::optional<ClosureSourceLocation> closure_source_location =
371 eval_storage.closure ? eval_storage.closure->source_location() : std::nullopt;
372
373 bke::EvaluateClosureComputeContext closure_compute_context{
374 user_data.compute_context, bnode_.identifier, &btree_, closure_source_location};
375 GeoNodesUserData closure_user_data = user_data;
376 closure_user_data.compute_context = &closure_compute_context;
377 closure_user_data.log_socket_values = should_log_socket_values_for_context(
378 user_data, closure_compute_context.hash());
379 GeoNodesLocalUserData closure_local_user_data{closure_user_data};
380
381 lf::Context eval_graph_context{
382 eval_storage.graph_executor_storage, &closure_user_data, &closure_local_user_data};
383 eval_storage.graph_executor->execute(params, eval_graph_context);
384 }
385
386 bool is_recursive_call(const GeoNodesUserData &user_data) const
387 {
388 for (const ComputeContext *context = user_data.compute_context; context;
389 context = context->parent())
390 {
391 if (const auto *closure_context = dynamic_cast<const bke::EvaluateClosureComputeContext *>(
392 context))
393 {
394 if (closure_context->node() == &bnode_) {
395 return true;
396 }
397 }
398 }
399 return false;
400 }
401
403 {
404 for (const bNodeSocket *bsocket : bnode_.output_sockets().drop_back(1)) {
405 const int index = bsocket->index();
406 set_default_value_for_output_socket(params, indices_.outputs.main[index], *bsocket);
407 }
408 for (const bNodeSocket *bsocket : bnode_.input_sockets().drop_back(1)) {
409 params.set_output(indices_.outputs.input_usages[bsocket->index()], false);
410 }
411 }
412
414 const lf::Context &context) const
415 {
416 const auto &node_storage = *static_cast<const NodeEvaluateClosure *>(bnode_.storage);
417 const auto &user_data = *static_cast<GeoNodesUserData *>(context.user_data);
418 const auto &local_user_data = *static_cast<GeoNodesLocalUserData *>(context.local_user_data);
419 geo_eval_log::GeoTreeLogger *tree_logger = local_user_data.try_get_tree_logger(user_data);
420 if (tree_logger == nullptr) {
421 return;
422 }
423 const ClosureSignature &signature = closure.signature();
424 for (const NodeEvaluateClosureInputItem &item :
425 Span{node_storage.input_items.items, node_storage.input_items.items_num})
426 {
427 const bke::bNodeSocketType *item_type = bke::node_socket_type_find_static(item.socket_type);
428 if (const std::optional<int> i = signature.find_input_index(item.name)) {
429 const ClosureSignature::Item &closure_item = signature.inputs[*i];
430 if (!btree_.typeinfo->validate_link(eNodeSocketDatatype(item.socket_type),
431 eNodeSocketDatatype(closure_item.type->type)))
432 {
433 tree_logger->node_warnings.append(
434 *tree_logger->allocator,
435 {bnode_.identifier,
436 {NodeWarningType::Error,
437 fmt::format("{}: {} \"{}\" ({} " BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE
438 " {})",
439 TIP_("Conversion not supported when evaluating closure"),
440 TIP_("Input"),
441 item.name,
442 TIP_(item_type->label),
443 TIP_(closure_item.type->label))}});
444 }
445 else if (item.socket_type != closure_item.type->type) {
446 tree_logger->node_warnings.append(
447 *tree_logger->allocator,
448 {bnode_.identifier,
449 {NodeWarningType::Info,
450 fmt::format("{}: {} \"{}\" ({} " BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE
451 " {})",
452 TIP_("Implicit type conversion when evaluating closure"),
453 TIP_("Input"),
454 item.name,
455 TIP_(item_type->label),
456 TIP_(closure_item.type->label))}});
457 }
458 }
459 else {
460 tree_logger->node_warnings.append(
461 *tree_logger->allocator,
462 {bnode_.identifier,
463 {
464 NodeWarningType::Error,
465 fmt::format(fmt::runtime(TIP_("Closure does not have input: \"{}\"")), item.name),
466 }});
467 }
468 }
469 for (const NodeEvaluateClosureOutputItem &item :
470 Span{node_storage.output_items.items, node_storage.output_items.items_num})
471 {
472 const bke::bNodeSocketType *item_type = bke::node_socket_type_find_static(item.socket_type);
473 if (const std::optional<int> i = signature.find_output_index(item.name)) {
474 const ClosureSignature::Item &closure_item = signature.outputs[*i];
475 if (!btree_.typeinfo->validate_link(eNodeSocketDatatype(closure_item.type->type),
476 eNodeSocketDatatype(item.socket_type)))
477 {
478 tree_logger->node_warnings.append(
479 *tree_logger->allocator,
480 {bnode_.identifier,
481 {NodeWarningType::Error,
482 fmt::format("{}: {} \"{}\" ({} " BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE
483 " {})",
484 TIP_("Conversion not supported when evaluating closure"),
485 TIP_("Output"),
486 item.name,
487 TIP_(closure_item.type->label),
488 TIP_(item_type->label))}});
489 }
490 else if (item.socket_type != closure_item.type->type) {
491 tree_logger->node_warnings.append(
492 *tree_logger->allocator,
493 {bnode_.identifier,
494 {NodeWarningType::Info,
495 fmt::format("{}: {} \"{}\" ({} " BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE
496 " {})",
497 TIP_("Implicit type conversion when evaluating closure"),
498 TIP_("Output"),
499 item.name,
500 TIP_(closure_item.type->label),
501 TIP_(item_type->label))}});
502 }
503 }
504 else {
505 tree_logger->node_warnings.append(
506 *tree_logger->allocator,
507 {bnode_.identifier,
508 {NodeWarningType::Error,
509 fmt::format(fmt::runtime(TIP_("Closure does not have output: \"{}\"")),
510 item.name)}});
511 }
512 }
513 }
514
516 {
517 const auto &node_storage = *static_cast<const NodeEvaluateClosure *>(bnode_.storage);
518
519 lf::Graph &lf_graph = eval_storage.graph;
520
521 for (const lf::Input &input : inputs_) {
522 lf_graph.add_input(*input.type, input.debug_name);
523 }
524 for (const lf::Output &output : outputs_) {
525 lf_graph.add_output(*output.type, output.debug_name);
526 }
527 const Span<lf::GraphInputSocket *> lf_graph_inputs = lf_graph.graph_inputs();
528 const Span<lf::GraphOutputSocket *> lf_graph_outputs = lf_graph.graph_outputs();
529
530 const Closure &closure = *eval_storage.closure;
531 const ClosureSignature &closure_signature = closure.signature();
532 const ClosureFunctionIndices &closure_indices = closure.indices();
533
534 Array<std::optional<int>> inputs_map(node_storage.input_items.items_num);
535 for (const int i : inputs_map.index_range()) {
536 inputs_map[i] = closure_signature.find_input_index(node_storage.input_items.items[i].name);
537 }
538 Array<std::optional<int>> outputs_map(node_storage.output_items.items_num);
539 for (const int i : outputs_map.index_range()) {
540 outputs_map[i] = closure_signature.find_output_index(
541 node_storage.output_items.items[i].name);
542 }
543
544 lf::FunctionNode &lf_closure_node = lf_graph.add_function(closure.function());
545
546 static constexpr bool static_true = true;
547 static constexpr bool static_false = false;
548 /* The closure input is always used. */
549 lf_graph_outputs[indices_.outputs.input_usages[0]]->set_default_value(&static_true);
550
551 for (const int input_item_i : IndexRange(node_storage.input_items.items_num)) {
552 lf::GraphOutputSocket &lf_usage_output =
553 *lf_graph_outputs[indices_.outputs.input_usages[input_item_i + 1]];
554 if (const std::optional<int> mapped_i = inputs_map[input_item_i]) {
555 const bke::bNodeSocketType &from_type = *bnode_.input_socket(input_item_i + 1).typeinfo;
556 const bke::bNodeSocketType &to_type = *closure_signature.inputs[*mapped_i].type;
557 lf::OutputSocket *lf_from = lf_graph_inputs[indices_.inputs.main[input_item_i + 1]];
558 lf::InputSocket &lf_to = lf_closure_node.input(closure_indices.inputs.main[*mapped_i]);
559 if (&from_type != &to_type) {
560 if (const LazyFunction *conversion_fn = build_implicit_conversion_lazy_function(
561 from_type, to_type, eval_storage.scope))
562 {
563 /* The provided type when evaluating the closure may be different from what the closure
564 * expects exactly, so do an implicit conversion. */
565 lf::Node &conversion_node = lf_graph.add_function(*conversion_fn);
566 lf_graph.add_link(*lf_from, conversion_node.input(0));
567 lf_from = &conversion_node.output(0);
568 }
569 else {
570 /* Use the default value if the provided input value is not compatible with what the
571 * closure expects. */
572 lf_to.set_default_value(&closure.default_input_value(*mapped_i));
573 lf_usage_output.set_default_value(&static_false);
574 continue;
575 }
576 }
577 lf_graph.add_link(*lf_from, lf_to);
578 lf_graph.add_link(lf_closure_node.output(closure_indices.outputs.input_usages[*mapped_i]),
579 lf_usage_output);
580 }
581 else {
582 lf_usage_output.set_default_value(&static_false);
583 }
584 }
585
586 for (const int output_item_i : IndexRange(node_storage.output_items.items_num)) {
587 lf::GraphOutputSocket &lf_main_output =
588 *lf_graph_outputs[indices_.outputs.main[output_item_i]];
589 const bke::bNodeSocketType &main_output_type = *bnode_.output_socket(output_item_i).typeinfo;
590 if (const std::optional<int> mapped_i = outputs_map[output_item_i]) {
591 const bke::bNodeSocketType &closure_output_type =
592 *closure_signature.outputs[*mapped_i].type;
593 lf::OutputSocket *lf_from = &lf_closure_node.output(
594 closure_indices.outputs.main[*mapped_i]);
595 if (&closure_output_type != &main_output_type) {
596 if (const LazyFunction *conversion_fn = build_implicit_conversion_lazy_function(
597 closure_output_type, main_output_type, eval_storage.scope))
598 {
599 /* Convert the type of the value coming out of the closure to the output socket type of
600 * the evaluation. */
601 lf::Node &conversion_node = lf_graph.add_function(*conversion_fn);
602 lf_graph.add_link(*lf_from, conversion_node.input(0));
603 lf_from = &conversion_node.output(0);
604 }
605 else {
606 /* The socket types are not compatible, so use the default value. */
607 lf_main_output.set_default_value(main_output_type.geometry_nodes_default_value);
608 continue;
609 }
610 }
611 /* Link the output of the closure to the output of the entire evaluation. */
612 lf_graph.add_link(*lf_from, lf_main_output);
613 lf_graph.add_link(*lf_graph_inputs[indices_.inputs.output_usages[output_item_i]],
614 lf_closure_node.input(closure_indices.inputs.output_usages[*mapped_i]));
615 }
616 else {
617 lf_main_output.set_default_value(main_output_type.geometry_nodes_default_value);
618 }
619 }
620
621 for (const int i : closure_indices.inputs.main.index_range()) {
622 lf::InputSocket &lf_closure_input = lf_closure_node.input(closure_indices.inputs.main[i]);
623 if (lf_closure_input.origin()) {
624 /* Handled already. */
625 continue;
626 }
627 lf_closure_input.set_default_value(&closure.default_input_value(i));
628 }
629
630 static const bke::GeometryNodesReferenceSet static_empty_reference_set;
631 for (const int i : closure_indices.outputs.main.index_range()) {
632 lf::OutputSocket &lf_closure_output = lf_closure_node.output(
633 closure_indices.outputs.main[i]);
634 if (const std::optional<int> lf_reference_set_input_i =
636 {
637 lf::InputSocket &lf_reference_set_input = lf_closure_node.input(*lf_reference_set_input_i);
638 const int node_output_i = outputs_map.as_span().first_index_try(i);
639 if (node_output_i == -1) {
640 lf_reference_set_input.set_default_value(&static_empty_reference_set);
641 }
642 else {
643 if (const std::optional<int> lf_evaluate_node_reference_set_input_i =
644 indices_.inputs.reference_set_by_output.lookup_try(node_output_i))
645 {
646 lf_graph.add_link(*lf_graph_inputs[*lf_evaluate_node_reference_set_input_i],
647 lf_reference_set_input);
648 }
649 else {
650 lf_reference_set_input.set_default_value(&static_empty_reference_set);
651 }
652 }
653 }
654 if (!lf_closure_output.targets().is_empty()) {
655 /* Handled already. */
656 continue;
657 }
658 lf_closure_node.input(closure_indices.inputs.output_usages[i])
659 .set_default_value(&static_false);
660 }
661
662 lf_graph.update_node_indices();
663 eval_storage.side_effect_provider.emplace(lf_closure_node);
664 eval_storage.graph_executor.emplace(
665 lf_graph, nullptr, &*eval_storage.side_effect_provider, nullptr);
666 eval_storage.graph_executor_storage = eval_storage.graph_executor->init_storage(
667 eval_storage.scope.allocator());
668
669 /* Log graph for debugging purposes. */
670 const bNodeTree &btree_orig = *DEG_get_original(&btree_);
671 if (btree_orig.runtime->logged_zone_graphs) {
672 std::lock_guard lock{btree_orig.runtime->logged_zone_graphs->mutex};
673 btree_orig.runtime->logged_zone_graphs->graph_by_zone_id.lookup_or_add_cb(
674 bnode_.identifier, [&]() { return lf_graph.to_dot(); });
675 }
676 }
677
679 {
680 const auto &node_storage = *static_cast<const NodeEvaluateClosure *>(bnode_.storage);
681 lf::Graph &lf_graph = eval_storage.graph;
682 for (const lf::Input &input : inputs_) {
683 lf_graph.add_input(*input.type, input.debug_name);
684 }
685 for (const lf::Output &output : outputs_) {
686 lf_graph.add_output(*output.type, output.debug_name);
687 }
688 const Span<lf::GraphInputSocket *> lf_graph_inputs = lf_graph.graph_inputs();
689 const Span<lf::GraphOutputSocket *> lf_graph_outputs = lf_graph.graph_outputs();
690
691 for (const int output_item_i : IndexRange(node_storage.output_items.items_num)) {
692 const bNodeSocket &output_bsocket = bnode_.output_socket(output_item_i);
694 output_bsocket);
695 lf::GraphOutputSocket &lf_main_output =
696 *lf_graph_outputs[indices_.outputs.main[output_item_i]];
697 lf::GraphInputSocket &lf_usage_input =
698 *lf_graph_inputs[indices_.inputs.output_usages[output_item_i]];
699 const bke::bNodeSocketType &output_type = *output_bsocket.typeinfo;
700 if (input_bsocket) {
701 lf::OutputSocket &lf_main_input =
702 *lf_graph_inputs[indices_.inputs.main[input_bsocket->index()]];
703 lf::GraphOutputSocket &lf_usage_output =
704 *lf_graph_outputs[indices_.outputs.input_usages[input_bsocket->index()]];
705 const bke::bNodeSocketType &input_type = *input_bsocket->typeinfo;
706 if (&input_type == &output_type) {
707 lf_graph.add_link(lf_main_input, lf_main_output);
708 lf_graph.add_link(lf_usage_input, lf_usage_output);
709 continue;
710 }
711 if (const LazyFunction *conversion_fn = build_implicit_conversion_lazy_function(
712 input_type, output_type, eval_storage.scope))
713 {
714 lf::Node &conversion_node = lf_graph.add_function(*conversion_fn);
715 lf_graph.add_link(lf_main_input, conversion_node.input(0));
716 lf_graph.add_link(conversion_node.output(0), lf_main_output);
717 lf_graph.add_link(lf_usage_input, lf_usage_output);
718 continue;
719 }
720 }
721 lf_main_output.set_default_value(output_type.geometry_nodes_default_value);
722 }
723
724 static constexpr bool static_false = false;
725 for (const int usage_i : indices_.outputs.input_usages) {
726 lf::GraphOutputSocket &lf_usage_output = *lf_graph_outputs[usage_i];
727 if (!lf_usage_output.origin()) {
728 lf_usage_output.set_default_value(&static_false);
729 }
730 }
731
732 lf_graph.update_node_indices();
733 eval_storage.graph_executor.emplace(lf_graph, nullptr, nullptr, nullptr);
734 eval_storage.graph_executor_storage = eval_storage.graph_executor->init_storage(
735 eval_storage.scope.allocator());
736 }
737};
738
740{
741 const LazyFunction &fn = closure.function();
742 const ClosureFunctionIndices &indices = closure.indices();
743 const ClosureSignature &signature = closure.signature();
744 const int fn_inputs_num = fn.inputs().size();
745 const int fn_outputs_num = fn.outputs().size();
746
747 ResourceScope scope;
748 LinearAllocator<> &allocator = scope.allocator();
749
750 GeoNodesLocalUserData local_user_data(*params.user_data);
751 void *storage = fn.init_storage(allocator);
752 lf::Context lf_context{storage, params.user_data, &local_user_data};
753
754 Array<GMutablePointer> lf_input_values(fn_inputs_num);
755 Array<GMutablePointer> lf_output_values(fn_outputs_num);
756 Array<std::optional<lf::ValueUsage>> lf_input_usages(fn_inputs_num);
757 Array<lf::ValueUsage> lf_output_usages(fn_outputs_num, lf::ValueUsage::Unused);
758 Array<bool> lf_set_outputs(fn_outputs_num, false);
759
760 Array<std::optional<int>> inputs_map(params.inputs.size());
761 for (const int i : inputs_map.index_range()) {
762 inputs_map[i] = signature.find_input_index(params.inputs[i].key);
763 }
764 Array<std::optional<int>> outputs_map(params.outputs.size());
765 for (const int i : outputs_map.index_range()) {
766 outputs_map[i] = signature.find_output_index(params.outputs[i].key);
767 }
768
769 for (const int input_item_i : params.inputs.index_range()) {
770 ClosureEagerEvalParams::InputItem &item = params.inputs[input_item_i];
771 if (const std::optional<int> mapped_i = inputs_map[input_item_i]) {
772 const bke::bNodeSocketType &from_type = *item.type;
773 const bke::bNodeSocketType &to_type = *signature.inputs[*mapped_i].type;
774 bke::SocketValueVariant input_value;
775 if (std::optional<bke::SocketValueVariant> value = implicitly_convert_socket_value(
776 from_type, item.value, to_type))
777 {
778 input_value = *value;
779 }
780 else {
781 input_value = *to_type.geometry_nodes_default_value;
782 }
783 lf_input_values[indices.inputs.main[*mapped_i]] = {
785 allocator.construct<bke::SocketValueVariant>(std::move(input_value)).release()};
786 }
787 else {
788 /* Provided input value is ignored. */
789 }
790 }
791 for (const int output_item_i : params.outputs.index_range()) {
792 if (const std::optional<int> mapped_i = outputs_map[output_item_i]) {
793 /* Tell the closure that this output is used. */
794 lf_input_values[indices.inputs.output_usages[*mapped_i]] = {
795 CPPType::get<bool>(), allocator.construct<bool>(true).release()};
796 lf_output_usages[indices.outputs.main[*mapped_i]] = lf::ValueUsage::Used;
797 }
798 }
799
800 /* Set remaining main inputs to their default values. */
801 for (const int main_input_i : indices.inputs.main.index_range()) {
802 const int lf_input_i = indices.inputs.main[main_input_i];
803 if (!lf_input_values[lf_input_i]) {
805 closure.default_input_value(main_input_i));
806 lf_input_values[lf_input_i] = &value;
807 }
808 lf_output_values[indices.outputs.input_usages[main_input_i]] = allocator.allocate<bool>();
809 }
810 /* Set remaining output usages to false. */
811 for (const int output_usage_i : indices.inputs.output_usages.index_range()) {
812 const int lf_input_i = indices.inputs.output_usages[output_usage_i];
813 if (!lf_input_values[lf_input_i]) {
814 lf_input_values[lf_input_i] = {CPPType::get<bool>(),
815 allocator.construct<bool>(false).release()};
816 }
817 }
819 for (auto &&[main_output_i, lf_input_i] : indices.inputs.output_data_reference_sets.items()) {
820 /* TODO: Propagate all attributes or let the caller decide. */
821 auto *value = &scope.construct<bke::GeometryNodesReferenceSet>();
822 lf_input_values[lf_input_i] = {value};
823 }
825 for (const int main_output_i : indices.outputs.main.index_range()) {
826 lf_output_values[indices.outputs.main[main_output_i]] =
828 }
829
830 lf::BasicParams lf_params{
831 fn, lf_input_values, lf_output_values, lf_input_usages, lf_output_usages, lf_set_outputs};
832 fn.execute(lf_params, lf_context);
833 fn.destruct_storage(storage);
834
835 for (const int output_item_i : params.outputs.index_range()) {
836 ClosureEagerEvalParams::OutputItem &item = params.outputs[output_item_i];
837 if (const std::optional<int> mapped_i = outputs_map[output_item_i]) {
838 const bke::bNodeSocketType &from_type = *signature.outputs[*mapped_i].type;
839 const bke::bNodeSocketType &to_type = *item.type;
840 if (std::optional<bke::SocketValueVariant> value = implicitly_convert_socket_value(
841 from_type,
842 *lf_output_values[indices.outputs.main[*mapped_i]].get<bke::SocketValueVariant>(),
843 to_type))
844 {
845 new (item.value) bke::SocketValueVariant(std::move(*value));
846 }
847 else {
848 new (item.value) bke::SocketValueVariant(*to_type.geometry_nodes_default_value);
849 }
850 }
851 else {
852 /* This output item is not computed by the closure, so set it to the default value. */
854 }
855 }
856
857 for (GMutablePointer value : lf_input_values) {
858 if (value) {
859 value.destruct();
860 }
861 }
862 for (const int i : lf_output_values.index_range()) {
863 if (lf_set_outputs[i]) {
864 lf_output_values[i].destruct();
865 }
866 }
867}
868
870 const bNodeTree &btree,
871 const bke::bNodeTreeZone &zone,
872 ZoneBuildInfo &zone_info,
873 const ZoneBodyFunction &body_fn)
874{
875 return scope.construct<LazyFunctionForClosureZone>(btree, zone, zone_info, body_fn);
876}
877
879 const bNode &bnode)
880{
882 auto &fn = scope.construct<LazyFunctionForEvaluateClosureNode>(bnode);
883 info.lazy_function = &fn;
884 info.indices = fn.indices();
885 return info;
886}
887
888} // namespace blender::nodes
#define BLI_assert(a)
Definition BLI_assert.h:46
T * DEG_get_original(T *id)
eNodeSocketDatatype
volatile int lock
__forceinline float extract(const int4 &b)
Definition binning.cpp:27
static const CPPType & get()
std::optional< Value > lookup_try(const Key &key) const
Definition BLI_map.hh:531
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
ItemIterator items() const &
Definition BLI_map.hh:902
Span< T > as_span() const
Definition BLI_array.hh:243
IndexRange index_range() const
Definition BLI_array.hh:360
static const CPPType & get()
const ComputeContextHash & hash() const
constexpr IndexRange index_range() const
destruct_ptr< T > construct(Args &&...args)
void * allocate(const int64_t size, const int64_t alignment)
T & construct(Args &&...args)
LinearAllocator & allocator()
constexpr bool is_empty() const
Definition BLI_span.hh:260
void append(const T &value)
static SocketValueVariant From(T &&value)
Span< GraphInputSocket * > graph_inputs()
FunctionNode & add_function(const LazyFunction &fn)
void add_link(OutputSocket &from, InputSocket &to)
Span< GraphOutputSocket * > graph_outputs()
GraphOutputSocket & add_output(const CPPType &type, std::string name="")
GraphInputSocket & add_input(const CPPType &type, std::string name="")
const InputSocket & input(int index) const
const OutputSocket & output(int index) const
Vector< const lf::FunctionNode * > get_nodes_with_side_effects(const lf::Context &context) const override
std::optional< int > find_output_index(StringRef key) const
CustomIDVectorSet< Item, ItemKeyGetter > inputs
std::optional< int > find_input_index(StringRef key) const
CustomIDVectorSet< Item, ItemKeyGetter > outputs
const ClosureSignature & signature() const
const fn::lazy_function::LazyFunction & function() const
const bke::SocketValueVariant & default_input_value(const int index) const
const ClosureFunctionIndices & indices() const
LazyFunctionForClosureZone(const bNodeTree &btree, const bke::bNodeTreeZone &zone, ZoneBuildInfo &zone_info, const ZoneBodyFunction &body_fn)
void execute_impl(lf::Params &params, const lf::Context &context) const override
bool is_recursive_call(const GeoNodesUserData &user_data) const
void initialize_execution_graph(EvaluateClosureEvalStorage &eval_storage) const
void initialize_pass_through_graph(EvaluateClosureEvalStorage &eval_storage) const
void generate_closure_compatibility_warnings(const Closure &closure, const lf::Context &context) const
void * init_storage(LinearAllocator<> &allocator) const override
void execute_impl(lf::Params &params, const lf::Context &context) const override
linear_allocator::ChunkedList< WarningWithNode > node_warnings
static ushort indices[]
#define input
#define output
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
bNodeSocketType * node_socket_type_find_static(int type, int subtype=0)
Definition node.cc:2471
static Type to_type(const GPUType type)
void evaluate_closure_eagerly(const Closure &closure, ClosureEagerEvalParams &params)
void initialize_zone_wrapper(const bNodeTreeZone &zone, ZoneBuildInfo &zone_info, const ZoneBodyFunction &body_fn, const bool expose_all_reference_sets, Vector< lf::Input > &r_inputs, Vector< lf::Output > &r_outputs)
void construct_socket_default_value(const bke::bNodeSocketType &stype, void *r_value)
bool should_log_socket_values_for_context(const GeoNodesUserData &user_data, const ComputeContextHash hash)
LazyFunction & build_closure_zone_lazy_function(ResourceScope &scope, const bNodeTree &btree, const bke::bNodeTreeZone &zone, ZoneBuildInfo &zone_info, const ZoneBodyFunction &body_fn)
const LazyFunction * build_implicit_conversion_lazy_function(const bke::bNodeSocketType &from_type, const bke::bNodeSocketType &to_type, ResourceScope &scope)
std::optional< SocketValueVariant > implicitly_convert_socket_value(const bke::bNodeSocketType &from_type, const SocketValueVariant &from_value, const bke::bNodeSocketType &to_type)
const bNodeSocket * evaluate_closure_node_internally_linked_input(const bNodeSocket &output_socket)
ImplicitSharingPtr< Closure > ClosurePtr
void set_default_value_for_output_socket(lf::Params &params, const int lf_index, const bNodeSocket &bsocket)
EvaluateClosureFunction build_evaluate_closure_node_lazy_function(ResourceScope &scope, const bNode &bnode)
unsigned int session_uid
Definition DNA_ID.h:462
bNodeSocketTypeHandle * typeinfo
bNodeTreeRuntimeHandle * runtime
char name[64]
Defines a socket type.
Definition BKE_node.hh:158
eNodeSocketDatatype type
Definition BKE_node.hh:193
const SocketValueVariant * geometry_nodes_default_value
Definition BKE_node.hh:211
struct blender::nodes::ClosureFunctionIndices::@226263262065312210267224327362010175040155163171 outputs
struct blender::nodes::ClosureFunctionIndices::@337045024022036116043042002001100361150157266076 inputs
std::optional< ClosureIntermediateGraphSideEffectProvider > side_effect_provider
const GeoNodesSideEffectNodes * side_effect_nodes
MultiValueMap< ComputeContextHash, const lf::FunctionNode * > nodes_by_context
struct blender::nodes::ZoneFunctionIndices::@273312016272125124266321211240073263106341134120 inputs
i
Definition text_draw.cc:230