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