Blender V5.0
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_string_utf8.h"
18#include "BLI_utildefines.h"
19
20#include "BKE_compositor.hh"
21#include "BKE_context.hh"
22#include "BKE_global.hh"
23#include "BKE_image.hh"
24#include "BKE_lib_id.hh"
25#include "BKE_main.hh"
26#include "BKE_scene.hh"
27
29
30#include "DNA_image_types.h"
31#include "DNA_scene_types.h"
32#include "DNA_space_types.h"
33#include "DNA_vec_types.h"
34
35#include "RE_engine.h"
36#include "RE_pipeline.h"
37
38#include "RNA_access.hh"
39
40#include "UI_interface.hh"
42#include "UI_resources.hh"
43
44#include "GPU_shader.hh"
45
47
49#include "COM_node_operation.hh"
50#include "COM_utilities.hh"
51
52/* **************** IMAGE (and RenderResult, multi-layer image) ******************** */
53
55 {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
56 {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
57 {SOCK_FLOAT, N_(RE_PASSNAME_DEPTH), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
58 {SOCK_VECTOR, N_(RE_PASSNAME_NORMAL), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
59 {SOCK_VECTOR, N_(RE_PASSNAME_UV), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
60 {SOCK_VECTOR, N_(RE_PASSNAME_VECTOR), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
61 {SOCK_VECTOR, N_(RE_PASSNAME_POSITION), 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_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
64 {SOCK_RGBA, N_(RE_PASSNAME_SHADOW), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
65 {SOCK_RGBA, N_(RE_PASSNAME_AO), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
66 {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
67 {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
68 {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
69 {SOCK_FLOAT, N_(RE_PASSNAME_INDEXOB), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
70 {SOCK_FLOAT, N_(RE_PASSNAME_INDEXMA), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
71 {SOCK_FLOAT, N_(RE_PASSNAME_MIST), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
72 {SOCK_RGBA, N_(RE_PASSNAME_EMIT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
73 {SOCK_RGBA, N_(RE_PASSNAME_ENVIRONMENT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
74 {SOCK_RGBA, N_(RE_PASSNAME_DIFFUSE_DIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
75 {SOCK_RGBA, N_(RE_PASSNAME_DIFFUSE_INDIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
76 {SOCK_RGBA, N_(RE_PASSNAME_DIFFUSE_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
77 {SOCK_RGBA, N_(RE_PASSNAME_GLOSSY_DIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
78 {SOCK_RGBA, N_(RE_PASSNAME_GLOSSY_INDIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
79 {SOCK_RGBA, N_(RE_PASSNAME_GLOSSY_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
80 {SOCK_RGBA, N_(RE_PASSNAME_TRANSM_DIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
81 {SOCK_RGBA, N_(RE_PASSNAME_TRANSM_INDIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
82 {SOCK_RGBA, N_(RE_PASSNAME_TRANSM_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
83 {SOCK_RGBA, N_(RE_PASSNAME_SUBSURFACE_DIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
84 {SOCK_RGBA, N_(RE_PASSNAME_SUBSURFACE_INDIRECT), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
85 {SOCK_RGBA, N_(RE_PASSNAME_SUBSURFACE_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
86 {-1, ""},
87};
88#define NUM_LEGACY_SOCKETS (ARRAY_SIZE(cmp_node_rlayers_out) - 1)
89
90static const char *cmp_node_legacy_pass_name(const char *name)
91{
92 if (STREQ(name, "Diffuse Direct")) {
93 return "DiffDir";
94 }
95 if (STREQ(name, "Diffuse Indirect")) {
96 return "DiffInd";
97 }
98 if (STREQ(name, "Diffuse Color")) {
99 return "DiffCol";
100 }
101 if (STREQ(name, "Glossy Direct")) {
102 return "GlossDir";
103 }
104 if (STREQ(name, "Glossy Indirect")) {
105 return "GlossInd";
106 }
107 if (STREQ(name, "Glossy Color")) {
108 return "GlossCol";
109 }
110 if (STREQ(name, "Transmission Direct")) {
111 return "TransDir";
112 }
113 if (STREQ(name, "Transmission Indirect")) {
114 return "TransInd";
115 }
116 if (STREQ(name, "Transmission Color")) {
117 return "TransCol";
118 }
119 if (STREQ(name, "Volume Direct")) {
120 return "VolumeDir";
121 }
122 if (STREQ(name, "Volume Indirect")) {
123 return "VolumeInd";
124 }
125 if (STREQ(name, "Volume Color")) {
126 return "VolumeCol";
127 }
128 if (STREQ(name, "Ambient Occlusion")) {
129 return "AO";
130 }
131 if (STREQ(name, "Environment")) {
132 return "Env";
133 }
134 if (STREQ(name, "Material Index")) {
135 return "IndexMA";
136 }
137 if (STREQ(name, "Object Index")) {
138 return "IndexOB";
139 }
140 if (STREQ(name, "Grease Pencil")) {
141 return "GreasePencil";
142 }
143 if (STREQ(name, "Emission")) {
144 return "Emit";
145 }
146
147 return nullptr;
148}
149
151 bNode *node,
152 const char *name,
153 const char *passname,
154 int rres_index,
156 int /*is_rlayers*/,
157 LinkNodePair *available_sockets,
158 int *prev_index)
159{
162
163 /* Rename legacy socket names to new ones. */
164 if (sock == nullptr) {
165 const char *legacy_name = cmp_node_legacy_pass_name(name);
166 if (legacy_name) {
167 sock = (bNodeSocket *)BLI_findstring(
168 &node->outputs, legacy_name, offsetof(bNodeSocket, name));
169 if (sock) {
170 STRNCPY(sock->name, name);
171 STRNCPY(sock->identifier, name);
172 }
173 }
174 }
175
176 /* Replace if types don't match. */
177 if (sock && sock->type != type) {
178 blender::bke::node_remove_socket(*ntree, *node, *sock);
179 sock = nullptr;
180 }
181
182 /* Create socket if it doesn't exist yet. */
183 if (sock == nullptr) {
184 if (rres_index >= 0) {
186 ntree, node, &cmp_node_rlayers_out[rres_index], SOCK_OUT);
187 }
188 else {
190 *ntree, *node, SOCK_OUT, type, PROP_NONE, name, name);
191 }
192 /* extra socket info */
193 NodeImageLayer *sockdata = MEM_callocN<NodeImageLayer>(__func__);
194 sock->storage = sockdata;
195 }
196
197 NodeImageLayer *sockdata = (NodeImageLayer *)sock->storage;
198 if (sockdata) {
199 STRNCPY_UTF8(sockdata->pass_name, passname);
200 }
201
202 /* Reorder sockets according to order that passes are added. */
203 const int after_index = (*prev_index)++;
204 bNodeSocket *after_sock = (bNodeSocket *)BLI_findlink(&node->outputs, after_index);
205 BLI_remlink(&node->outputs, sock);
206 BLI_insertlinkafter(&node->outputs, after_sock, sock);
207
208 BLI_linklist_append(available_sockets, sock);
209}
210
212{
213 switch (pass->channels) {
214 case 1:
215 return SOCK_FLOAT;
216 case 2:
217 case 3:
218 if (STR_ELEM(pass->chan_id, "RGB", "rgb")) {
219 return SOCK_RGBA;
220 }
221 else {
222 return SOCK_VECTOR;
223 }
224 case 4:
225 if (STR_ELEM(pass->chan_id, "RGBA", "rgba")) {
226 return SOCK_RGBA;
227 }
228 else {
229 return SOCK_VECTOR;
230 }
231 default:
232 break;
233 }
234
236 return SOCK_FLOAT;
237}
238
240 bNode *node,
241 LinkNodePair *available_sockets)
242{
243 Image *ima = (Image *)node->id;
244 ImBuf *ibuf;
245 int prev_index = -1;
246 if (ima) {
247 ImageUser *iuser = (ImageUser *)node->storage;
248 ImageUser load_iuser = {nullptr};
249 int offset = BKE_image_sequence_guess_offset(ima);
250
251 /* It is possible that image user in this node is not
252 * properly updated yet. In this case loading image will
253 * fail and sockets detection will go wrong.
254 *
255 * So we manually construct image user to be sure first
256 * image from sequence (that one which is set as filename
257 * for image data-block) is used for sockets detection. */
258 load_iuser.framenr = offset;
259
260 /* make sure ima->type is correct */
261 ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, nullptr);
262
263 if (ima->rr) {
264 RenderLayer *rl = (RenderLayer *)BLI_findlink(&ima->rr->layers, iuser->layer);
265
266 if (rl) {
267 LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
268 const eNodeSocketDatatype type = socket_type_from_pass(rpass);
270 node,
271 rpass->name,
272 rpass->name,
273 -1,
274 type,
275 false,
276 available_sockets,
277 &prev_index);
278 /* Special handling for the Combined pass to ensure compatibility. */
279 if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
281 node,
282 "Alpha",
283 rpass->name,
284 -1,
286 false,
287 available_sockets,
288 &prev_index);
289 }
290 }
291 BKE_image_release_ibuf(ima, ibuf, nullptr);
292 return;
293 }
294 }
295 }
296
298 node,
299 "Image",
301 -1,
302 SOCK_RGBA,
303 false,
304 available_sockets,
305 &prev_index);
307 node,
308 "Alpha",
310 -1,
312 false,
313 available_sockets,
314 &prev_index);
315
316 if (ima) {
317 BKE_image_release_ibuf(ima, ibuf, nullptr);
318 }
319}
320
325
327 bNode *node,
328 Scene *scene,
329 ViewLayer *view_layer,
330 const char *name,
332{
334
335 if (scene == nullptr || view_layer == nullptr || data == nullptr || node->id != (ID *)scene) {
336 return;
337 }
338
339 ViewLayer *node_view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1);
340 if (node_view_layer != view_layer) {
341 return;
342 }
343
344 /* Special handling for the Combined pass to ensure compatibility. */
347 ntree, node, "Image", name, -1, type, true, data->available_sockets, &data->prev_index);
349 node,
350 "Alpha",
351 name,
352 -1,
354 true,
355 data->available_sockets,
356 &data->prev_index);
357 }
358 else {
360 ntree, node, name, name, -1, type, true, data->available_sockets, &data->prev_index);
361 }
362}
363
368
369static void cmp_node_rlayer_create_outputs_cb(void *userdata,
370 Scene *scene,
371 ViewLayer *view_layer,
372 const char *name,
373 int /*channels*/,
374 const char * /*chanid*/,
376{
378 node_cmp_rlayers_register_pass(&data.ntree, &data.node, scene, view_layer, name, type);
379}
380
382 bNode *node,
383 LinkNodePair *available_sockets)
384{
385 Scene *scene = (Scene *)node->id;
386
387 if (scene) {
388 RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
389 if (engine_type && engine_type->update_render_passes) {
390 ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1);
391 if (view_layer) {
392 RLayerUpdateData *data = MEM_mallocN<RLayerUpdateData>("render layer update data");
393 data->available_sockets = available_sockets;
394 data->prev_index = -1;
395 node->storage = data;
396
397 CreateOutputUserData userdata = {*ntree, *node};
398
399 RenderEngine *engine = RE_engine_create(engine_type);
401 engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, &userdata);
402 RE_engine_free(engine);
403
404 if ((scene->r.mode & R_EDGE_FRS) &&
406 {
408 ntree, node, scene, view_layer, RE_PASSNAME_FREESTYLE, SOCK_RGBA);
409 }
410
413 ntree, node, scene, view_layer, RE_PASSNAME_GREASE_PENCIL, SOCK_RGBA);
414 }
415
417 node->storage = nullptr;
418
419 return;
420 }
421 }
422 }
423
424 int prev_index = -1;
426 node,
427 "Image",
430 SOCK_RGBA,
431 true,
432 available_sockets,
433 &prev_index);
435 node,
436 "Alpha",
440 true,
441 available_sockets,
442 &prev_index);
443}
444
445/* XXX make this into a generic socket verification function for dynamic socket replacement
446 * (multi-layer, groups, static templates). */
447static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rlayer)
448{
449 bNodeSocket *sock, *sock_next;
450 LinkNodePair available_sockets = {nullptr, nullptr};
451
452 /* XXX make callback */
453 if (rlayer) {
454 cmp_node_rlayer_create_outputs(ntree, node, &available_sockets);
455 }
456 else {
457 cmp_node_image_create_outputs(ntree, node, &available_sockets);
458 }
459
460 /* Get rid of sockets whose passes are not available in the image.
461 * If sockets that are not available would be deleted, the connections to them would be lost
462 * when e.g. opening a file (since there's no render at all yet).
463 * Therefore, sockets with connected links will just be set as unavailable.
464 *
465 * Another important detail comes from compatibility with the older socket model, where there
466 * was a fixed socket per pass type that was just hidden or not. Therefore, older versions expect
467 * the first 31 passes to belong to a specific pass type.
468 * So, we keep those 31 always allocated before the others as well,
469 * even if they have no links attached. */
470 int sock_index = 0;
471 for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock_next, sock_index++) {
472 sock_next = sock->next;
473 if (BLI_linklist_index(available_sockets.list, sock) >= 0) {
475 }
476 else {
477 bNodeLink *link;
478 for (link = (bNodeLink *)ntree->links.first; link; link = link->next) {
479 if (link->fromsock == sock) {
480 break;
481 }
482 }
483 if (!link && (!rlayer || sock_index >= NUM_LEGACY_SOCKETS)) {
484 MEM_freeN(reinterpret_cast<NodeImageLayer *>(sock->storage));
485 blender::bke::node_remove_socket(*ntree, *node, *sock);
486 }
487 else {
488 blender::bke::node_set_socket_availability(*ntree, *sock, false);
489 }
490 }
491 }
492
493 BLI_linklist_free(available_sockets.list, nullptr);
494}
495
497
498static void cmp_node_image_update(bNodeTree *ntree, bNode *node)
499{
500 /* avoid unnecessary updates, only changes to the image/image user data are of interest */
501 if (node->runtime->update & NODE_UPDATE_ID) {
502 cmp_node_image_verify_outputs(ntree, node, false);
503 }
504
505 cmp_node_update_default(ntree, node);
506}
507
508static void node_composit_init_image(bNodeTree *ntree, bNode *node)
509{
510 ImageUser *iuser = MEM_callocN<ImageUser>(__func__);
511 node->storage = iuser;
512 iuser->frames = 1;
513 iuser->sfra = 1;
514 iuser->flag |= IMA_ANIM_ALWAYS;
515
516 /* setup initial outputs */
517 cmp_node_image_verify_outputs(ntree, node, false);
518}
519
521{
522 /* free extra socket info */
523 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
524 MEM_freeN(reinterpret_cast<NodeImageLayer *>(sock->storage));
525 }
526
527 MEM_freeN(reinterpret_cast<ImageUser *>(node->storage));
528}
529
530static void node_composit_copy_image(bNodeTree * /*dst_ntree*/,
531 bNode *dest_node,
532 const bNode *src_node)
533{
534 dest_node->storage = MEM_dupallocN(src_node->storage);
535
536 const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first;
537 bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first;
538 while (dest_output_sock != nullptr) {
539 dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage);
540
541 src_output_sock = src_output_sock->next;
542 dest_output_sock = dest_output_sock->next;
543 }
544}
545
546using namespace blender::compositor;
547
549 public:
551
552 void execute() override
553 {
554 for (const bNodeSocket *output : this->node()->output_sockets()) {
556 continue;
557 }
558
559 compute_output(output->identifier);
560 }
561 }
562
563 void compute_output(StringRef identifier)
564 {
565 if (!should_compute_output(identifier)) {
566 return;
567 }
568
569 const StringRef pass_name = this->get_pass_name(identifier);
570 Result cached_image = context().cache_manager().cached_images.get(
571 context(), get_image(), get_image_user(), pass_name.data());
572
573 Result &result = get_result(identifier);
574 if (!cached_image.is_allocated()) {
575 result.allocate_invalid();
576 return;
577 }
578
579 /* Alpha is not an actual pass, but one that is extracted from the combined pass. */
580 if (identifier == "Alpha" && pass_name == RE_PASSNAME_COMBINED) {
581 extract_alpha(context(), cached_image, result);
582 }
583 else {
584 result.set_type(cached_image.type());
585 result.set_precision(cached_image.precision());
586 result.wrap_external(cached_image);
587 }
588 }
589
590 /* Get the name of the pass corresponding to the output with the given identifier. */
591 const char *get_pass_name(StringRef identifier)
592 {
593 DOutputSocket output = node().output_by_identifier(identifier);
594 return static_cast<NodeImageLayer *>(output->storage)->pass_name;
595 }
596
598 {
599 return reinterpret_cast<Image *>(bnode().id);
600 }
601
603 {
604 return static_cast<ImageUser *>(bnode().storage);
605 }
606};
607
609{
610 return new ImageOperation(context, node);
611}
612
613} // namespace blender::nodes::node_composite_image_cc
614
616{
617 namespace file_ns = blender::nodes::node_composite_image_cc;
618
619 static blender::bke::bNodeType ntype;
620
621 cmp_node_type_base(&ntype, "CompositorNodeImage", CMP_NODE_IMAGE);
622 ntype.ui_name = "Image";
623 ntype.ui_description = "Input image or movie file";
624 ntype.enum_name_legacy = "IMAGE";
625 ntype.nclass = NODE_CLASS_INPUT;
626 ntype.initfunc = file_ns::node_composit_init_image;
628 ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image);
629 ntype.updatefunc = file_ns::cmp_node_image_update;
630 ntype.get_compositor_operation = file_ns::get_compositor_operation;
632 ntype.flag |= NODE_PREVIEW;
633
635}
637
638/* **************** RENDER RESULT ******************** */
639
641{
642 cmp_node_image_verify_outputs(ntree, node, true);
643}
644
645const char *node_cmp_rlayers_sock_to_pass(int sock_index)
646{
647 if (sock_index >= NUM_LEGACY_SOCKETS) {
648 return nullptr;
649 }
650 const char *name = cmp_node_rlayers_out[sock_index].name;
651 /* Exception for alpha, which is derived from Combined. */
652 return STREQ(name, "Alpha") ? RE_PASSNAME_COMBINED : name;
653}
654
656
658{
659 Scene *scene = CTX_data_scene(C);
660 bNode *node = (bNode *)ptr->data;
661 int sock_index = 0;
662
663 node->id = &scene->id;
664 id_us_plus(node->id);
665
666 for (bNodeSocket *sock = (bNodeSocket *)node->outputs.first; sock;
667 sock = sock->next, sock_index++)
668 {
669 NodeImageLayer *sockdata = MEM_callocN<NodeImageLayer>(__func__);
670 sock->storage = sockdata;
671
673 }
674}
675
677{
678 /* free extra socket info */
679 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
680 if (sock->storage) {
681 MEM_freeN(reinterpret_cast<NodeImageLayer *>(sock->storage));
682 }
683 }
684}
685
686static void node_composit_copy_rlayers(bNodeTree * /*dst_ntree*/,
687 bNode *dest_node,
688 const bNode *src_node)
689{
690 /* copy extra socket info */
691 const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first;
692 bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first;
693 while (dest_output_sock != nullptr) {
694 dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage);
695
696 src_output_sock = src_output_sock->next;
697 dest_output_sock = dest_output_sock->next;
698 }
699}
700
701static void cmp_node_rlayers_update(bNodeTree *ntree, bNode *node)
702{
703 cmp_node_image_verify_outputs(ntree, node, true);
704
705 cmp_node_update_default(ntree, node);
706}
707
709{
710 bNode *node = (bNode *)ptr->data;
711 uiLayout *col, *row;
712
713 uiTemplateID(layout, C, ptr, "scene", nullptr, nullptr, nullptr);
714
715 if (!node->id) {
716 return;
717 }
718
719 col = &layout->column(false);
720 row = &col->row(true);
721 row->prop(ptr, "layer", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
722
723 PropertyRNA *prop = RNA_struct_find_property(ptr, "layer");
724 const char *layer_name;
725 if (!RNA_property_enum_identifier(C, ptr, prop, RNA_property_enum_get(ptr, prop), &layer_name)) {
726 return;
727 }
728
729 PointerRNA scn_ptr;
730 char scene_name[MAX_ID_NAME - 2];
731 scn_ptr = RNA_pointer_get(ptr, "scene");
732 RNA_string_get(&scn_ptr, "name", scene_name);
733
734 PointerRNA op_ptr = row->op(
735 "RENDER_OT_render", "", ICON_RENDER_STILL, wm::OpCallContext::InvokeDefault, UI_ITEM_NONE);
736 RNA_string_set(&op_ptr, "layer", layer_name);
737 RNA_string_set(&op_ptr, "scene", scene_name);
738}
739
740static void node_extra_info(NodeExtraInfoParams &parameters)
741{
742 SpaceNode *space_node = CTX_wm_space_node(&parameters.C);
743 if (space_node->node_tree_sub_type != SNODE_COMPOSITOR_SCENE) {
745 row.text = RPT_("Node Unsupported");
746 row.tooltip = TIP_("The Render Layers node is only supported for scene compositing");
747 row.icon = ICON_ERROR;
748 parameters.rows.append(std::move(row));
749 }
750
751 /* EEVEE supports passes. */
752 const Scene *scene = CTX_data_scene(&parameters.C);
754 return;
755 }
756
758 return;
759 }
760
761 bool is_any_pass_used = false;
762 for (const bNodeSocket *output : parameters.node.output_sockets()) {
763 /* Combined pass is always available. */
764 if (StringRef(output->name) == "Image" || StringRef(output->name) == "Alpha") {
765 continue;
766 }
767 if (output->is_logically_linked()) {
768 is_any_pass_used = true;
769 break;
770 }
771 }
772
773 if (!is_any_pass_used) {
774 return;
775 }
776
778 row.text = RPT_("Passes Not Supported");
779 row.tooltip = TIP_("Render passes in the Viewport compositor are only supported in EEVEE");
780 row.icon = ICON_ERROR;
781 parameters.rows.append(std::move(row));
782}
783
784using namespace blender::compositor;
785
787 public:
789
790 void execute() override
791 {
792 const Scene *scene = reinterpret_cast<const Scene *>(this->bnode().id);
793 const int view_layer = this->bnode().custom1;
794
795 Result &image_result = this->get_result("Image");
796 Result &alpha_result = this->get_result("Alpha");
797
798 if (image_result.should_compute() || alpha_result.should_compute()) {
799 const Result combined_pass = this->context().get_pass(
800 scene, view_layer, RE_PASSNAME_COMBINED);
801 if (image_result.should_compute()) {
802 this->execute_pass(combined_pass, image_result);
803 }
804 if (alpha_result.should_compute()) {
805 this->execute_pass(combined_pass, alpha_result);
806 }
807 }
808
809 for (const bNodeSocket *output : this->node()->output_sockets()) {
811 continue;
812 }
813
814 if (STR_ELEM(output->identifier, "Image", "Alpha")) {
815 continue;
816 }
817
818 Result &result = this->get_result(output->identifier);
819 if (!result.should_compute()) {
820 continue;
821 }
822
823 const char *pass_name = this->get_pass_name(output->identifier);
824 this->context().populate_meta_data_for_pass(scene, view_layer, pass_name, result.meta_data);
825
826 const Result pass = this->context().get_pass(scene, view_layer, pass_name);
827 this->execute_pass(pass, result);
828 }
829 }
830
831 void execute_pass(const Result &pass, Result &result)
832 {
833 if (!pass.is_allocated()) {
834 /* Pass not rendered yet, or not supported by viewport. */
835 result.allocate_invalid();
836 return;
837 }
838
839 if (!this->context().is_valid_compositing_region()) {
840 result.allocate_invalid();
841 return;
842 }
843
844 /* Vector sockets are 3D by default, so we need to overwrite the type if the pass turned out to
845 * be 4D. */
846 if (result.type() == ResultType::Float3 && pass.type() == ResultType::Float4) {
847 result.set_type(pass.type());
848 }
849 result.set_precision(pass.precision());
850
851 if (this->context().use_gpu()) {
852 this->execute_pass_gpu(pass, result);
853 }
854 else {
855 this->execute_pass_cpu(pass, result);
856 }
857 }
858
860 {
861 gpu::Shader *shader = this->context().get_shader(this->get_shader_name(pass, result),
862 result.precision());
863 GPU_shader_bind(shader);
864
865 /* The compositing space might be limited to a subset of the pass texture, so only read that
866 * compositing region into an appropriately sized result. */
867 const int2 lower_bound = this->context().get_compositing_region().min;
868 GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
869
870 pass.bind_as_texture(shader, "input_tx");
871
872 result.allocate_texture(Domain(this->context().get_compositing_region_size()));
873 result.bind_as_image(shader, "output_img");
874
875 compute_dispatch_threads_at_least(shader, result.domain().size);
876
878 pass.unbind_as_texture();
879 result.unbind_as_image();
880 }
881
882 const char *get_shader_name(const Result &pass, const Result &result)
883 {
884 /* Special case for alpha output. */
885 if (pass.type() == ResultType::Color && result.type() == ResultType::Float) {
886 return "compositor_read_input_alpha";
887 }
888
889 switch (pass.type()) {
891 return "compositor_read_input_float";
895 return "compositor_read_input_float4";
896 case ResultType::Int:
897 case ResultType::Int2:
899 case ResultType::Bool:
900 case ResultType::Menu:
901 /* Not supported. */
902 break;
904 /* Single only types do not support GPU code path. */
907 break;
908 }
909
911 return nullptr;
912 }
913
915 {
916 /* The compositing space might be limited to a subset of the pass texture, so only read that
917 * compositing region into an appropriately sized result. */
918 const int2 lower_bound = this->context().get_compositing_region().min;
919
920 result.allocate_texture(Domain(this->context().get_compositing_region_size()));
921
922 /* Special case for alpha output. */
923 if (pass.type() == ResultType::Color && result.type() == ResultType::Float) {
924 parallel_for(result.domain().size, [&](const int2 texel) {
925 result.store_pixel(texel, pass.load_pixel<float4>(texel + lower_bound).w);
926 });
927 }
928 else {
929 parallel_for(result.domain().size, [&](const int2 texel) {
930 result.store_pixel_generic_type(texel, pass.load_pixel_generic_type(texel + lower_bound));
931 });
932 }
933 }
934
935 /* Get the name of the pass corresponding to the output with the given identifier. */
936 const char *get_pass_name(StringRef identifier)
937 {
938 DOutputSocket output = this->node().output_by_identifier(identifier);
939 return static_cast<NodeImageLayer *>(output->storage)->pass_name;
940 }
941};
942
944{
945 return new RenderLayerOperation(context, node);
946}
947
948} // namespace blender::nodes::node_composite_render_layer_cc
949
951{
953
954 static blender::bke::bNodeType ntype;
955
956 cmp_node_type_base(&ntype, "CompositorNodeRLayers", CMP_NODE_R_LAYERS);
957 ntype.ui_name = "Render Layers";
958 ntype.ui_description = "Input render passes from a scene render";
959 ntype.enum_name_legacy = "R_LAYERS";
960 ntype.nclass = NODE_CLASS_INPUT;
962 ntype.draw_buttons = file_ns::node_composit_buts_viewlayers;
963 ntype.initfunc_api = file_ns::node_composit_init_rlayers;
964 ntype.get_compositor_operation = file_ns::get_compositor_operation;
965 ntype.flag |= NODE_PREVIEW;
967 std::nullopt,
968 file_ns::node_composit_free_rlayers,
969 file_ns::node_composit_copy_rlayers);
970 ntype.updatefunc = file_ns::cmp_node_rlayers_update;
972 ntype.get_extra_info = file_ns::node_extra_info;
974
976}
SpaceNode * CTX_wm_space_node(const bContext *C)
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:358
#define NODE_CLASS_INPUT
Definition BKE_node.hh:447
#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
#define BLI_assert(a)
Definition BLI_assert.h:46
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:661
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define STRNCPY_UTF8(dst, src)
#define STREQ(a, b)
#define RPT_(msgid)
#define TIP_(msgid)
#define MAX_ID_NAME
Definition DNA_ID.h:373
@ FREESTYLE_AS_RENDER_PASS
@ IMA_ANIM_ALWAYS
@ GREASE_PENCIL_AS_SEPARATE_PASS
@ NODE_PREVIEW
@ SOCK_OUT
@ NODE_UPDATE_ID
eNodeSocketDatatype
@ SOCK_VECTOR
@ SOCK_FLOAT
@ SOCK_RGBA
#define RE_PASSNAME_COMBINED
#define RE_PASSNAME_UV
#define RE_PASSNAME_DIFFUSE_INDIRECT
#define RE_PASSNAME_INDEXMA
@ R_EDGE_FRS
#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_DEPTH
#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
#define RE_PASSNAME_FREESTYLE
#define RE_PASSNAME_GREASE_PENCIL
#define RE_PASSNAME_DIFFUSE_DIRECT
#define RE_PASSNAME_INDEXOB
@ SNODE_COMPOSITOR_SCENE
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
void GPU_shader_unbind()
void GPU_shader_uniform_2iv(blender::gpu::Shader *sh, const char *name, const int data[2])
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ PROP_NONE
Definition RNA_types.hh:233
#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
BMesh const char void * data
void append(const T &value)
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
virtual Result get_pass(const Scene *scene, int view_layer, const char *name)
virtual Bounds< int2 > get_compositing_region() const =0
gpu::Shader * get_shader(const char *info_name, ResultPrecision precision)
bool should_compute_output(StringRef identifier)
NodeOperation(Context &context, DNode node)
Result & get_result(StringRef identifier)
Definition operation.cc:39
void allocate_texture(const Domain domain, const bool from_pool=true, const std::optional< ResultStorageType > storage_type=std::nullopt)
Definition result.cc:389
void unbind_as_texture() const
Definition result.cc:511
bool is_allocated() const
Definition result.cc:763
static bool is_single_value_only_type(ResultType type)
Definition result.cc:44
static ResultType type(blender::gpu::TextureFormat format)
Definition result.cc:261
void bind_as_texture(gpu::Shader *shader, const char *texture_name) const
Definition result.cc:487
static ResultPrecision precision(blender::gpu::TextureFormat format)
Definition result.cc:233
const char * get_shader_name(const Result &pass, const Result &result)
#define offsetof(t, d)
uint col
#define output
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
bool is_viewport_compositor_used(const bContext &context)
void node_remove_socket(bNodeTree &ntree, bNode &node, bNodeSocket &sock)
Definition node.cc:3243
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
void node_set_socket_availability(bNodeTree &ntree, bNodeSocket &sock, bool is_available)
Definition node.cc:4739
void node_type_socket_templates(bNodeType *ntype, bNodeSocketTemplate *inputs, bNodeSocketTemplate *outputs)
Definition node.cc:5352
bNodeSocket * node_add_static_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out, int type, int subtype, StringRefNull identifier, StringRefNull name)
Definition node.cc:3197
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:5414
void node_type_size_preset(bNodeType &ntype, eNodeSizePreset size)
Definition node.cc:5396
void compute_dispatch_threads_at_least(gpu::Shader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:196
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:27
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 node_extra_info(NodeExtraInfoParams &parameters)
static void cmp_node_rlayers_update(bNodeTree *ntree, bNode *node)
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_legacy_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
const char * name
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)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
const char * RE_engine_id_BLENDER_EEVEE
Definition scene.cc:1580
Definition DNA_ID.h:414
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:110
ListBase passes
Definition RE_pipeline.h:94
char chan_id[24]
Definition RE_pipeline.h:56
ListBase layers
struct RenderData r
ListBase view_layers
char node_tree_sub_type
struct FreestyleConfig freestyle_config
int grease_pencil_flags
struct bNodeSocket * next
char identifier[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:99
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
NodeGetCompositorOperationFunction get_compositor_operation
Definition BKE_node.hh:348
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
void(* labelfunc)(const bNodeTree *ntree, const bNode *node, char *label, int label_maxncpy)
Definition BKE_node.hh:270
NodeExtraInfoFunction get_extra_info
Definition BKE_node.hh:381
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
void(* updatefunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:281
void(* initfunc_api)(const bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:302
Vector< NodeExtraInfoRow > & rows
uiLayout & column(bool align)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
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:4238