Shader Nodes: improve preservation of repeat zones in shader nodes inlining
These are some cleanups and fixes extracted from #146068. Preserved repeat zones are not used by any render engine yet. Pull Request: https://projects.blender.org/blender/blender/pulls/146890
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -256,7 +256,8 @@ class ShaderNodesInliner {
|
||||
});
|
||||
bNodeSocket *copied_socket = static_cast<bNodeSocket *>(
|
||||
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<const NodeGeometryRepeatOutput *>(
|
||||
@@ -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<int>(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<const NodeGeometryRepeatInput *>(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<const bNodeSocket *, bNodeSocket *> 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<InputSocketValue>(&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<InputSocketValue>(&value.value)) {
|
||||
/* Cases were the input has a primitive value are handled above. */
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user