Files
test/source/blender/nodes/composite/node_composite_tree.cc
Brecht Van Lommel db43a8fb57 Refactor: realtime compositer context changes for render compositing
* Provide render data, node tree and color management directly instead
  of going through scene, as these may be modified by the render pipeline.
  Also better for cached texture hits this way.
* Change legacy pass type to pass name.
* Skip file output node when not doing final render.
* Gracefully handle incomplete render results.

Pull Request: https://projects.blender.org/blender/blender/pulls/108629
2023-06-07 14:17:37 +02:00

249 lines
7.1 KiB
C++

/* SPDX-FileCopyrightText: 2007 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup nodes
*/
#include <cstdio>
#include "DNA_color_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_node.hh"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "BKE_tracking.h"
#include "UI_resources.h"
#include "node_common.h"
#include "node_util.hh"
#include "RNA_access.h"
#include "RNA_prototypes.h"
#include "NOD_composite.h"
#include "node_composite_util.hh"
#ifdef WITH_COMPOSITOR_CPU
# include "COM_compositor.h"
#endif
static void composite_get_from_context(
const bContext *C, bNodeTreeType * /*treetype*/, bNodeTree **r_ntree, ID **r_id, ID **r_from)
{
Scene *scene = CTX_data_scene(C);
*r_from = nullptr;
*r_id = &scene->id;
*r_ntree = scene->nodetree;
}
static void foreach_nodeclass(Scene * /*scene*/, void *calldata, bNodeClassCallback func)
{
func(calldata, NODE_CLASS_INPUT, N_("Input"));
func(calldata, NODE_CLASS_OUTPUT, N_("Output"));
func(calldata, NODE_CLASS_OP_COLOR, N_("Color"));
func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector"));
func(calldata, NODE_CLASS_OP_FILTER, N_("Filter"));
func(calldata, NODE_CLASS_CONVERTER, N_("Converter"));
func(calldata, NODE_CLASS_MATTE, N_("Matte"));
func(calldata, NODE_CLASS_DISTORT, N_("Distort"));
func(calldata, NODE_CLASS_GROUP, N_("Group"));
func(calldata, NODE_CLASS_INTERFACE, N_("Interface"));
func(calldata, NODE_CLASS_LAYOUT, N_("Layout"));
}
/* local tree then owns all compbufs */
static void localize(bNodeTree *localtree, bNodeTree *ntree)
{
bNode *node = (bNode *)ntree->nodes.first;
bNode *local_node = (bNode *)localtree->nodes.first;
while (node != nullptr) {
/* Ensure new user input gets handled ok. */
node->runtime->need_exec = 0;
local_node->runtime->original = node;
/* move over the compbufs */
/* right after #blender::bke::ntreeCopyTree() `oldsock` pointers are valid */
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
if (node->id) {
if (node->flag & NODE_DO_OUTPUT) {
local_node->id = (ID *)node->id;
}
else {
local_node->id = nullptr;
}
}
}
node = node->next;
local_node = local_node->next;
}
}
static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
{
/* move over the compbufs and previews */
blender::bke::node_preview_merge_tree(ntree, localtree, true);
for (bNode *lnode = (bNode *)localtree->nodes.first; lnode; lnode = lnode->next) {
if (bNode *orig_node = nodeFindNodebyName(ntree, lnode->name)) {
if (ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
if (lnode->id && (lnode->flag & NODE_DO_OUTPUT)) {
/* image_merge does sanity check for pointers */
BKE_image_merge(bmain, (Image *)orig_node->id, (Image *)lnode->id);
}
}
else if (lnode->type == CMP_NODE_MOVIEDISTORTION) {
/* special case for distortion node: distortion context is allocating in exec function
* and to achieve much better performance on further calls this context should be
* copied back to original node */
if (lnode->storage) {
if (orig_node->storage) {
BKE_tracking_distortion_free((MovieDistortion *)orig_node->storage);
}
orig_node->storage = BKE_tracking_distortion_copy((MovieDistortion *)lnode->storage);
}
}
}
}
}
static void update(bNodeTree *ntree)
{
ntreeSetOutput(ntree);
ntree_update_reroute_nodes(ntree);
}
static void composite_node_add_init(bNodeTree * /*bnodetree*/, bNode *bnode)
{
/* Composite node will only show previews for input classes
* by default, other will be hidden
* but can be made visible with the show_preview option */
if (bnode->typeinfo->nclass != NODE_CLASS_INPUT) {
bnode->flag &= ~NODE_PREVIEW;
}
}
static bool composite_node_tree_socket_type_valid(bNodeTreeType * /*ntreetype*/,
bNodeSocketType *socket_type)
{
return blender::bke::nodeIsStaticSocketType(socket_type) &&
ELEM(socket_type->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA);
}
bNodeTreeType *ntreeType_Composite;
void register_node_tree_type_cmp()
{
bNodeTreeType *tt = ntreeType_Composite = MEM_cnew<bNodeTreeType>(__func__);
tt->type = NTREE_COMPOSIT;
strcpy(tt->idname, "CompositorNodeTree");
strcpy(tt->group_idname, "CompositorNodeGroup");
strcpy(tt->ui_name, N_("Compositor"));
tt->ui_icon = ICON_NODE_COMPOSITING;
strcpy(tt->ui_description, N_("Compositing nodes"));
tt->foreach_nodeclass = foreach_nodeclass;
tt->localize = localize;
tt->local_merge = local_merge;
tt->update = update;
tt->get_from_context = composite_get_from_context;
tt->node_add_init = composite_node_add_init;
tt->valid_socket_type = composite_node_tree_socket_type_valid;
tt->rna_ext.srna = &RNA_CompositorNodeTree;
ntreeTypeAdd(tt);
}
void ntreeCompositExecTree(Scene *scene,
bNodeTree *ntree,
RenderData *rd,
bool rendering,
int do_preview,
const char *view_name)
{
#ifdef WITH_COMPOSITOR_CPU
COM_execute(rd, scene, ntree, rendering, view_name);
#else
UNUSED_VARS(scene, ntree, rd, rendering, view_name);
#endif
UNUSED_VARS(do_preview);
}
/* *********************************************** */
void ntreeCompositUpdateRLayers(bNodeTree *ntree)
{
if (ntree == nullptr) {
return;
}
for (bNode *node : ntree->all_nodes()) {
if (node->type == CMP_NODE_R_LAYERS) {
node_cmp_rlayers_outputs(ntree, node);
}
}
}
void ntreeCompositTagRender(Scene *scene)
{
/* XXX Think using G_MAIN here is valid, since you want to update current file's scene nodes,
* not the ones in temp main generated for rendering?
* This is still rather weak though,
* ideally render struct would store own main AND original G_MAIN. */
for (Scene *sce_iter = (Scene *)G_MAIN->scenes.first; sce_iter;
sce_iter = (Scene *)sce_iter->id.next)
{
if (sce_iter->nodetree) {
for (bNode *node : sce_iter->nodetree->all_nodes()) {
if (node->id == (ID *)scene || node->type == CMP_NODE_COMPOSITE) {
BKE_ntree_update_tag_node_property(sce_iter->nodetree, node);
}
else if (node->type == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ {
BKE_ntree_update_tag_node_property(sce_iter->nodetree, node);
}
}
}
}
BKE_ntree_update_main(G_MAIN, nullptr);
}
void ntreeCompositClearTags(bNodeTree *ntree)
{
/* XXX: after render animation system gets a refresh, this call allows composite to end clean. */
if (ntree == nullptr) {
return;
}
for (bNode *node : ntree->all_nodes()) {
node->runtime->need_exec = 0;
if (node->type == NODE_GROUP) {
ntreeCompositClearTags((bNodeTree *)node->id);
}
}
}
void ntreeCompositTagNeedExec(bNode *node)
{
node->runtime->need_exec = true;
}