GPU: Codegen: Native support for repeat zones

Support loops at the GLSL level instead of relying on
NOD_shader_nodes_inline.
This improves compilation and runtime performance, avoids causing
recompilations on iteration count changes, and allows supporting
dynamic iteration counts.

(EEVEE-only)

Pull Request: https://projects.blender.org/blender/blender/pulls/145269
This commit is contained in:
Miguel Pozo
2025-10-08 16:38:14 +02:00
parent 2998b97d5e
commit 71f4277467
18 changed files with 391 additions and 107 deletions

View File

@@ -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);

View File

@@ -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<bNode *> stack;
blender::Stack<bNode *> 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<bNode *>(
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)

View File

@@ -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

View File

@@ -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);

View File

@@ -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<StringRefNull> &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<float>(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<StringRefNull> &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<float>(coefficients, 3);
}
eval_ss << ")";
}
source_reference(input);
break;
}
default:
eval_ss << input;
break;
}
eval_ss << ", ";
GPUOutput *output = static_cast<GPUOutput *>(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<int, GPUNode *> zone_starts;
blender::Map<int, GPUNode *> 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);
}
}

View File

@@ -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 :

View File

@@ -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>("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<GPUNode *> stack;
blender::Stack<GPUNode *> 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<GPUNode *>(graph->nodes.first), *next = nullptr; node;

View File

@@ -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);

View File

@@ -553,6 +553,7 @@ void gpu_material_library_use_function(blender::Set<blender::StringRefNull> &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<GPUSource *>(function->source);
used_libraries.add(source->filename.c_str());
}

View File

@@ -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

View File

@@ -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

View File

@@ -132,6 +132,16 @@ static bool node_insert_link(bke::NodeInsertLinkParams &params)
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<RepeatItemsAccessor>(&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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -54,6 +54,10 @@ void node_combsep_color_label(const ListBase *sockets, NodeCombSepColorMode mode
*/
bool node_insert_link_default(blender::bke::NodeInsertLinkParams &params);
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);

View File

@@ -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) {

View File

@@ -7,7 +7,6 @@
*/
#include <cstring>
#include <stack>
#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<StackNode> stack;
stack.push({start_node, 0});
blender::Stack<StackNode> stack;
blender::Stack<StackNode> 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--) {

Binary file not shown.