Blender V4.5
node_composite_image.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2006 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
10
11#include "BLI_assert.h"
12#include "BLI_linklist.h"
13#include "BLI_listbase.h"
15#include "BLI_string.h"
16#include "BLI_string_ref.hh"
17#include "BLI_utildefines.h"
18
19#include "BKE_context.hh"
20#include "BKE_global.hh"
21#include "BKE_image.hh"
22#include "BKE_lib_id.hh"
23#include "BKE_main.hh"
24#include "BKE_scene.hh"
25
27
28#include "DNA_image_types.h"
29#include "DNA_scene_types.h"
30#include "DNA_vec_types.h"
31
32#include "RE_engine.h"
33#include "RE_pipeline.h"
34
35#include "RNA_access.hh"
36
37#include "UI_interface.hh"
38#include "UI_resources.hh"
39
40#include "GPU_shader.hh"
41
43#include "COM_node_operation.hh"
44#include "COM_utilities.hh"
45
46/* **************** IMAGE (and RenderResult, multi-layer image) ******************** */
47
49 {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
50 {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
51 {SOCK_FLOAT, N_(RE_PASSNAME_Z), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
52 {SOCK_VECTOR, N_(RE_PASSNAME_NORMAL), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
53 {SOCK_VECTOR, N_(RE_PASSNAME_UV), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
54 {SOCK_VECTOR, N_(RE_PASSNAME_VECTOR), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
55 {SOCK_VECTOR, N_(RE_PASSNAME_POSITION), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
56 {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
57 {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
58 {SOCK_RGBA, N_(RE_PASSNAME_SHADOW), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
59 {SOCK_RGBA, N_(RE_PASSNAME_AO), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
60 {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
61 {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
62 {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
63 {SOCK_FLOAT, N_(RE_PASSNAME_INDEXOB), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
64 {SOCK_FLOAT, N_(RE_PASSNAME_INDEXMA), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
65 {SOCK_FLOAT, N_(RE_PASSNAME_MIST), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
66 {SOCK_RGBA, N_(RE_PASSNAME_EMIT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
67 {SOCK_RGBA, N_(RE_PASSNAME_ENVIRONMENT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
68 {SOCK_RGBA, N_(RE_PASSNAME_DIFFUSE_DIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
69 {SOCK_RGBA, N_(RE_PASSNAME_DIFFUSE_INDIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
70 {SOCK_RGBA, N_(RE_PASSNAME_DIFFUSE_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
71 {SOCK_RGBA, N_(RE_PASSNAME_GLOSSY_DIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
72 {SOCK_RGBA, N_(RE_PASSNAME_GLOSSY_INDIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
73 {SOCK_RGBA, N_(RE_PASSNAME_GLOSSY_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
74 {SOCK_RGBA, N_(RE_PASSNAME_TRANSM_DIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
75 {SOCK_RGBA, N_(RE_PASSNAME_TRANSM_INDIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
76 {SOCK_RGBA, N_(RE_PASSNAME_TRANSM_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
77 {SOCK_RGBA, N_(RE_PASSNAME_SUBSURFACE_DIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
78 {SOCK_RGBA, N_(RE_PASSNAME_SUBSURFACE_INDIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
79 {SOCK_RGBA, N_(RE_PASSNAME_SUBSURFACE_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
80 {-1, ""},
81};
82#define NUM_LEGACY_SOCKETS (ARRAY_SIZE(cmp_node_rlayers_out) - 1)
83
84static const char *cmp_node_new_pass_name(const char *name)
85{
86 if (STREQ(name, "DiffDir")) {
87 return "Diffuse Direct";
88 }
89 if (STREQ(name, "DiffInd")) {
90 return "Diffuse Indirect";
91 }
92 if (STREQ(name, "DiffCol")) {
93 return "Diffuse Color";
94 }
95 if (STREQ(name, "GlossDir")) {
96 return "Glossy Direct";
97 }
98 if (STREQ(name, "GlossInd")) {
99 return "Glossy Indirect";
100 }
101 if (STREQ(name, "GlossCol")) {
102 return "Glossy Color";
103 }
104 if (STREQ(name, "TransDir")) {
105 return "Transmission Direct";
106 }
107 if (STREQ(name, "TransInd")) {
108 return "Transmission Indirect";
109 }
110 if (STREQ(name, "TransCol")) {
111 return "Transmission Color";
112 }
113 if (STREQ(name, "VolumeDir")) {
114 return "Volume Direct";
115 }
116 if (STREQ(name, "VolumeInd")) {
117 return "Volume Indirect";
118 }
119 if (STREQ(name, "VolumeCol")) {
120 return "Volume Color";
121 }
122 if (STREQ(name, "AO")) {
123 return "Ambient Occlusion";
124 }
125 if (STREQ(name, "Env")) {
126 return "Environment";
127 }
128 if (STREQ(name, "IndexMA")) {
129 return "Material Index";
130 }
131 if (STREQ(name, "IndexOB")) {
132 return "Object Index";
133 }
134 if (STREQ(name, "GreasePencil")) {
135 return "Grease Pencil";
136 }
137 if (STREQ(name, "Emit")) {
138 return "Emission";
139 }
140
141 return nullptr;
142}
143
145 bNode *node,
146 const char *name,
147 const char *passname,
148 int rres_index,
150 int /*is_rlayers*/,
151 LinkNodePair *available_sockets,
152 int *prev_index)
153{
155 &node->outputs, name, offsetof(bNodeSocket, name));
156
157 /* Forward compatibility, rename new socket names to old ones. */
158 if (sock == nullptr) {
159 const char *new_name = cmp_node_new_pass_name(name);
160 if (new_name) {
161 sock = (bNodeSocket *)BLI_findstring(&node->outputs, new_name, offsetof(bNodeSocket, name));
162 if (sock) {
163 STRNCPY(sock->name, name);
164 STRNCPY(sock->identifier, name);
165 }
166 }
167 }
168
169 /* Replace if types don't match. */
170 if (sock && sock->type != type) {
171 blender::bke::node_remove_socket(*ntree, *node, *sock);
172 sock = nullptr;
173 }
174
175 /* Create socket if it doesn't exist yet. */
176 if (sock == nullptr) {
177 if (rres_index >= 0) {
179 ntree, node, &cmp_node_rlayers_out[rres_index], SOCK_OUT);
180 }
181 else {
183 *ntree, *node, SOCK_OUT, type, PROP_NONE, name, name);
184 }
185 /* extra socket info */
186 NodeImageLayer *sockdata = MEM_callocN<NodeImageLayer>(__func__);
187 sock->storage = sockdata;
188 }
189
190 NodeImageLayer *sockdata = (NodeImageLayer *)sock->storage;
191 if (sockdata) {
192 STRNCPY(sockdata->pass_name, passname);
193 }
194
195 /* Reorder sockets according to order that passes are added. */
196 const int after_index = (*prev_index)++;
197 bNodeSocket *after_sock = (bNodeSocket *)BLI_findlink(&node->outputs, after_index);
198 BLI_remlink(&node->outputs, sock);
199 BLI_insertlinkafter(&node->outputs, after_sock, sock);
200
201 BLI_linklist_append(available_sockets, sock);
202}
203
205{
206 switch (pass->channels) {
207 case 1:
208 return SOCK_FLOAT;
209 case 2:
210 case 3:
211 if (STR_ELEM(pass->chan_id, "RGB", "rgb")) {
212 return SOCK_RGBA;
213 }
214 else {
215 return SOCK_VECTOR;
216 }
217 case 4:
218 if (STR_ELEM(pass->chan_id, "RGBA", "rgba")) {
219 return SOCK_RGBA;
220 }
221 else {
222 return SOCK_VECTOR;
223 }
224 default:
225 break;
226 }
227
229 return SOCK_FLOAT;
230}
231
233 bNode *node,
234 LinkNodePair *available_sockets)
235{
236 Image *ima = (Image *)node->id;
237 ImBuf *ibuf;
238 int prev_index = -1;
239 if (ima) {
240 ImageUser *iuser = (ImageUser *)node->storage;
241 ImageUser load_iuser = {nullptr};
242 int offset = BKE_image_sequence_guess_offset(ima);
243
244 /* It is possible that image user in this node is not
245 * properly updated yet. In this case loading image will
246 * fail and sockets detection will go wrong.
247 *
248 * So we manually construct image user to be sure first
249 * image from sequence (that one which is set as filename
250 * for image data-block) is used for sockets detection. */
251 load_iuser.framenr = offset;
252
253 /* make sure ima->type is correct */
254 ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, nullptr);
255
256 if (ima->rr) {
257 RenderLayer *rl = (RenderLayer *)BLI_findlink(&ima->rr->layers, iuser->layer);
258
259 if (rl) {
260 LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
261 const eNodeSocketDatatype type = socket_type_from_pass(rpass);
263 node,
264 rpass->name,
265 rpass->name,
266 -1,
267 type,
268 false,
269 available_sockets,
270 &prev_index);
271 /* Special handling for the Combined pass to ensure compatibility. */
272 if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
274 node,
275 "Alpha",
276 rpass->name,
277 -1,
279 false,
280 available_sockets,
281 &prev_index);
282 }
283 }
284 BKE_image_release_ibuf(ima, ibuf, nullptr);
285 return;
286 }
287 }
288 }
289
291 node,
292 "Image",
294 -1,
295 SOCK_RGBA,
296 false,
297 available_sockets,
298 &prev_index);
300 node,
301 "Alpha",
303 -1,
305 false,
306 available_sockets,
307 &prev_index);
308
309 if (ima) {
310 BKE_image_release_ibuf(ima, ibuf, nullptr);
311 }
312}
313
318
320 bNode *node,
321 Scene *scene,
322 ViewLayer *view_layer,
323 const char *name,
325{
327
328 if (scene == nullptr || view_layer == nullptr || data == nullptr || node->id != (ID *)scene) {
329 return;
330 }
331
332 ViewLayer *node_view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1);
333 if (node_view_layer != view_layer) {
334 return;
335 }
336
337 /* Special handling for the Combined pass to ensure compatibility. */
338 if (STREQ(name, RE_PASSNAME_COMBINED)) {
340 ntree, node, "Image", name, -1, type, true, data->available_sockets, &data->prev_index);
342 node,
343 "Alpha",
344 name,
345 -1,
347 true,
348 data->available_sockets,
349 &data->prev_index);
350 }
351 else {
353 ntree, node, name, name, -1, type, true, data->available_sockets, &data->prev_index);
354 }
355}
356
361
362static void cmp_node_rlayer_create_outputs_cb(void *userdata,
363 Scene *scene,
364 ViewLayer *view_layer,
365 const char *name,
366 int /*channels*/,
367 const char * /*chanid*/,
369{
371 node_cmp_rlayers_register_pass(&data.ntree, &data.node, scene, view_layer, name, type);
372}
373
375 bNode *node,
376 LinkNodePair *available_sockets)
377{
378 Scene *scene = (Scene *)node->id;
379
380 if (scene) {
381 RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
382 if (engine_type && engine_type->update_render_passes) {
383 ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1);
384 if (view_layer) {
385 RLayerUpdateData *data = MEM_mallocN<RLayerUpdateData>("render layer update data");
386 data->available_sockets = available_sockets;
387 data->prev_index = -1;
388 node->storage = data;
389
390 CreateOutputUserData userdata = {*ntree, *node};
391
392 RenderEngine *engine = RE_engine_create(engine_type);
394 engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, &userdata);
395 RE_engine_free(engine);
396
397 if ((scene->r.mode & R_EDGE_FRS) &&
399 {
401 ntree, node, scene, view_layer, RE_PASSNAME_FREESTYLE, SOCK_RGBA);
402 }
403
406 ntree, node, scene, view_layer, RE_PASSNAME_GREASE_PENCIL, SOCK_RGBA);
407 }
408
410 node->storage = nullptr;
411
412 return;
413 }
414 }
415 }
416
417 int prev_index = -1;
419 node,
420 "Image",
423 SOCK_RGBA,
424 true,
425 available_sockets,
426 &prev_index);
428 node,
429 "Alpha",
433 true,
434 available_sockets,
435 &prev_index);
436}
437
438/* XXX make this into a generic socket verification function for dynamic socket replacement
439 * (multi-layer, groups, static templates). */
440static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rlayer)
441{
442 bNodeSocket *sock, *sock_next;
443 LinkNodePair available_sockets = {nullptr, nullptr};
444
445 /* XXX make callback */
446 if (rlayer) {
447 cmp_node_rlayer_create_outputs(ntree, node, &available_sockets);
448 }
449 else {
450 cmp_node_image_create_outputs(ntree, node, &available_sockets);
451 }
452
453 /* Get rid of sockets whose passes are not available in the image.
454 * If sockets that are not available would be deleted, the connections to them would be lost
455 * when e.g. opening a file (since there's no render at all yet).
456 * Therefore, sockets with connected links will just be set as unavailable.
457 *
458 * Another important detail comes from compatibility with the older socket model, where there
459 * was a fixed socket per pass type that was just hidden or not. Therefore, older versions expect
460 * the first 31 passes to belong to a specific pass type.
461 * So, we keep those 31 always allocated before the others as well,
462 * even if they have no links attached. */
463 int sock_index = 0;
464 for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock_next, sock_index++) {
465 sock_next = sock->next;
466 if (BLI_linklist_index(available_sockets.list, sock) >= 0) {
467 sock->flag &= ~SOCK_HIDDEN;
469 }
470 else {
471 bNodeLink *link;
472 for (link = (bNodeLink *)ntree->links.first; link; link = link->next) {
473 if (link->fromsock == sock) {
474 break;
475 }
476 }
477 if (!link && (!rlayer || sock_index >= NUM_LEGACY_SOCKETS)) {
478 MEM_freeN(reinterpret_cast<NodeImageLayer *>(sock->storage));
479 blender::bke::node_remove_socket(*ntree, *node, *sock);
480 }
481 else {
482 blender::bke::node_set_socket_availability(*ntree, *sock, false);
483 }
484 }
485 }
486
487 BLI_linklist_free(available_sockets.list, nullptr);
488}
489
491
492static void cmp_node_image_update(bNodeTree *ntree, bNode *node)
493{
494 /* avoid unnecessary updates, only changes to the image/image user data are of interest */
495 if (node->runtime->update & NODE_UPDATE_ID) {
496 cmp_node_image_verify_outputs(ntree, node, false);
497 }
498
499 cmp_node_update_default(ntree, node);
500}
501
502static void node_composit_init_image(bNodeTree *ntree, bNode *node)
503{
504 ImageUser *iuser = MEM_callocN<ImageUser>(__func__);
505 node->storage = iuser;
506 iuser->frames = 1;
507 iuser->sfra = 1;
508 iuser->flag |= IMA_ANIM_ALWAYS;
509
510 /* setup initial outputs */
511 cmp_node_image_verify_outputs(ntree, node, false);
512}
513
515{
516 /* free extra socket info */
517 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
518 MEM_freeN(reinterpret_cast<NodeImageLayer *>(sock->storage));
519 }
520
521 MEM_freeN(reinterpret_cast<ImageUser *>(node->storage));
522}
523
524static void node_composit_copy_image(bNodeTree * /*dst_ntree*/,
525 bNode *dest_node,
526 const bNode *src_node)
527{
528 dest_node->storage = MEM_dupallocN(src_node->storage);
529
530 const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first;
531 bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first;
532 while (dest_output_sock != nullptr) {
533 dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage);
534
535 src_output_sock = src_output_sock->next;
536 dest_output_sock = dest_output_sock->next;
537 }
538}
539
540using namespace blender::compositor;
541
543 public:
545
546 void execute() override
547 {
548 for (const bNodeSocket *output : this->node()->output_sockets()) {
550 continue;
551 }
552
553 compute_output(output->identifier);
554 }
555 }
556
557 void compute_output(StringRef identifier)
558 {
559 if (!should_compute_output(identifier)) {
560 return;
561 }
562
563 const StringRef pass_name = this->get_pass_name(identifier);
564 Result cached_image = context().cache_manager().cached_images.get(
565 context(), get_image(), get_image_user(), pass_name.data());
566
567 Result &result = get_result(identifier);
568 if (!cached_image.is_allocated()) {
569 result.allocate_invalid();
570 return;
571 }
572
573 /* Alpha is not an actual pass, but one that is extracted from the combined pass. */
574 if (identifier == "Alpha" && pass_name == RE_PASSNAME_COMBINED) {
575 extract_alpha(context(), cached_image, result);
576 }
577 else {
578 result.set_type(cached_image.type());
579 result.set_precision(cached_image.precision());
580 result.wrap_external(cached_image);
581 }
582 }
583
584 /* Get the name of the pass corresponding to the output with the given identifier. */
585 const char *get_pass_name(StringRef identifier)
586 {
587 DOutputSocket output = node().output_by_identifier(identifier);
588 return static_cast<NodeImageLayer *>(output->storage)->pass_name;
589 }
590
592 {
593 return reinterpret_cast<Image *>(bnode().id);
594 }
595
597 {
598 return static_cast<ImageUser *>(bnode().storage);
599 }
600};
601
603{
604 return new ImageOperation(context, node);
605}
606
607} // namespace blender::nodes::node_composite_image_cc
608
610{
611 namespace file_ns = blender::nodes::node_composite_image_cc;
612
613 static blender::bke::bNodeType ntype;
614
615 cmp_node_type_base(&ntype, "CompositorNodeImage", CMP_NODE_IMAGE);
616 ntype.ui_name = "Image";
617 ntype.ui_description = "Input image or movie file";
618 ntype.enum_name_legacy = "IMAGE";
619 ntype.nclass = NODE_CLASS_INPUT;
620 ntype.initfunc = file_ns::node_composit_init_image;
622 ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image);
623 ntype.updatefunc = file_ns::cmp_node_image_update;
624 ntype.get_compositor_operation = file_ns::get_compositor_operation;
626 ntype.flag |= NODE_PREVIEW;
627
629}
631
632/* **************** RENDER RESULT ******************** */
633
635{
636 cmp_node_image_verify_outputs(ntree, node, true);
637}
638
639const char *node_cmp_rlayers_sock_to_pass(int sock_index)
640{
641 if (sock_index >= NUM_LEGACY_SOCKETS) {
642 return nullptr;
643 }
644 const char *name = cmp_node_rlayers_out[sock_index].name;
645 /* Exception for alpha, which is derived from Combined. */
646 return STREQ(name, "Alpha") ? RE_PASSNAME_COMBINED : name;
647}
648
650
652{
653 Scene *scene = CTX_data_scene(C);
654 bNode *node = (bNode *)ptr->data;
655 int sock_index = 0;
656
657 node->id = &scene->id;
658 id_us_plus(node->id);
659
660 for (bNodeSocket *sock = (bNodeSocket *)node->outputs.first; sock;
661 sock = sock->next, sock_index++)
662 {
663 NodeImageLayer *sockdata = MEM_callocN<NodeImageLayer>(__func__);
664 sock->storage = sockdata;
665
666 STRNCPY(sockdata->pass_name, node_cmp_rlayers_sock_to_pass(sock_index));
667 }
668}
669
671 const bNodeTree *ntree,
672 const char **r_disabled_hint)
673{
674 if (!STREQ(ntree->idname, "CompositorNodeTree")) {
675 *r_disabled_hint = RPT_("Not a compositor node tree");
676 return false;
677 }
678
679 Scene *scene;
680
681 /* XXX ugly: check if ntree is a local scene node tree.
682 * Render layers node can only be used in local `scene->nodetree`,
683 * since it directly links to the scene.
684 */
685 for (scene = (Scene *)G.main->scenes.first; scene; scene = (Scene *)scene->id.next) {
686 if (scene->nodetree == ntree) {
687 break;
688 }
689 }
690
691 if (scene == nullptr) {
692 *r_disabled_hint = RPT_(
693 "The node tree must be the compositing node tree of any scene in the file");
694 return false;
695 }
696 return true;
697}
698
700{
701 /* free extra socket info */
702 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
703 if (sock->storage) {
704 MEM_freeN(reinterpret_cast<NodeImageLayer *>(sock->storage));
705 }
706 }
707}
708
709static void node_composit_copy_rlayers(bNodeTree * /*dst_ntree*/,
710 bNode *dest_node,
711 const bNode *src_node)
712{
713 /* copy extra socket info */
714 const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first;
715 bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first;
716 while (dest_output_sock != nullptr) {
717 dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage);
718
719 src_output_sock = src_output_sock->next;
720 dest_output_sock = dest_output_sock->next;
721 }
722}
723
724static void cmp_node_rlayers_update(bNodeTree *ntree, bNode *node)
725{
726 cmp_node_image_verify_outputs(ntree, node, true);
727
728 cmp_node_update_default(ntree, node);
729}
730
732{
733 bNode *node = (bNode *)ptr->data;
734 uiLayout *col, *row;
735
736 uiTemplateID(layout, C, ptr, "scene", nullptr, nullptr, nullptr);
737
738 if (!node->id) {
739 return;
740 }
741
742 col = &layout->column(false);
743 row = &col->row(true);
744 row->prop(ptr, "layer", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
745
746 PropertyRNA *prop = RNA_struct_find_property(ptr, "layer");
747 const char *layer_name;
748 if (!RNA_property_enum_identifier(C, ptr, prop, RNA_property_enum_get(ptr, prop), &layer_name)) {
749 return;
750 }
751
752 PointerRNA scn_ptr;
753 char scene_name[MAX_ID_NAME - 2];
754 scn_ptr = RNA_pointer_get(ptr, "scene");
755 RNA_string_get(&scn_ptr, "name", scene_name);
756
757 PointerRNA op_ptr = row->op(
758 "RENDER_OT_render", "", ICON_RENDER_STILL, WM_OP_INVOKE_DEFAULT, UI_ITEM_NONE);
759 RNA_string_set(&op_ptr, "layer", layer_name);
760 RNA_string_set(&op_ptr, "scene", scene_name);
761}
762
763using namespace blender::compositor;
764
766 public:
768
769 void execute() override
770 {
771 const Scene *scene = reinterpret_cast<const Scene *>(this->bnode().id);
772 const int view_layer = this->bnode().custom1;
773
774 Result &image_result = this->get_result("Image");
775 Result &alpha_result = this->get_result("Alpha");
776
777 if (image_result.should_compute() || alpha_result.should_compute()) {
778 const Result combined_pass = this->context().get_pass(
779 scene, view_layer, RE_PASSNAME_COMBINED);
780 if (image_result.should_compute()) {
781 this->execute_pass(combined_pass, image_result);
782 }
783 if (alpha_result.should_compute()) {
784 this->execute_pass(combined_pass, alpha_result);
785 }
786 }
787
788 for (const bNodeSocket *output : this->node()->output_sockets()) {
790 continue;
791 }
792
793 if (STR_ELEM(output->identifier, "Image", "Alpha")) {
794 continue;
795 }
796
797 Result &result = this->get_result(output->identifier);
798 if (!result.should_compute()) {
799 continue;
800 }
801
802 const char *pass_name = this->get_pass_name(output->identifier);
803 this->context().populate_meta_data_for_pass(scene, view_layer, pass_name, result.meta_data);
804
805 const Result pass = this->context().get_pass(scene, view_layer, pass_name);
806 this->execute_pass(pass, result);
807 }
808 }
809
810 void execute_pass(const Result &pass, Result &result)
811 {
812 if (!pass.is_allocated()) {
813 /* Pass not rendered yet, or not supported by viewport. */
814 result.allocate_invalid();
815 this->context().set_info_message("Viewport compositor setup not fully supported");
816 return;
817 }
818
819 if (!this->context().is_valid_compositing_region()) {
820 result.allocate_invalid();
821 return;
822 }
823
824 /* Vector sockets are 3D by default, so we need to overwrite the type if the pass turned out to
825 * be 4D. */
826 if (result.type() == ResultType::Float3 && pass.type() == ResultType::Float4) {
827 result.set_type(pass.type());
828 }
829 result.set_precision(pass.precision());
830
831 if (this->context().use_gpu()) {
832 this->execute_pass_gpu(pass, result);
833 }
834 else {
835 this->execute_pass_cpu(pass, result);
836 }
837 }
838
840 {
841 GPUShader *shader = this->context().get_shader(this->get_shader_name(pass, result),
842 result.precision());
843 GPU_shader_bind(shader);
844
845 /* The compositing space might be limited to a subset of the pass texture, so only read that
846 * compositing region into an appropriately sized result. */
847 const rcti compositing_region = this->context().get_compositing_region();
848 const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin);
849 GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
850
851 pass.bind_as_texture(shader, "input_tx");
852
853 result.allocate_texture(Domain(this->context().get_compositing_region_size()));
854 result.bind_as_image(shader, "output_img");
855
856 compute_dispatch_threads_at_least(shader, result.domain().size);
857
859 pass.unbind_as_texture();
860 result.unbind_as_image();
861 }
862
863 const char *get_shader_name(const Result &pass, const Result &result)
864 {
865 /* Special case for alpha output. */
866 if (pass.type() == ResultType::Color && result.type() == ResultType::Float) {
867 return "compositor_read_input_alpha";
868 }
869
870 switch (pass.type()) {
872 return "compositor_read_input_float";
876 return "compositor_read_input_float4";
877 case ResultType::Int:
878 case ResultType::Int2:
880 case ResultType::Bool:
881 /* Not supported. */
882 break;
883 }
884
886 return nullptr;
887 }
888
890 {
891 /* The compositing space might be limited to a subset of the pass texture, so only read that
892 * compositing region into an appropriately sized result. */
893 const rcti compositing_region = this->context().get_compositing_region();
894 const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin);
895
896 result.allocate_texture(Domain(this->context().get_compositing_region_size()));
897
898 /* Special case for alpha output. */
899 if (pass.type() == ResultType::Color && result.type() == ResultType::Float) {
900 parallel_for(result.domain().size, [&](const int2 texel) {
901 result.store_pixel(texel, pass.load_pixel<float4>(texel + lower_bound).w);
902 });
903 }
904 else {
905 parallel_for(result.domain().size, [&](const int2 texel) {
906 result.store_pixel_generic_type(texel, pass.load_pixel_generic_type(texel + lower_bound));
907 });
908 }
909 }
910
911 /* Get the name of the pass corresponding to the output with the given identifier. */
912 const char *get_pass_name(StringRef identifier)
913 {
914 DOutputSocket output = this->node().output_by_identifier(identifier);
915 return static_cast<NodeImageLayer *>(output->storage)->pass_name;
916 }
917};
918
920{
921 return new RenderLayerOperation(context, node);
922}
923
924} // namespace blender::nodes::node_composite_render_layer_cc
925
927{
929
930 static blender::bke::bNodeType ntype;
931
932 cmp_node_type_base(&ntype, "CompositorNodeRLayers", CMP_NODE_R_LAYERS);
933 ntype.ui_name = "Render Layers";
934 ntype.ui_description = "Input render passes from a scene render";
935 ntype.enum_name_legacy = "R_LAYERS";
936 ntype.nclass = NODE_CLASS_INPUT;
938 ntype.draw_buttons = file_ns::node_composit_buts_viewlayers;
939 ntype.initfunc_api = file_ns::node_composit_init_rlayers;
940 ntype.poll = file_ns::node_composit_poll_rlayers;
941 ntype.get_compositor_operation = file_ns::get_compositor_operation;
943 "Render passes in the Viewport compositor are only supported in EEVEE");
944 ntype.flag |= NODE_PREVIEW;
946 std::nullopt,
947 file_ns::node_composit_free_rlayers,
948 file_ns::node_composit_copy_rlayers);
949 ntype.updatefunc = file_ns::cmp_node_rlayers_update;
952
954}
Scene * CTX_data_scene(const bContext *C)
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
int BKE_image_sequence_guess_offset(Image *image)
void id_us_plus(ID *id)
Definition lib_id.cc:353
#define NODE_CLASS_INPUT
Definition BKE_node.hh:433
#define RRES_OUT_ALPHA
#define RRES_OUT_IMAGE
#define CMP_NODE_R_LAYERS
#define CMP_NODE_IMAGE
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findstring(const ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:608
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define STR_ELEM(...)
Definition BLI_string.h:656
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define STREQ(a, b)
#define RPT_(msgid)
@ FREESTYLE_AS_RENDER_PASS
@ IMA_ANIM_ALWAYS
@ GREASE_PENCIL_AS_SEPARATE_PASS
@ NODE_PREVIEW
@ NODE_UPDATE_ID
@ SOCK_OUT
@ SOCK_HIDDEN
eNodeSocketDatatype
@ SOCK_VECTOR
@ SOCK_FLOAT
@ SOCK_RGBA
#define RE_PASSNAME_COMBINED
#define RE_PASSNAME_UV
#define RE_PASSNAME_DIFFUSE_INDIRECT
#define RE_PASSNAME_INDEXMA
#define RE_PASSNAME_SUBSURFACE_DIRECT
#define RE_PASSNAME_NORMAL
#define RE_PASSNAME_TRANSM_DIRECT
#define RE_PASSNAME_VECTOR
#define RE_PASSNAME_TRANSM_COLOR
#define RE_PASSNAME_EMIT
#define RE_PASSNAME_SUBSURFACE_INDIRECT
#define RE_PASSNAME_GLOSSY_COLOR
#define RE_PASSNAME_SUBSURFACE_COLOR
#define RE_PASSNAME_TRANSM_INDIRECT
#define RE_PASSNAME_GLOSSY_DIRECT
#define RE_PASSNAME_SHADOW
#define RE_PASSNAME_MIST
#define RE_PASSNAME_ENVIRONMENT
#define RE_PASSNAME_POSITION
#define RE_PASSNAME_DIFFUSE_COLOR
#define RE_PASSNAME_GLOSSY_INDIRECT
#define RE_PASSNAME_AO
#define RE_PASSNAME_DEPRECATED
@ R_EDGE_FRS
#define RE_PASSNAME_Z
#define RE_PASSNAME_FREESTYLE
#define RE_PASSNAME_GREASE_PENCIL
#define RE_PASSNAME_DIFFUSE_DIRECT
#define RE_PASSNAME_INDEXOB
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2])
void GPU_shader_unbind()
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_NONE
Definition RNA_types.hh:221
#define C
Definition RandGen.cpp:29
void uiTemplateID(uiLayout *layout, const bContext *C, PointerRNA *ptr, blender::StringRefNull propname, const char *newop, const char *openop, const char *unlinkop, int filter=UI_TEMPLATE_ID_FILTER_ALL, bool live_icon=false, std::optional< blender::StringRef > text=std::nullopt)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
#define UI_ITEM_NONE
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
BMesh const char void * data
constexpr const char * data() const
Result get(Context &context, Image *image, const ImageUser *image_user, const char *pass_name)
virtual void populate_meta_data_for_pass(const Scene *scene, int view_layer_id, const char *pass_name, MetaData &meta_data) const
GPUShader * get_shader(const char *info_name, ResultPrecision precision)
virtual Result get_pass(const Scene *scene, int view_layer, const char *pass_name)=0
virtual rcti get_compositing_region() const =0
virtual void set_info_message(StringRef message) const =0
bool should_compute_output(StringRef identifier)
NodeOperation(Context &context, DNode node)
Result & get_result(StringRef identifier)
Definition operation.cc:39
static ResultPrecision precision(eGPUTextureFormat format)
Definition result.cc:166
void unbind_as_texture() const
Definition result.cc:389
static ResultType type(eGPUTextureFormat format)
Definition result.cc:194
void bind_as_texture(GPUShader *shader, const char *texture_name) const
Definition result.cc:365
bool is_allocated() const
Definition result.cc:627
const char * get_shader_name(const Result &pass, const Result &result)
#define offsetof(t, d)
uint col
#define output
#define MAX_ID_NAME
void RE_engine_update_render_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer, update_render_passes_cb_t callback, void *callback_data)
RenderEngineType * RE_engines_find(const char *idname)
RenderEngine * RE_engine_create(RenderEngineType *type)
void RE_engine_free(RenderEngine *engine)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
void node_remove_socket(bNodeTree &ntree, bNode &node, bNodeSocket &sock)
Definition node.cc:3575
void node_register_type(bNodeType &ntype)
Definition node.cc:2748
void node_set_socket_availability(bNodeTree &ntree, bNodeSocket &sock, bool is_available)
Definition node.cc:5011
void node_type_socket_templates(bNodeType *ntype, bNodeSocketTemplate *inputs, bNodeSocketTemplate *outputs)
Definition node.cc:5541
bNodeSocket * node_add_static_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out, int type, int subtype, StringRefNull identifier, StringRefNull name)
Definition node.cc:3529
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:5603
void node_type_size_preset(bNodeType &ntype, eNodeSizePreset size)
Definition node.cc:5585
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:170
void extract_alpha(Context &context, Result &input, Result &output)
void parallel_for(const int2 range, const Function &function)
bool is_socket_available(const bNodeSocket *socket)
Definition utilities.cc:25
static void node_composit_copy_image(bNodeTree *, bNode *dest_node, const bNode *src_node)
static void cmp_node_image_update(bNodeTree *ntree, bNode *node)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static void node_composit_init_image(bNodeTree *ntree, bNode *node)
static void cmp_node_rlayers_update(bNodeTree *ntree, bNode *node)
static bool node_composit_poll_rlayers(const blender::bke::bNodeType *, const bNodeTree *ntree, const char **r_disabled_hint)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void node_composit_copy_rlayers(bNodeTree *, bNode *dest_node, const bNode *src_node)
static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
VecBase< int32_t, 2 > int2
static void cmp_node_image_create_outputs(bNodeTree *ntree, bNode *node, LinkNodePair *available_sockets)
static void register_node_type_cmp_rlayers()
static eNodeSocketDatatype socket_type_from_pass(const RenderPass *pass)
static blender::bke::bNodeSocketTemplate cmp_node_rlayers_out[]
static const char * cmp_node_new_pass_name(const char *name)
void node_cmp_rlayers_register_pass(bNodeTree *ntree, bNode *node, Scene *scene, ViewLayer *view_layer, const char *name, eNodeSocketDatatype type)
static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, bNode *node, LinkNodePair *available_sockets)
static void register_node_type_cmp_image()
static void cmp_node_image_add_pass_output(bNodeTree *ntree, bNode *node, const char *name, const char *passname, int rres_index, eNodeSocketDatatype type, int, LinkNodePair *available_sockets, int *prev_index)
void node_cmp_rlayers_outputs(bNodeTree *ntree, bNode *node)
static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rlayer)
#define NUM_LEGACY_SOCKETS
const char * node_cmp_rlayers_sock_to_pass(int sock_index)
static void cmp_node_rlayer_create_outputs_cb(void *userdata, Scene *scene, ViewLayer *view_layer, const char *name, int, const char *, eNodeSocketDatatype type)
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void cmp_node_update_default(bNodeTree *, bNode *node)
bNodeSocket * node_add_socket_from_template(bNodeTree *ntree, bNode *node, bke::bNodeSocketTemplate *stemp, eNodeSocketInOut in_out)
void node_image_label(const bNodeTree *, const bNode *node, char *label, int label_maxncpy)
Definition node_util.cc:189
bool RNA_property_enum_identifier(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **r_identifier)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
Definition DNA_ID.h:404
void * next
Definition DNA_ID.h:407
struct RenderResult * rr
LinkNode * list
void * first
LinkNodePair * available_sockets
char engine[32]
void(* update_render_passes)(struct RenderEngine *engine, struct Scene *scene, struct ViewLayer *view_layer)
Definition RE_engine.h:111
ListBase passes
Definition RE_pipeline.h:89
char chan_id[8]
Definition RE_pipeline.h:51
ListBase layers
struct bNodeTree * nodetree
struct RenderData r
ListBase view_layers
struct FreestyleConfig freestyle_config
int grease_pencil_flags
struct bNodeSocket * next
char identifier[64]
char idname[64]
ListBase links
int16_t custom1
struct ID * id
char name[64]
bNodeRuntimeHandle * runtime
void * storage
ListBase outputs
Compact definition of a node socket.
Definition BKE_node.hh:98
Defines a node type.
Definition BKE_node.hh:226
std::string ui_description
Definition BKE_node.hh:232
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:336
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
void(* labelfunc)(const bNodeTree *ntree, const bNode *node, char *label, int label_maxncpy)
Definition BKE_node.hh:258
const char * compositor_unsupported_message
Definition BKE_node.hh:341
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
bool(* poll)(const bNodeType *ntype, const bNodeTree *nodetree, const char **r_disabled_hint)
Definition BKE_node.hh:309
void(* updatefunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:269
void(* initfunc_api)(const bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:290
int ymin
int xmin
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
uiLayout & column(bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4226