diff --git a/source/blender/blenkernel/BKE_node.hh b/source/blender/blenkernel/BKE_node.hh index 2986a516f12..64105282d78 100644 --- a/source/blender/blenkernel/BKE_node.hh +++ b/source/blender/blenkernel/BKE_node.hh @@ -1048,7 +1048,7 @@ void node_chain_iterator(const bNodeTree *ntree, * \note Recursive */ void node_chain_iterator_backwards(const bNodeTree *ntree, - const bNode *node_start, + bNode *node_start, bool (*callback)(bNode *, bNode *, void *), void *userdata, int recursion_lvl); diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index e65343a1f4f..eb203d23e77 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -38,6 +38,7 @@ #include "BLI_math_vector.hh" #include "BLI_rand.hh" #include "BLI_set.hh" +#include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_string_utils.hh" @@ -3369,35 +3370,56 @@ void node_chain_iterator(const bNodeTree *ntree, } static void iter_backwards_ex(const bNodeTree *ntree, - const bNode *node_start, + bNode *node_start, bool (*callback)(bNode *, bNode *, void *), void *userdata, const char recursion_mask) { - LISTBASE_FOREACH (bNodeSocket *, sock, &node_start->inputs) { - bNodeLink *link = sock->link; - if (link == nullptr) { - continue; - } - if ((link->flag & NODE_LINK_VALID) == 0) { - /* Skip links marked as cyclic. */ - continue; - } - if (link->fromnode->runtime->iter_flag & recursion_mask) { - continue; - } + blender::Stack stack; + blender::Stack zone_stack; + stack.push(node_start); - link->fromnode->runtime->iter_flag |= recursion_mask; + while (!stack.is_empty() || !zone_stack.is_empty()) { + bNode *node = !stack.is_empty() ? stack.pop() : zone_stack.pop(); - if (!callback(link->fromnode, link->tonode, userdata)) { - return; + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + bNodeLink *link = sock->link; + if (link == nullptr) { + continue; + } + if ((link->flag & NODE_LINK_VALID) == 0) { + /* Skip links marked as cyclic. */ + continue; + } + if (link->fromnode->runtime->iter_flag & recursion_mask) { + continue; + } + + link->fromnode->runtime->iter_flag |= recursion_mask; + + if (!callback(link->fromnode, link->tonode, userdata)) { + break; + } + stack.push(link->fromnode); + } + /* Zone input nodes are implicitly linked to their corresponding zone output nodes, + * even if there is no bNodeLink between them. */ + if (const bNodeZoneType *zone_type = zone_type_by_node_type(node->type_legacy)) { + if (zone_type->output_type == node->type_legacy) { + if (bNode *zone_input_node = const_cast( + zone_type->get_corresponding_input(*ntree, *node))) + { + if (callback(zone_input_node, node, userdata)) { + zone_stack.push(zone_input_node); + } + } + } } - iter_backwards_ex(ntree, link->fromnode, callback, userdata, recursion_mask); } } void node_chain_iterator_backwards(const bNodeTree *ntree, - const bNode *node_start, + bNode *node_start, bool (*callback)(bNode *, bNode *, void *), void *userdata, const int recursion_lvl) diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 3a271fb6669..6c5233946f5 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -650,6 +650,7 @@ set(GLSL_SRC shaders/material/gpu_shader_material_principled.glsl shaders/material/gpu_shader_material_ray_portal.glsl shaders/material/gpu_shader_material_refraction.glsl + shaders/material/gpu_shader_material_repeat_zone.glsl shaders/material/gpu_shader_material_rgb_to_bw.glsl shaders/material/gpu_shader_material_radial_tiling.glsl shaders/material/gpu_shader_material_radial_tiling_shared.glsl diff --git a/source/blender/gpu/GPU_material.hh b/source/blender/gpu/GPU_material.hh index 683a48b5018..74fb796d96f 100644 --- a/source/blender/gpu/GPU_material.hh +++ b/source/blender/gpu/GPU_material.hh @@ -407,6 +407,16 @@ bool GPU_stack_link(GPUMaterial *mat, GPUNodeStack *out, ...); +bool GPU_stack_link_zone(GPUMaterial *material, + const bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + int zone_index, + bool is_zone_end, + int in_argument_count, + int out_argument_count); + void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link); void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link); void GPU_material_output_displacement(GPUMaterial *material, GPUNodeLink *link); diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index b4466841dde..6bc781691ca 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -39,24 +39,16 @@ using namespace blender::gpu::shader; /** \name Type > string conversion * \{ */ -#if 0 -# define SRC_NAME(io, link, list, type) \ - link->node->name << "_" << io << BLI_findindex(&link->node->list, (const void *)link) << "_" \ - << type -#else -# define SRC_NAME(io, list, link, type) type -#endif - static std::ostream &operator<<(std::ostream &stream, const GPUInput *input) { switch (input->source) { case GPU_SOURCE_FUNCTION_CALL: case GPU_SOURCE_OUTPUT: - return stream << SRC_NAME("in", input, inputs, "tmp") << input->id; + return stream << (input->is_zone_io ? "zone" : "tmp") << input->id; case GPU_SOURCE_CONSTANT: - return stream << SRC_NAME("in", input, inputs, "cons") << input->id; + return stream << (input->is_zone_io ? "zone" : "cons") << input->id; case GPU_SOURCE_UNIFORM: - return stream << "node_tree.u" << input->id; + return stream << "node_tree.u" << input->id << (input->is_duplicate ? "b" : ""); case GPU_SOURCE_ATTR: return stream << "var_attrs.v" << input->attr->id; case GPU_SOURCE_UNIFORM_ATTR: @@ -77,7 +69,7 @@ static std::ostream &operator<<(std::ostream &stream, const GPUInput *input) static std::ostream &operator<<(std::ostream &stream, const GPUOutput *output) { - return stream << SRC_NAME("out", output, outputs, "tmp") << output->id; + return stream << (output->is_zone_io ? "zone" : "tmp") << output->id; } /* Print data constructor (i.e: vec2(1.0f, 1.0f)). */ @@ -263,7 +255,7 @@ void GPUCodegen::generate_resources() ss << input->type << " crypto_hash;\n"; } else { - ss << input->type << " u" << input->id << ";\n"; + ss << input->type << " u" << input->id << (input->is_duplicate ? "b" : "") << ";\n"; } } ss << "};\n\n"; @@ -296,24 +288,77 @@ void GPUCodegen::node_serialize(Set &used_libraries, { gpu_material_library_use_function(used_libraries, node->name); + auto source_reference = [&](GPUInput *input) { + BLI_assert(ELEM(input->source, GPU_SOURCE_OUTPUT, GPU_SOURCE_ATTR)); + /* These inputs can have non matching types. Do conversion. */ + GPUType to = input->type; + GPUType from = (input->source == GPU_SOURCE_ATTR) ? input->attr->gputype : + input->link->output->type; + if (from != to) { + /* Use defines declared inside codegen_lib (e.g. vec4_from_float). */ + eval_ss << to << "_from_" << from << "("; + } + + if (input->source == GPU_SOURCE_ATTR) { + eval_ss << input; + } + else { + eval_ss << input->link->output; + } + + if (from != to) { + /* Special case that needs luminance coefficients as argument. */ + if (from == GPU_VEC4 && to == GPU_FLOAT) { + float coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(coefficients); + eval_ss << ", " << blender::Span(coefficients, 3); + } + eval_ss << ")"; + } + }; + /* Declare constants. */ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + auto type = [&]() { + /* Don't declare zone io variables twice. */ + std::stringstream ss; + if (!input->is_duplicate) { + ss << input->type; + } + return ss.str(); + }; switch (input->source) { case GPU_SOURCE_FUNCTION_CALL: - eval_ss << input->type << " " << input << "; " << input->function_call << input << ");\n"; + eval_ss << type() << " " << input << "; " << input->function_call << input << ");\n"; break; case GPU_SOURCE_STRUCT: eval_ss << input->type << " " << input << " = CLOSURE_DEFAULT;\n"; break; case GPU_SOURCE_CONSTANT: - eval_ss << input->type << " " << input << " = " << (GPUConstant *)input << ";\n"; + if (!input->is_duplicate) { + eval_ss << type() << " " << input << " = " << (GPUConstant *)input << ";\n"; + } + break; + case GPU_SOURCE_OUTPUT: + case GPU_SOURCE_ATTR: + if (input->is_zone_io) { + eval_ss << type() << " " << input << " = "; + source_reference(input); + eval_ss << ";\n"; + } break; default: + if (input->is_zone_io && (!input->is_duplicate || !input->link)) { + eval_ss << type() << " zone" << input->id << " = " << input << ";\n"; + } break; } } /* Declare temporary variables for node output storage. */ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + if (output->is_zone_io) { + break; + } eval_ss << output->type << " " << output << ";\n"; } @@ -321,47 +366,31 @@ void GPUCodegen::node_serialize(Set &used_libraries, eval_ss << node->name << "("; /* Input arguments. */ LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + if (input->is_zone_io) { + break; + } switch (input->source) { case GPU_SOURCE_OUTPUT: case GPU_SOURCE_ATTR: { - /* These inputs can have non matching types. Do conversion. */ - GPUType to = input->type; - GPUType from = (input->source == GPU_SOURCE_ATTR) ? input->attr->gputype : - input->link->output->type; - if (from != to) { - /* Use defines declared inside codegen_lib (i.e: vec4_from_float). */ - eval_ss << to << "_from_" << from << "("; - } - - if (input->source == GPU_SOURCE_ATTR) { - eval_ss << input; - } - else { - eval_ss << input->link->output; - } - - if (from != to) { - /* Special case that needs luminance coefficients as argument. */ - if (from == GPU_VEC4 && to == GPU_FLOAT) { - float coefficients[3]; - IMB_colormanagement_get_luminance_coefficients(coefficients); - eval_ss << ", " << Span(coefficients, 3); - } - - eval_ss << ")"; - } + source_reference(input); break; } default: eval_ss << input; break; } - eval_ss << ", "; + GPUOutput *output = static_cast(node->outputs.first); + if ((input->next && !input->next->is_zone_io) || (output && !output->is_zone_io)) { + eval_ss << ", "; + } } /* Output arguments. */ LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { + if (output->is_zone_io) { + break; + } eval_ss << output; - if (output->next) { + if (output->next && !output->next->is_zone_io) { eval_ss << ", "; } } @@ -473,6 +502,9 @@ void GPUCodegen::generate_uniform_buffer() /* Sets id for unique names for all inputs, resources and temp variables. */ void GPUCodegen::set_unique_ids() { + blender::Map zone_starts; + blender::Map zone_ends; + int id = 1; LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { @@ -481,6 +513,39 @@ void GPUCodegen::set_unique_ids() LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { output->id = id++; } + if (node->zone_index != -1) { + auto &map = node->is_zone_end ? zone_ends : zone_starts; + map.add(node->zone_index, node); + } + } + + auto find_zone_io = [](auto first) { + while (first && !first->is_zone_io && first->next) { + first = first->next; + } + return first; + }; + + /* Assign the same id to inputs and outputs of start and end zones. */ + for (GPUNode *end : zone_ends.values()) { + + GPUInput *end_input = find_zone_io((GPUInput *)end->inputs.first); + GPUOutput *end_output = find_zone_io((GPUOutput *)end->outputs.first); + + GPUNode *start = zone_starts.lookup(end->zone_index); + + GPUInput *start_input = find_zone_io((GPUInput *)start->inputs.first); + GPUOutput *start_output = find_zone_io((GPUOutput *)start->outputs.first); + + for (; start_input; start_input = start_input->next, + start_output = start_output->next, + end_input = end_input->next, + end_output = end_output->next) + { + start_output->id = start_input->id; + end_input->id = start_input->id; + end_output->id = start_input->id; + } } } @@ -506,14 +571,14 @@ void GPUCodegen::generate_graphs() node->tag &= ~GPU_NODE_TAG_FUNCTION; } /* Tag only the nodes needed for the current function */ - gpu_nodes_tag(func_link->outlink, GPU_NODE_TAG_FUNCTION); + gpu_nodes_tag(&graph, func_link->outlink, GPU_NODE_TAG_FUNCTION); GPUGraphOutput graph = graph_serialize(GPU_NODE_TAG_FUNCTION, func_link->outlink); eval_ss << "float " << func_link->name << "() {\n" << graph.serialized << "}\n\n"; output.material_functions.append({eval_ss.str(), graph.dependencies}); } /* Leave the function tags as they were before serialization */ LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, funclink, &graph.material_functions) { - gpu_nodes_tag(funclink->outlink, GPU_NODE_TAG_FUNCTION); + gpu_nodes_tag(&graph, funclink->outlink, GPU_NODE_TAG_FUNCTION); } } diff --git a/source/blender/gpu/intern/gpu_material.cc b/source/blender/gpu/intern/gpu_material.cc index e862f3d01b4..8fd8fceab62 100644 --- a/source/blender/gpu/intern/gpu_material.cc +++ b/source/blender/gpu/intern/gpu_material.cc @@ -163,7 +163,7 @@ GPUMaterialFromNodeTreeResult GPU_material_from_nodetree( bNodeTree *localtree = blender::bke::node_tree_add_tree( nullptr, (blender::StringRef(ntree->id.name) + " Inlined").c_str(), ntree->idname); blender::nodes::InlineShaderNodeTreeParams inline_params; - inline_params.allow_preserving_repeat_zones = false; + inline_params.allow_preserving_repeat_zones = true; blender::nodes::inline_shader_node_tree(*ntree, *localtree, inline_params); for (blender::nodes::InlineShaderNodeTreeParams::ErrorMessage &error : diff --git a/source/blender/gpu/intern/gpu_node_graph.cc b/source/blender/gpu/intern/gpu_node_graph.cc index c92c5202163..7c08b1791bd 100644 --- a/source/blender/gpu/intern/gpu_node_graph.cc +++ b/source/blender/gpu/intern/gpu_node_graph.cc @@ -17,6 +17,7 @@ #include "BLI_ghash.h" #include "BLI_listbase.h" +#include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -59,6 +60,8 @@ static GPUNode *gpu_node_create(const char *name) GPUNode *node = MEM_callocN("GPUNode"); node->name = name; + node->zone_index = -1; + node->is_zone_end = false; return node; } @@ -214,7 +217,7 @@ static GPUNodeLink *gpu_uniformbuffer_link(GPUMaterial *mat, return nullptr; } - if (!ELEM(socket->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA)) { + if (!ELEM(socket->type, SOCK_INT, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA)) { return nullptr; } @@ -860,6 +863,59 @@ bool GPU_stack_link(GPUMaterial *material, return valid; } +bool GPU_stack_link_zone(GPUMaterial *material, + const bNode *bnode, + const char *name, + GPUNodeStack *in, + GPUNodeStack *out, + int zone_index, + bool is_zone_end, + int in_argument_count, + int out_argument_count) +{ + GPUNodeGraph *graph = gpu_material_node_graph(material); + GPUNode *node; + int i, totin, totout; + + node = gpu_node_create(name); + node->zone_index = zone_index; + node->is_zone_end = is_zone_end; + + totin = 0; + totout = 0; + + if (in) { + for (i = 0; !in[i].end; i++) { + if (in[i].type != GPU_NONE) { + gpu_node_input_socket(material, bnode, node, &in[i], i); + totin++; + } + } + } + + if (out) { + for (i = 0; !out[i].end; i++) { + if (out[i].type != GPU_NONE) { + gpu_node_output(node, out[i].type, &out[i].link); + totout++; + } + } + } + + LISTBASE_FOREACH_INDEX (GPUInput *, input, &node->inputs, i) { + input->is_zone_io = i >= in_argument_count; + input->is_duplicate = input->is_zone_io && is_zone_end; + } + LISTBASE_FOREACH_INDEX (GPUOutput *, output, &node->outputs, i) { + output->is_zone_io = i >= out_argument_count; + output->is_duplicate = output->is_zone_io; + } + + BLI_addtail(&graph->nodes, node); + + return true; +} + /* Node Graph */ static void gpu_inputs_free(ListBase *inputs) @@ -934,23 +990,43 @@ void gpu_node_graph_free(GPUNodeGraph *graph) /* Prune Unused Nodes */ -void gpu_nodes_tag(GPUNodeLink *link, GPUNodeTag tag) +void gpu_nodes_tag(GPUNodeGraph *graph, GPUNodeLink *link_start, GPUNodeTag tag) { - GPUNode *node; - - if (!link || !link->output) { + if (!link_start || !link_start->output) { return; } - node = link->output->node; - if (node->tag & tag) { - return; - } + blender::Stack stack; + blender::Stack zone_stack; + stack.push(link_start->output->node); - node->tag |= tag; - LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { - if (input->link) { - gpu_nodes_tag(input->link, tag); + while (!stack.is_empty() || !zone_stack.is_empty()) { + GPUNode *node = !stack.is_empty() ? stack.pop() : zone_stack.pop(); + + if (node->tag & tag) { + continue; + } + + node->tag |= tag; + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + if (input->link && input->link->output) { + stack.push(input->link->output->node); + } + } + + /* Zone input nodes are implicitly linked to their corresponding zone output nodes, + * even if there is no GPUNodeLink between them. */ + if (node->is_zone_end) { + LISTBASE_FOREACH (GPUNode *, node2, &graph->nodes) { + if (node2->zone_index == node->zone_index && !node2->is_zone_end && !(node2->tag & tag)) { + node2->tag |= tag; + LISTBASE_FOREACH (GPUInput *, input, &node2->inputs) { + if (input->link && input->link->output) { + zone_stack.push(input->link->output->node); + } + } + } + } } } } @@ -961,19 +1037,19 @@ void gpu_node_graph_prune_unused(GPUNodeGraph *graph) node->tag = GPU_NODE_TAG_NONE; } - gpu_nodes_tag(graph->outlink_surface, GPU_NODE_TAG_SURFACE); - gpu_nodes_tag(graph->outlink_volume, GPU_NODE_TAG_VOLUME); - gpu_nodes_tag(graph->outlink_displacement, GPU_NODE_TAG_DISPLACEMENT); - gpu_nodes_tag(graph->outlink_thickness, GPU_NODE_TAG_THICKNESS); + gpu_nodes_tag(graph, graph->outlink_surface, GPU_NODE_TAG_SURFACE); + gpu_nodes_tag(graph, graph->outlink_volume, GPU_NODE_TAG_VOLUME); + gpu_nodes_tag(graph, graph->outlink_displacement, GPU_NODE_TAG_DISPLACEMENT); + gpu_nodes_tag(graph, graph->outlink_thickness, GPU_NODE_TAG_THICKNESS); LISTBASE_FOREACH (GPUNodeGraphOutputLink *, aovlink, &graph->outlink_aovs) { - gpu_nodes_tag(aovlink->outlink, GPU_NODE_TAG_AOV); + gpu_nodes_tag(graph, aovlink->outlink, GPU_NODE_TAG_AOV); } LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, funclink, &graph->material_functions) { - gpu_nodes_tag(funclink->outlink, GPU_NODE_TAG_FUNCTION); + gpu_nodes_tag(graph, funclink->outlink, GPU_NODE_TAG_FUNCTION); } LISTBASE_FOREACH (GPUNodeGraphOutputLink *, compositor_link, &graph->outlink_compositor) { - gpu_nodes_tag(compositor_link->outlink, GPU_NODE_TAG_COMPOSITOR); + gpu_nodes_tag(graph, compositor_link->outlink, GPU_NODE_TAG_COMPOSITOR); } for (GPUNode *node = static_cast(graph->nodes.first), *next = nullptr; node; diff --git a/source/blender/gpu/intern/gpu_node_graph.hh b/source/blender/gpu/intern/gpu_node_graph.hh index bcaf67bbfc4..18ed3635299 100644 --- a/source/blender/gpu/intern/gpu_node_graph.hh +++ b/source/blender/gpu/intern/gpu_node_graph.hh @@ -73,6 +73,10 @@ struct GPUNode { ListBase inputs; ListBase outputs; + + /* Zones. */ + int zone_index; + bool is_zone_end; }; struct GPUNodeLink { @@ -111,6 +115,11 @@ struct GPUOutput { GPUType type; /* data type = length of vector/matrix */ GPUNodeLink *link; /* output link */ int id; /* unique id as created by code generator */ + + /* True for Zone Items. */ + bool is_zone_io; + /* This variable is shared with other socket/s and doesn't need to be declared. */ + bool is_duplicate; }; struct GPUInput { @@ -138,6 +147,11 @@ struct GPUInput { /* GPU_SOURCE_FUNCTION_CALL */ char function_call[64]; }; + + /* True for Zone Items. */ + bool is_zone_io; + /* This variable is shared with other socket/s and doesn't need to be declared. */ + bool is_duplicate; }; struct GPUNodeGraphOutputLink { @@ -181,7 +195,7 @@ struct GPUNodeGraph { /* Node Graph */ -void gpu_nodes_tag(GPUNodeLink *link, GPUNodeTag tag); +void gpu_nodes_tag(GPUNodeGraph *graph, GPUNodeLink *link_start, GPUNodeTag tag); void gpu_node_graph_prune_unused(GPUNodeGraph *graph); void gpu_node_graph_finalize_uniform_attrs(GPUNodeGraph *graph); diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 979f18c69da..169fc4426f8 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -553,6 +553,7 @@ void gpu_material_library_use_function(blender::Set &use const char *name) { GPUFunction *function = g_functions->lookup_default(name, nullptr); + BLI_assert_msg(function != nullptr, "Requested function not in the function library"); GPUSource *source = reinterpret_cast(function->source); used_libraries.add(source->filename.c_str()); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_repeat_zone.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_repeat_zone.glsl new file mode 100644 index 00000000000..6c7019dc883 --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_repeat_zone.glsl @@ -0,0 +1,22 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef GPU_SHADER + +# define REPEAT_BEGIN(count, var) \ + for (int var##_i = 0; var##_i < count; var##_i++) { \ + var = float(var##_i); + +# define REPEAT_END() } + +#else + +/** + * Dummy functions for gpu_shader_dependency. + * Functions need parameters to be reflected, but we don't really rely on the reflection data. + */ +void REPEAT_BEGIN(float dummy) {}; +void REPEAT_END(float dummy) {}; + +#endif diff --git a/source/blender/nodes/NOD_shader_nodes_inline.hh b/source/blender/nodes/NOD_shader_nodes_inline.hh index f2fdff1deaa..19e6031e483 100644 --- a/source/blender/nodes/NOD_shader_nodes_inline.hh +++ b/source/blender/nodes/NOD_shader_nodes_inline.hh @@ -12,13 +12,13 @@ struct bNode; 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. + * Disable loop unrolling and keep Repeat Zone nodes in the tree. + * (For engines with native support for Repeat Zones) + * + * Some Repeat Zones may still be unrolled (eg. if they have Closure or Bundle Zone Items). */ - bool dynamic_repeat_zone_iterations_is_error = true; + bool allow_preserving_repeat_zones = false; struct ErrorMessage { /* In theory, more contextual information could be added here like the entire context path to diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat.cc index 0e931d1ef5b..f371f02b619 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat.cc @@ -132,6 +132,16 @@ static bool node_insert_link(bke::NodeInsertLinkParams ¶ms) params.ntree, params.node, *output_node, params.link); } +static int node_shader_fn(GPUMaterial *mat, + bNode *node, + bNodeExecData * /*execdata*/, + GPUNodeStack *in, + GPUNodeStack *out) +{ + int zone_id = node_storage(*node).output_node_id; + return GPU_stack_link_zone(mat, node, "REPEAT_BEGIN", in, out, zone_id, false, 1, 1); +} + static void node_register() { static blender::bke::bNodeType ntype; @@ -146,6 +156,7 @@ static void node_register() ntype.insert_link = node_insert_link; ntype.no_muting = true; ntype.draw_buttons_ex = node_layout_ex; + ntype.gpu_fn = node_shader_fn; blender::bke::node_type_storage( ntype, "NodeGeometryRepeatInput", node_free_standard_storage, node_copy_standard_storage); blender::bke::node_register_type(ntype); @@ -281,6 +292,16 @@ static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader & socket_items::blend_read_data(&reader, node); } +static int node_shader_fn(GPUMaterial *mat, + bNode *node, + bNodeExecData * /*execdata*/, + GPUNodeStack *in, + GPUNodeStack *out) +{ + int zone_id = node->identifier; + return GPU_stack_link_zone(mat, node, "REPEAT_END", in, out, zone_id, true, 0, 0); +} + static void node_register() { static blender::bke::bNodeType ntype; @@ -298,6 +319,7 @@ static void node_register() ntype.register_operators = node_operators; ntype.blend_write_storage_content = node_blend_write; ntype.blend_data_read_storage_content = node_blend_read; + ntype.gpu_fn = node_shader_fn; blender::bke::node_type_storage( ntype, "NodeGeometryRepeatOutput", node_free_storage, node_copy_storage); blender::bke::node_register_type(ntype); diff --git a/source/blender/nodes/intern/node_exec.cc b/source/blender/nodes/intern/node_exec.cc index bacf6de217b..bfd630b0fde 100644 --- a/source/blender/nodes/intern/node_exec.cc +++ b/source/blender/nodes/intern/node_exec.cc @@ -122,6 +122,12 @@ static bNodeStack *setup_stack(bNodeStack *stack, bNodeTree *ntree, bNode *node, ns->sockettype = sock->type; switch (sock->type) { + case SOCK_INT: + ns->vec[0] = node_socket_get_int(ntree, node, sock); + break; + case SOCK_BOOLEAN: + ns->vec[0] = node_socket_get_bool(ntree, node, sock); + break; case SOCK_FLOAT: ns->vec[0] = node_socket_get_float(ntree, node, sock); break; diff --git a/source/blender/nodes/intern/node_util.cc b/source/blender/nodes/intern/node_util.cc index 81d2d08d692..903217e6468 100644 --- a/source/blender/nodes/intern/node_util.cc +++ b/source/blender/nodes/intern/node_util.cc @@ -274,6 +274,30 @@ bool node_insert_link_default(blender::bke::NodeInsertLinkParams & /*params*/) /** \name Default value RNA access * \{ */ +int node_socket_get_int(bNodeTree *ntree, bNode * /*node*/, bNodeSocket *sock) +{ + PointerRNA ptr = RNA_pointer_create_discrete((ID *)ntree, &RNA_NodeSocket, sock); + return RNA_int_get(&ptr, "default_value"); +} + +void node_socket_set_int(bNodeTree *ntree, bNode * /*node*/, bNodeSocket *sock, int value) +{ + PointerRNA ptr = RNA_pointer_create_discrete((ID *)ntree, &RNA_NodeSocket, sock); + RNA_int_set(&ptr, "default_value", value); +} + +bool node_socket_get_bool(bNodeTree *ntree, bNode * /*node*/, bNodeSocket *sock) +{ + PointerRNA ptr = RNA_pointer_create_discrete((ID *)ntree, &RNA_NodeSocket, sock); + return RNA_boolean_get(&ptr, "default_value"); +} + +void node_socket_set_bool(bNodeTree *ntree, bNode * /*node*/, bNodeSocket *sock, bool value) +{ + PointerRNA ptr = RNA_pointer_create_discrete((ID *)ntree, &RNA_NodeSocket, sock); + RNA_boolean_set(&ptr, "default_value", value); +} + float node_socket_get_float(bNodeTree *ntree, bNode * /*node*/, bNodeSocket *sock) { PointerRNA ptr = RNA_pointer_create_discrete((ID *)ntree, &RNA_NodeSocket, sock); diff --git a/source/blender/nodes/intern/node_util.hh b/source/blender/nodes/intern/node_util.hh index 1e71e41acb3..4be340fbb72 100644 --- a/source/blender/nodes/intern/node_util.hh +++ b/source/blender/nodes/intern/node_util.hh @@ -54,6 +54,10 @@ void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode */ bool node_insert_link_default(blender::bke::NodeInsertLinkParams ¶ms); +int node_socket_get_int(bNodeTree *ntree, bNode *node, bNodeSocket *sock); +void node_socket_set_int(bNodeTree *ntree, bNode *node, bNodeSocket *sock, int value); +bool node_socket_get_bool(bNodeTree *ntree, bNode *node, bNodeSocket *sock); +void node_socket_set_bool(bNodeTree *ntree, bNode *node, bNodeSocket *sock, bool value); float node_socket_get_float(bNodeTree *ntree, bNode *node, bNodeSocket *sock); void node_socket_set_float(bNodeTree *ntree, bNode *node, bNodeSocket *sock, float value); void node_socket_get_color(bNodeTree *ntree, bNode *node, bNodeSocket *sock, float *value); diff --git a/source/blender/nodes/intern/shader_nodes_inline.cc b/source/blender/nodes/intern/shader_nodes_inline.cc index 026b590b4fe..01c2e0fbefb 100644 --- a/source/blender/nodes/intern/shader_nodes_inline.cc +++ b/source/blender/nodes/intern/shader_nodes_inline.cc @@ -1159,7 +1159,7 @@ class ShaderNodesInliner { } return; } - if (params_.dynamic_repeat_zone_iterations_is_error) { + if (!params_.allow_preserving_repeat_zones) { const bool is_iterations_input = dst_node.inputs.first == &dst_socket && dst_node.is_type("GeometryNodeRepeatInput"); if (is_iterations_input) { diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index fd57a8de14d..703f4785168 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -7,7 +7,6 @@ */ #include -#include #include "DNA_light_types.h" #include "DNA_linestyle_types.h" @@ -20,8 +19,10 @@ #include "BLI_array.hh" #include "BLI_linklist.h" #include "BLI_listbase.h" +#include "BLI_map.hh" #include "BLI_math_vector.h" #include "BLI_set.hh" +#include "BLI_stack.hh" #include "BLI_threads.h" #include "BLI_utildefines.h" #include "BLI_vector.hh" @@ -302,6 +303,7 @@ static void ntree_shader_unlink_script_nodes(bNodeTree *ntree) } } } + struct branchIterData { bool (*node_filter)(const bNode *node); int node_count; @@ -792,32 +794,35 @@ static void ntree_shader_shader_to_rgba_branches(bNodeTree *ntree) } } -static void iter_shader_to_rgba_depth_count(bNode *start_node, int16_t &max_depth) +static void iter_shader_to_rgba_depth_count(bNodeTree *ntree, + bNode *node_start, + int16_t &max_depth) { struct StackNode { bNode *node; int16_t depth; }; - std::stack stack; - stack.push({start_node, 0}); + blender::Stack stack; + blender::Stack zone_stack; + stack.push({node_start, 0}); - while (!stack.empty()) { - StackNode s_node = stack.top(); - stack.pop(); + while (!stack.is_empty() || !zone_stack.is_empty()) { + StackNode s_node = !stack.is_empty() ? stack.pop() : zone_stack.pop(); bNode *node = s_node.node; int16_t depth_level = s_node.depth; + if (node->runtime->tmp_flag >= depth_level) { + /* We already iterated this branch at this or a greater depth. */ + continue; + } + if (node->type_legacy == SH_NODE_SHADERTORGB) { depth_level++; max_depth = std::max(max_depth, depth_level); } - if (node->runtime->tmp_flag >= depth_level) { - /* We already iterated this branch at this or a greater depth. */ - continue; - } node->runtime->tmp_flag = std::max(node->runtime->tmp_flag, depth_level); LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { @@ -831,6 +836,18 @@ static void iter_shader_to_rgba_depth_count(bNode *start_node, int16_t &max_dept } stack.push({link->fromnode, depth_level}); } + + /* Zone input nodes are linked to their corresponding zone output nodes, even if there is no + * bNodeLink between them. */ + if (const blender::bke::bNodeZoneType *zone_type = blender::bke::zone_type_by_node_type( + node->type_legacy)) + { + if (zone_type->output_type == node->type_legacy) { + if (bNode *zone_input_node = zone_type->get_corresponding_input(*ntree, *node)) { + zone_stack.push({zone_input_node, depth_level}); + } + } + } } } @@ -994,11 +1011,11 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat) node->runtime->tmp_flag = -1; } if (output != nullptr) { - iter_shader_to_rgba_depth_count(output, max_depth); + iter_shader_to_rgba_depth_count(localtree, output, max_depth); } LISTBASE_FOREACH (bNode *, node, &localtree->nodes) { if (node->type_legacy == SH_NODE_OUTPUT_AOV) { - iter_shader_to_rgba_depth_count(node, max_depth); + iter_shader_to_rgba_depth_count(localtree, node, max_depth); } } for (int depth = max_depth; depth >= 0; depth--) { diff --git a/tests/files/render/node_inlining/eevee_renders/repeat_zones.png b/tests/files/render/node_inlining/eevee_renders/repeat_zones.png index f3f1faf4281..9512c375e5a 100644 --- a/tests/files/render/node_inlining/eevee_renders/repeat_zones.png +++ b/tests/files/render/node_inlining/eevee_renders/repeat_zones.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:253bd42157ae0d937977d29789bb98615382ed8843da915e855233cb6554b171 -size 2033 +oid sha256:4330c3286d67511c52df26b52116fd2a24a9a01e3fa4ffa8ef4c7314bb43e6e9 +size 2025