diff --git a/source/blender/nodes/NOD_shader_nodes_inline.hh b/source/blender/nodes/NOD_shader_nodes_inline.hh index ff6a5319d22..f2fdff1deaa 100644 --- a/source/blender/nodes/NOD_shader_nodes_inline.hh +++ b/source/blender/nodes/NOD_shader_nodes_inline.hh @@ -13,6 +13,12 @@ namespace blender::nodes { struct InlineShaderNodeTreeParams { bool allow_preserving_repeat_zones = false; + /** + * In general, only a constant number of iterations per repeat zone is allowed, because otherwise + * it can't be inlined. However, if the render engine supports repeat zones natively, it could + * also support a dynamic number of iterations. + */ + bool dynamic_repeat_zone_iterations_is_error = true; struct ErrorMessage { /* In theory, more contextual information could be added here like the entire context path to diff --git a/source/blender/nodes/intern/shader_nodes_inline.cc b/source/blender/nodes/intern/shader_nodes_inline.cc index 551a3e2a225..82c80b30e11 100644 --- a/source/blender/nodes/intern/shader_nodes_inline.cc +++ b/source/blender/nodes/intern/shader_nodes_inline.cc @@ -256,7 +256,8 @@ class ShaderNodesInliner { }); bNodeSocket *copied_socket = static_cast( BLI_findlink(&copied_node->inputs, socket.socket->index())); - this->set_socket_value(*copied_node, *copied_socket, value_by_socket_.lookup(socket)); + this->set_socket_value( + *src_node, *copied_node, *copied_socket, value_by_socket_.lookup(socket)); } this->restore_zones_in_output_tree(); @@ -370,6 +371,13 @@ class ShaderNodesInliner { const bke::bNodeTreeZone *from_zone = zones->get_zone_by_socket(*link.fromsock); const ComputeContext *context = to_socket.context; for (const bke::bNodeTreeZone *zone = to_zone; zone != from_zone; zone = zone->parent_zone) { + const bNode &zone_output_node = *zone->output_node(); + if (zone_output_node.is_type("GeometryNodeRepeatOutput")) { + if (this->should_preserve_repeat_zone_node(zone_output_node)) { + /* Preserved repeat zones are embedded into their outer compute context. */ + continue; + } + } context = parent_zone_contexts_.lookup(context); } return context; @@ -395,16 +403,20 @@ class ShaderNodesInliner { return; } if (node->is_type("GeometryNodeRepeatOutput")) { - if (!this->should_preserve_repeat_zone_node(*node)) { - this->handle_output_socket__repeat_output(socket); + if (this->should_preserve_repeat_zone_node(*node)) { + this->handle_output_socket__preserved_repeat_output(socket); return; } + this->handle_output_socket__repeat_output(socket); + return; } if (node->is_type("GeometryNodeRepeatInput")) { - if (!this->should_preserve_repeat_zone_node(*node)) { - this->handle_output_socket__repeat_input(socket); + if (this->should_preserve_repeat_zone_node(*node)) { + this->handle_output_socket__preserved_repeat_input(socket); return; } + this->handle_output_socket__repeat_input(socket); + return; } if (node->is_type("NodeClosureOutput")) { this->handle_output_socket__closure_output(socket); @@ -517,8 +529,9 @@ class ShaderNodesInliner { if (!zone) { return false; } + const bNode *repeat_zone_input_node = zone->input_node(); const bNode *repeat_zone_output_node = zone->output_node(); - if (!repeat_zone_output_node) { + if (!repeat_zone_input_node || !repeat_zone_output_node) { return false; } const auto &storage = *static_cast( @@ -561,8 +574,7 @@ class ShaderNodesInliner { if (!iterations_value_opt) { /* Number of iterations is not a primitive value. */ this->store_socket_value_fallback(socket); - params_.r_error_messages.append( - {repeat_input_node.node, TIP_("Iterations input has to be a constant value")}); + this->add_dynamic_repeat_zone_iterations_error(*repeat_input_node); return; } const int iterations = std::get(iterations_value_opt->value); @@ -582,6 +594,51 @@ class ShaderNodesInliner { this->forward_value_or_schedule(socket, origin_socket); } + void handle_output_socket__preserved_repeat_output(const SocketInContext &socket) + { + const bNodeTree &tree = socket->owner_tree(); + const NodeInContext repeat_output_node = socket.owner_node(); + const bke::bNodeTreeZones &zones = *tree.zones(); + const bke::bNodeTreeZone &zone = *zones.get_zone_by_node(repeat_output_node->identifier); + const bNode &repeat_input_node = *zone.input_node(); + + const EnsureInputsResult ensured_inputs = this->ensure_node_inputs(socket.owner_node()); + if (ensured_inputs.has_missing_inputs) { + /* The node can only be evaluated if all inputs values are known. */ + return; + } + const NodeInContext node = socket.owner_node(); + bNode &copied_node = this->handle_output_socket__eval_copy_node(node); + PreservedZone &preserved_zone = copied_zone_by_zone_output_node_.lookup_or_add_default( + repeat_output_node); + preserved_zone.output_node = &copied_node; + /* Ensure that the repeat input node is created as well. */ + this->schedule_socket({node.context, &repeat_input_node.output_socket(0)}); + } + + void handle_output_socket__preserved_repeat_input(const SocketInContext &socket) + { + const EnsureInputsResult ensured_inputs = this->ensure_node_inputs(socket.owner_node()); + if (ensured_inputs.has_missing_inputs) { + /* The node can only be evaluated if all inputs values are known. */ + return; + } + const bNodeTree &tree = socket->owner_tree(); + const NodeInContext node = socket.owner_node(); + bNode &copied_node = this->handle_output_socket__eval_copy_node(node); + const auto &storage = *static_cast(node->storage); + const NodeInContext repeat_output_node{node.context, tree.node_by_id(storage.output_node_id)}; + PreservedZone &preserved_zone = copied_zone_by_zone_output_node_.lookup_or_add_default( + repeat_output_node); + preserved_zone.input_node = &copied_node; + } + + void add_dynamic_repeat_zone_iterations_error(const bNode &repeat_input_node) + { + params_.r_error_messages.append( + {&repeat_input_node, TIP_("Iterations input has to be a constant value")}); + } + void handle_output_socket__repeat_input(const SocketInContext &socket) { const bNode &repeat_input_node = socket->owner_node(); @@ -800,29 +857,13 @@ class ShaderNodesInliner { void handle_output_socket__eval(const SocketInContext &socket) { const NodeInContext node = socket.owner_node(); - bool has_missing_inputs = false; - bool all_inputs_primitive = true; - for (const bNodeSocket *input_socket : node->input_sockets()) { - if (!input_socket->is_available()) { - continue; - } - const SocketInContext input_socket_ctx = {socket.context, input_socket}; - const SocketValue *value = value_by_socket_.lookup_ptr(input_socket_ctx); - if (!value) { - this->schedule_socket(input_socket_ctx); - has_missing_inputs = true; - continue; - } - if (!value->to_primitive(*input_socket->typeinfo)) { - all_inputs_primitive = false; - } - } - if (has_missing_inputs) { + const EnsureInputsResult ensured_inputs = this->ensure_node_inputs(node); + if (ensured_inputs.has_missing_inputs) { /* The node can only be evaluated if all inputs values are known. */ return; } const bke::bNodeType &node_type = *node->typeinfo; - if (node_type.build_multi_function && all_inputs_primitive) { + if (node_type.build_multi_function && ensured_inputs.all_inputs_primitive) { /* Do constant folding. */ this->handle_output_socket__eval_multi_function(node); return; @@ -831,6 +872,34 @@ class ShaderNodesInliner { this->handle_output_socket__eval_copy_node(node); } + struct EnsureInputsResult { + bool has_missing_inputs = false; + bool all_inputs_primitive = false; + }; + + EnsureInputsResult ensure_node_inputs(const NodeInContext &node) + { + EnsureInputsResult result; + result.has_missing_inputs = false; + result.all_inputs_primitive = true; + for (const bNodeSocket *input_socket : node->input_sockets()) { + if (!input_socket->is_available()) { + continue; + } + const SocketInContext input_socket_ctx = {node.context, input_socket}; + const SocketValue *value = value_by_socket_.lookup_ptr(input_socket_ctx); + if (!value) { + this->schedule_socket(input_socket_ctx); + result.has_missing_inputs = true; + continue; + } + if (!value->to_primitive(*input_socket->typeinfo)) { + result.all_inputs_primitive = false; + } + } + return result; + } + void handle_output_socket__eval_multi_function(const NodeInContext &node) { NodeMultiFunctionBuilder builder{*node.node, node->owner_tree()}; @@ -879,7 +948,7 @@ class ShaderNodesInliner { } } - void handle_output_socket__eval_copy_node(const NodeInContext &node) + bNode &handle_output_socket__eval_copy_node(const NodeInContext &node) { Map socket_map; /* We generate our own identifier and name here to get unique values without having to scan all @@ -906,7 +975,7 @@ class ShaderNodesInliner { bNodeSocket &dst_input_socket = *socket_map.lookup(src_input_socket); const SocketInContext input_socket_ctx = {node.context, src_input_socket}; const SocketValue &value = value_by_socket_.lookup(input_socket_ctx); - this->set_socket_value(copied_node, dst_input_socket, value); + this->set_socket_value(*node, copied_node, dst_input_socket, value); } for (const bNodeSocket *src_output_socket : node->output_sockets()) { if (!src_output_socket->is_available()) { @@ -917,32 +986,7 @@ class ShaderNodesInliner { this->store_socket_value(output_socket_ctx, {LinkedSocketValue{&copied_node, &dst_output_socket}}); } - this->remember_copied_zone_node_if_necessary(node, copied_node); - } - - void remember_copied_zone_node_if_necessary(const NodeInContext &node, bNode &copied_node) - { - const bNodeTree &tree = node->owner_tree(); - const bke::bNodeTreeZones *zones = tree.zones(); - if (!zones) { - return; - } - const bke::bNodeTreeZone *zone = zones->get_zone_by_node(node->identifier); - if (!zone) { - return; - } - if (!ELEM(node->identifier, zone->input_node_id, zone->output_node_id)) { - return; - } - const NodeInContext zone_output_node = {node.context, zone->output_node()}; - PreservedZone &copied_zone = copied_zone_by_zone_output_node_.lookup_or_add_default( - zone_output_node); - if (node == zone_output_node) { - copied_zone.output_node = &copied_node; - } - else { - copied_zone.input_node = &copied_node; - } + return copied_node; } /** Converts the given socket value if necessary. */ @@ -989,7 +1033,10 @@ class ShaderNodesInliner { return SocketValue{FallbackValue{}}; } - void set_socket_value(bNode &dst_node, bNodeSocket &dst_socket, const SocketValue &value) + void set_socket_value(const bNode &original_node, + bNode &dst_node, + bNodeSocket &dst_socket, + const SocketValue &value) { if (dst_socket.flag & SOCK_HIDE_VALUE) { if (const auto *input_socket_value = std::get_if(&value.value)) { @@ -1019,6 +1066,15 @@ class ShaderNodesInliner { } return; } + if (params_.dynamic_repeat_zone_iterations_is_error) { + const bool is_iterations_input = dst_node.inputs.first == &dst_socket && + dst_node.is_type("GeometryNodeRepeatInput"); + if (is_iterations_input) { + this->add_dynamic_repeat_zone_iterations_error(original_node); + this->set_primitive_value_on_socket(dst_socket, PrimitiveSocketValue{0}); + return; + } + } if (std::get_if(&value.value)) { /* Cases were the input has a primitive value are handled above. */ return;