Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
259 lines
7.1 KiB
C++
259 lines
7.1 KiB
C++
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup nodes
|
|
*/
|
|
|
|
#include "DNA_node_types.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_global.h"
|
|
#include "BKE_node.hh"
|
|
#include "BKE_node_runtime.hh"
|
|
#include "BKE_node_tree_update.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "node_exec.hh"
|
|
#include "node_util.hh"
|
|
|
|
static int node_exec_socket_use_stack(bNodeSocket *sock)
|
|
{
|
|
/* NOTE: INT supported as FLOAT. Only for EEVEE. */
|
|
return ELEM(sock->type, SOCK_INT, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_SHADER);
|
|
}
|
|
|
|
bNodeStack *node_get_socket_stack(bNodeStack *stack, bNodeSocket *sock)
|
|
{
|
|
if (stack && sock && sock->stack_index >= 0) {
|
|
return stack + sock->stack_index;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack **out)
|
|
{
|
|
/* build pointer stack */
|
|
if (in) {
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
|
*(in++) = node_get_socket_stack(stack, sock);
|
|
}
|
|
}
|
|
|
|
if (out) {
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
|
*(out++) = node_get_socket_stack(stack, sock);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void node_init_input_index(bNodeSocket *sock, int *index)
|
|
{
|
|
/* Only consider existing link if from socket is valid! */
|
|
if (sock->link && !(sock->link->flag & NODE_LINK_MUTED) && sock->link->fromsock &&
|
|
sock->link->fromsock->stack_index >= 0)
|
|
{
|
|
sock->stack_index = sock->link->fromsock->stack_index;
|
|
}
|
|
else {
|
|
if (node_exec_socket_use_stack(sock)) {
|
|
sock->stack_index = (*index)++;
|
|
}
|
|
else {
|
|
sock->stack_index = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void node_init_output_index_muted(bNodeSocket *sock,
|
|
int *index,
|
|
const blender::MutableSpan<bNodeLink> internal_links)
|
|
{
|
|
const bNodeLink *link;
|
|
/* copy the stack index from internally connected input to skip the node */
|
|
for (bNodeLink &iter_link : internal_links) {
|
|
if (iter_link.tosock == sock) {
|
|
sock->stack_index = iter_link.fromsock->stack_index;
|
|
/* set the link pointer to indicate that this socket
|
|
* should not overwrite the stack value!
|
|
*/
|
|
sock->link = &iter_link;
|
|
link = &iter_link;
|
|
break;
|
|
}
|
|
}
|
|
/* if not internally connected, assign a new stack index anyway to avoid bad stack access */
|
|
if (!link) {
|
|
if (node_exec_socket_use_stack(sock)) {
|
|
sock->stack_index = (*index)++;
|
|
}
|
|
else {
|
|
sock->stack_index = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void node_init_output_index(bNodeSocket *sock, int *index)
|
|
{
|
|
if (node_exec_socket_use_stack(sock)) {
|
|
sock->stack_index = (*index)++;
|
|
}
|
|
else {
|
|
sock->stack_index = -1;
|
|
}
|
|
}
|
|
|
|
/* basic preparation of socket stacks */
|
|
static bNodeStack *setup_stack(bNodeStack *stack, bNodeTree *ntree, bNode *node, bNodeSocket *sock)
|
|
{
|
|
bNodeStack *ns = node_get_socket_stack(stack, sock);
|
|
if (!ns) {
|
|
return nullptr;
|
|
}
|
|
|
|
/* don't mess with remote socket stacks, these are initialized by other nodes! */
|
|
if (sock->link && !(sock->link->flag & NODE_LINK_MUTED)) {
|
|
return ns;
|
|
}
|
|
|
|
ns->sockettype = sock->type;
|
|
|
|
switch (sock->type) {
|
|
case SOCK_FLOAT:
|
|
ns->vec[0] = node_socket_get_float(ntree, node, sock);
|
|
break;
|
|
case SOCK_VECTOR:
|
|
node_socket_get_vector(ntree, node, sock, ns->vec);
|
|
break;
|
|
case SOCK_RGBA:
|
|
node_socket_get_color(ntree, node, sock, ns->vec);
|
|
break;
|
|
}
|
|
|
|
return ns;
|
|
}
|
|
|
|
bNodeTreeExec *ntree_exec_begin(bNodeExecContext *context,
|
|
bNodeTree *ntree,
|
|
bNodeInstanceKey parent_key)
|
|
{
|
|
using namespace blender;
|
|
bNodeTreeExec *exec;
|
|
bNode *node;
|
|
bNodeExec *nodeexec;
|
|
bNodeInstanceKey nodekey;
|
|
bNodeStack *ns;
|
|
int index;
|
|
/* XXX: texture-nodes have threading issues with muting, have to disable it there. */
|
|
|
|
/* ensure all sock->link pointers and node levels are correct */
|
|
/* Using global main here is likely totally wrong, not sure what to do about that one though...
|
|
* We cannot even check ntree is in global main,
|
|
* since most of the time it won't be (thanks to ntree design)!!! */
|
|
BKE_ntree_update_main_tree(G.main, ntree, nullptr);
|
|
|
|
ntree->ensure_topology_cache();
|
|
const Span<bNode *> nodelist = ntree->toposort_left_to_right();
|
|
|
|
/* XXX could let callbacks do this for specialized data */
|
|
exec = MEM_cnew<bNodeTreeExec>("node tree execution data");
|
|
/* Back-pointer to node tree. */
|
|
exec->nodetree = ntree;
|
|
|
|
/* set stack indices */
|
|
index = 0;
|
|
for (const int n : nodelist.index_range()) {
|
|
node = nodelist[n];
|
|
|
|
/* init node socket stack indexes */
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
|
node_init_input_index(sock, &index);
|
|
}
|
|
|
|
if (node->flag & NODE_MUTED || node->type == NODE_REROUTE) {
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
|
node_init_output_index_muted(sock, &index, node->runtime->internal_links);
|
|
}
|
|
}
|
|
else {
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
|
node_init_output_index(sock, &index);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* allocated exec data pointers for nodes */
|
|
exec->totnodes = nodelist.size();
|
|
exec->nodeexec = (bNodeExec *)MEM_callocN(exec->totnodes * sizeof(bNodeExec),
|
|
"node execution data");
|
|
/* allocate data pointer for node stack */
|
|
exec->stacksize = index;
|
|
exec->stack = (bNodeStack *)MEM_callocN(exec->stacksize * sizeof(bNodeStack), "bNodeStack");
|
|
|
|
/* all non-const results are considered inputs */
|
|
int n;
|
|
for (n = 0; n < exec->stacksize; n++) {
|
|
exec->stack[n].hasinput = 1;
|
|
}
|
|
|
|
/* prepare all nodes for execution */
|
|
for (n = 0, nodeexec = exec->nodeexec; n < nodelist.size(); n++, nodeexec++) {
|
|
node = nodeexec->node = nodelist[n];
|
|
nodeexec->free_exec_fn = node->typeinfo->free_exec_fn;
|
|
|
|
/* tag inputs */
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
|
|
/* disable the node if an input link is invalid */
|
|
if (sock->link && !(sock->link->flag & NODE_LINK_VALID)) {
|
|
node->runtime->need_exec = 0;
|
|
}
|
|
|
|
ns = setup_stack(exec->stack, ntree, node, sock);
|
|
if (ns) {
|
|
ns->hasoutput = 1;
|
|
}
|
|
}
|
|
|
|
/* tag all outputs */
|
|
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
|
|
/* ns = */ setup_stack(exec->stack, ntree, node, sock);
|
|
}
|
|
|
|
nodekey = BKE_node_instance_key(parent_key, ntree, node);
|
|
nodeexec->data.preview = context->previews ? (bNodePreview *)BKE_node_instance_hash_lookup(
|
|
context->previews, nodekey) :
|
|
nullptr;
|
|
if (node->typeinfo->init_exec_fn) {
|
|
nodeexec->data.data = node->typeinfo->init_exec_fn(context, node, nodekey);
|
|
}
|
|
}
|
|
|
|
return exec;
|
|
}
|
|
|
|
void ntree_exec_end(bNodeTreeExec *exec)
|
|
{
|
|
bNodeExec *nodeexec;
|
|
int n;
|
|
|
|
if (exec->stack) {
|
|
MEM_freeN(exec->stack);
|
|
}
|
|
|
|
for (n = 0, nodeexec = exec->nodeexec; n < exec->totnodes; n++, nodeexec++) {
|
|
if (nodeexec->free_exec_fn) {
|
|
nodeexec->free_exec_fn(nodeexec->data.data);
|
|
}
|
|
}
|
|
|
|
if (exec->nodeexec) {
|
|
MEM_freeN(exec->nodeexec);
|
|
}
|
|
|
|
MEM_freeN(exec);
|
|
}
|