Blender V4.5
node_shader_preview.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
24
25#include "BLI_listbase.h"
26#include "BLI_string.h"
27
28#include "DNA_camera_types.h"
29#include "DNA_material_types.h"
30#include "DNA_world_types.h"
31
32#include "RNA_access.hh"
33#include "RNA_prototypes.hh"
34
35#include "BKE_colortools.hh"
38#include "BKE_context.hh"
39#include "BKE_global.hh"
40#include "BKE_layer.hh"
41#include "BKE_lib_id.hh"
42#include "BKE_main.hh"
43#include "BKE_material.hh"
44#include "BKE_node.hh"
46#include "BKE_node_runtime.hh"
48
49#include "DEG_depsgraph.hh"
50
51#include "IMB_imbuf.hh"
52
53#include "WM_api.hh"
54
55#include "ED_node_preview.hh"
56#include "ED_render.hh"
57#include "ED_screen.hh"
58#include "node_intern.hh"
59
61/* -------------------------------------------------------------------- */
64using NodeSocketPair = std::pair<bNode *, bNodeSocket *>;
65
69 /* Pointer to the job's stop variable which is used to know when the job is asked for finishing.
70 * The idea is that the renderer will read this value frequently and abort the render if it is
71 * true. */
72 bool *stop;
73 /* Pointer to the job's update variable which is set to true to refresh the UI when the renderer
74 * is delivering a fresh result. It allows the job to give some UI refresh tags to the WM. */
75 bool *do_update;
76
81 /* TreePath used to locate the nodetree.
82 * bNodeTreePath elements have some listbase pointers which should not be used. */
86
89
91};
92
94
95/* -------------------------------------------------------------------- */
98
99static void ensure_nodetree_previews(const bContext &C,
100 NestedTreePreviews &tree_previews,
101 Material &material,
102 ListBase &treepath);
103
104static std::optional<ComputeContextHash> get_compute_context_hash_for_node_editor(
105 const SpaceNode &snode)
106{
108 LISTBASE_FOREACH (const bNodeTreePath *, item, &snode.treepath) {
109 treepath.append(item);
110 }
111
112 if (treepath.is_empty()) {
113 return std::nullopt;
114 }
115 if (treepath.size() == 1) {
116 /* Top group. */
118 hash.v1 = hash.v2 = 0;
119 return hash;
120 }
121 bke::ComputeContextCache compute_context_cache;
122 const ComputeContext *compute_context = nullptr;
123 for (const int i : treepath.index_range().drop_back(1)) {
124 /* The tree path contains the name of the node but not its ID. */
125 bNodeTree *tree = treepath[i]->nodetree;
126 const bNode *node = bke::node_find_node_by_name(*tree, treepath[i + 1]->node_name);
127 if (node == nullptr) {
128 /* The current tree path is invalid, probably because some parent group node has been
129 * deleted. */
130 return std::nullopt;
131 }
132 compute_context = &compute_context_cache.for_group_node(
133 compute_context, node->identifier, tree);
134 }
135 return compute_context->hash();
136}
137
139{
140 if (snode.id == nullptr || GS(snode.id->name) != ID_MA) {
141 return nullptr;
142 }
143 NestedTreePreviews *tree_previews = nullptr;
145 tree_previews = snode.runtime->tree_previews_per_context
146 .lookup_or_add_cb(*hash,
147 [&]() {
148 return std::make_unique<NestedTreePreviews>(
149 U.node_preview_res);
150 })
151 .get();
152 Material *ma = reinterpret_cast<Material *>(snode.id);
153 ensure_nodetree_previews(C, *tree_previews, *ma, snode.treepath);
154 }
155 return tree_previews;
156}
157
159
160/* -------------------------------------------------------------------- */
163
165{
166 Material *ma_copy = reinterpret_cast<Material *>(
167 BKE_id_copy_ex(nullptr,
168 &mat.id,
169 nullptr,
171 return ma_copy;
172}
173
174static Scene *preview_prepare_scene(const Main *bmain,
175 const Scene *scene_orig,
176 Main *pr_main,
177 Material *mat_copy,
178 ePreviewType preview_type)
179{
180 Scene *scene_preview;
181
182 memcpy(pr_main->filepath, BKE_main_blendfile_path(bmain), sizeof(pr_main->filepath));
183
184 if (pr_main == nullptr) {
185 return nullptr;
186 }
187 scene_preview = static_cast<Scene *>(pr_main->scenes.first);
188 if (scene_preview == nullptr) {
189 return nullptr;
190 }
191
192 ViewLayer *view_layer = static_cast<ViewLayer *>(scene_preview->view_layers.first);
193
194 /* Only enable the combined render-pass. */
195 view_layer->passflag = SCE_PASS_COMBINED;
196 view_layer->eevee.render_passes = 0;
197
198 /* This flag tells render to not execute depsgraph or F-Curves etc. */
199 scene_preview->r.scemode |= R_BUTS_PREVIEW;
200 scene_preview->r.mode |= R_PERSISTENT_DATA;
201 STRNCPY(scene_preview->r.engine, scene_orig->r.engine);
202
203 scene_preview->r.color_mgt_flag = scene_orig->r.color_mgt_flag;
205 &scene_orig->display_settings);
206
209
210 scene_preview->r.alphamode = R_ADDSKY;
211
212 scene_preview->r.cfra = scene_orig->r.cfra;
213
214 /* Setup the world. */
215 scene_preview->world = ED_preview_prepare_world(
216 pr_main, scene_preview, scene_orig->world, ID_MA, PR_BUTS_RENDER);
217
218 BLI_addtail(&pr_main->materials, mat_copy);
219 scene_preview->world->use_nodes = false;
220 scene_preview->world->horr = 0.05f;
221 scene_preview->world->horg = 0.05f;
222 scene_preview->world->horb = 0.05f;
223
224 ED_preview_set_visibility(pr_main, scene_preview, view_layer, preview_type, PR_BUTS_RENDER);
225
226 BKE_view_layer_synced_ensure(scene_preview, view_layer);
228 if (base->object->id.name[2] == 'p') {
229 if (OB_TYPE_SUPPORT_MATERIAL(base->object->type)) {
230 /* Don't use BKE_object_material_assign, it changed mat->id.us, which shows in the UI. */
231 Material ***matar = BKE_object_material_array_p(base->object);
232 int actcol = max_ii(base->object->actcol - 1, 0);
233
234 if (matar && actcol < base->object->totcol) {
235 (*matar)[actcol] = mat_copy;
236 }
237 }
238 else if (base->object->type == OB_LAMP) {
240 }
241 }
242 }
243
244 return scene_preview;
245}
246
248
249/* -------------------------------------------------------------------- */
252
259{
260 bNodeSocket *socket = get_main_socket(ntree, node, SOCK_OUT);
261 if (socket == nullptr) {
262 socket = get_main_socket(ntree, node, SOCK_IN);
263 if (socket != nullptr && socket->link == nullptr) {
264 if (!ELEM(socket->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA)) {
265 /* We can not preview a socket with no link and no manual value. */
266 return nullptr;
267 }
268 }
269 }
270 return socket;
271}
272
273static bool socket_use_aov(const bNodeSocket *socket)
274{
275 return socket == nullptr || socket->type != SOCK_SHADER;
276}
277
278static bool node_use_aov(bNodeTree &ntree, const bNode *node)
279{
280 bNode *node_preview = const_cast<bNode *>(node);
281 bNodeSocket *socket_preview = node_find_preview_socket(ntree, *node_preview);
282 return socket_use_aov(socket_preview);
283}
284
286 const char *layer_name,
287 const char *pass_name)
288{
289 RenderLayer *rl;
290 if (layer_name) {
291 rl = RE_GetRenderLayer(&rr, layer_name);
292 }
293 else {
294 rl = static_cast<RenderLayer *>(rr.layers.first);
295 }
296 if (rl == nullptr) {
297 return nullptr;
298 }
299 RenderPass *rp;
300 if (pass_name) {
301 rp = RE_pass_find_by_name(rl, pass_name, nullptr);
302 }
303 else {
304 rp = static_cast<RenderPass *>(rl->passes.first);
305 }
306 ImBuf *ibuf = rp ? rp->ibuf : nullptr;
307 return ibuf;
308}
309
311 NestedTreePreviews &tree_previews,
312 const bNode &node)
313{
314 if (tree_previews.previews_render == nullptr) {
315 return nullptr;
316 }
317
319 ImBuf *&image_cached = tree_previews.previews_map.lookup_or_add(node.identifier, nullptr);
320 if (rr == nullptr) {
321 return image_cached;
322 }
323 if (image_cached == nullptr) {
324 if (tree_previews.rendering == false) {
325 ntree.runtime->previews_refresh_state++;
326 }
327 else {
328 /* When the render process is started, the user must see that the preview area is open. */
329 ImBuf *image_latest = nullptr;
330 if (node_use_aov(ntree, &node)) {
331 image_latest = get_image_from_viewlayer_and_pass(*rr, nullptr, node.name);
332 }
333 else {
334 image_latest = get_image_from_viewlayer_and_pass(*rr, node.name, nullptr);
335 }
336 if (image_latest) {
337 IMB_refImBuf(image_latest);
338 image_cached = image_latest;
339 }
340 }
341 }
342 return image_cached;
343}
344
346{
347 if (tree_previews.previews_render == nullptr) {
348 return;
349 }
350 RE_ReleaseResult(tree_previews.previews_render);
351}
352
360 bNode &nested_node,
361 bNodeSocket &nested_socket,
362 bNode &final_node,
363 bNodeSocket &final_socket,
364 const char *route_name)
365{
366 bNode *nested_node_iter = &nested_node;
367 bNodeSocket *nested_socket_iter = &nested_socket;
368 for (int i = treepath.size() - 1; i > 0; --i) {
369 bNodeTreePath *path = treepath[i];
370 bNodeTreePath *path_prev = treepath[i - 1];
371 bNodeTree *nested_nt = path->nodetree;
372 bNode *output_node = nullptr;
373 for (bNode *iter_node : nested_nt->all_nodes()) {
374 if (iter_node->is_group_output() && iter_node->flag & NODE_DO_OUTPUT) {
375 output_node = iter_node;
376 break;
377 }
378 }
379 if (output_node == nullptr) {
380 output_node = bke::node_add_static_node(nullptr, *nested_nt, NODE_GROUP_OUTPUT);
381 output_node->flag |= NODE_DO_OUTPUT;
382 }
383
384 nested_nt->tree_interface.add_socket(
385 route_name, "", nested_socket_iter->idname, NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
386 BKE_ntree_update_after_single_tree_change(*G.pr_main, *nested_nt);
388 route_name);
389
391 *nested_nt, *nested_node_iter, *nested_socket_iter, *output_node, *out_socket);
392 BKE_ntree_update_after_single_tree_change(*G.pr_main, *nested_nt);
393
394 /* Change the `nested_node` pointer to the nested node-group instance node. The tree path
395 * contains the name of the instance node but not its ID. */
396 nested_node_iter = bke::node_find_node_by_name(*path_prev->nodetree, path->node_name);
397
398 /* Update the sockets of the node because we added a new interface. */
399 BKE_ntree_update_tag_node_property(path_prev->nodetree, nested_node_iter);
401
402 /* Now use the newly created socket of the node-group as previewing socket of the node-group
403 * instance node. */
404 nested_socket_iter = blender::bke::node_find_enabled_output_socket(*nested_node_iter,
405 route_name);
406 }
407
408 bke::node_add_link(*treepath.first()->nodetree,
409 *nested_node_iter,
410 *nested_socket_iter,
411 final_node,
412 final_socket);
413}
414
415/* Connect the node to the output of the first nodetree from `treepath`. Last element of `treepath`
416 * should be the path to the node's nodetree */
418 NodeSocketPair nodesocket,
419 bNode &output_node)
420{
421 bNodeSocket *out_surface_socket = nullptr;
422 bNodeTree *main_nt = treepath.first()->nodetree;
423 bNode *node_preview = nodesocket.first;
424 bNodeSocket *socket_preview = nodesocket.second;
425 if (socket_preview == nullptr) {
426 return;
427 }
428 if (socket_preview->in_out == SOCK_IN) {
429 BLI_assert(socket_preview->link != nullptr);
430 node_preview = socket_preview->link->fromnode;
431 socket_preview = socket_preview->link->fromsock;
432 }
433 /* Ensure output is usable. */
434 out_surface_socket = bke::node_find_socket(output_node, SOCK_IN, "Surface");
435 if (out_surface_socket->link) {
436 /* Make sure no node is already wired to the output before wiring. */
437 bke::node_remove_link(main_nt, *out_surface_socket->link);
438 }
439
441 *node_preview,
442 *socket_preview,
443 output_node,
444 *out_surface_socket,
445 nodesocket.first->name);
447}
448
449/* Connect the nodes to some aov nodes located in the first nodetree from `treepath`. Last element
450 * of `treepath` should be the path to the nodes nodetree. */
452 const Span<NodeSocketPair> nodesocket_span)
453{
454 if (nodesocket_span.is_empty()) {
455 return;
456 }
457 bNodeTree *main_nt = treepath.first()->nodetree;
458 bNodeTree *active_nt = treepath.last()->nodetree;
459 for (NodeSocketPair nodesocket : nodesocket_span) {
460 bNode *node_preview = nodesocket.first;
461 bNodeSocket *socket_preview = nodesocket.second;
462
463 bNode *aov_node = bke::node_add_static_node(nullptr, *main_nt, SH_NODE_OUTPUT_AOV);
464 STRNCPY(reinterpret_cast<NodeShaderOutputAOV *>(aov_node->storage)->name,
465 nodesocket.first->name);
466 if (socket_preview == nullptr) {
467 continue;
468 }
469 bNodeSocket *aov_socket = bke::node_find_socket(*aov_node, SOCK_IN, "Color");
470 if (socket_preview->in_out == SOCK_IN) {
471 if (socket_preview->link == nullptr) {
472 /* Copy the custom value of the socket directly to the AOV node.
473 * If the socket does not support custom values, it will just render black. */
474 float vec[4] = {0., 0., 0., 1.};
476 switch (socket_preview->type) {
477 case SOCK_FLOAT:
478 ptr = RNA_pointer_create_discrete((ID *)active_nt, &RNA_NodeSocket, socket_preview);
479 vec[0] = RNA_float_get(&ptr, "default_value");
480 vec[1] = vec[0];
481 vec[2] = vec[0];
482 break;
483 case SOCK_VECTOR:
484 case SOCK_RGBA:
485 ptr = RNA_pointer_create_discrete((ID *)active_nt, &RNA_NodeSocket, socket_preview);
486 RNA_float_get_array(&ptr, "default_value", vec);
487 break;
488 }
489 ptr = RNA_pointer_create_discrete((ID *)active_nt, &RNA_NodeSocket, aov_socket);
490 RNA_float_set_array(&ptr, "default_value", vec);
491 continue;
492 }
493 node_preview = socket_preview->link->fromnode;
494 socket_preview = socket_preview->link->fromsock;
495 }
497 treepath, *node_preview, *socket_preview, *aov_node, *aov_socket, nodesocket.first->name);
498 }
500}
501
502/* Called by renderer, checks job stops. */
503static bool nodetree_previews_break(void *spv)
504{
505 ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(spv);
506
507 return *(job_data->stop);
508}
509
510static bool prepare_viewlayer_update(void *pvl_data, ViewLayer *vl, Depsgraph *depsgraph)
511{
512 NodeSocketPair nodesocket = {nullptr, nullptr};
513 ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(pvl_data);
514 for (NodeSocketPair nodesocket_iter : job_data->shader_nodes) {
515 if (STREQ(vl->name, nodesocket_iter.first->name)) {
516 nodesocket = nodesocket_iter;
517 job_data->rendering_node = nodesocket_iter.first;
518 job_data->rendering_AOVs = false;
519 break;
520 }
521 }
522 if (nodesocket.first == nullptr) {
523 job_data->rendering_node = nullptr;
524 job_data->rendering_AOVs = true;
525 /* The AOV layer is the default `ViewLayer` of the scene(which should be the first one). */
526 return job_data->AOV_nodes.size() > 0 && !vl->prev;
527 }
528
529 bNodeSocket *displacement_socket = bke::node_find_socket(
530 *job_data->mat_output_copy, SOCK_IN, "Displacement");
531 if (job_data->mat_displacement_copy.first != nullptr && displacement_socket->link == nullptr) {
533 *job_data->mat_displacement_copy.first,
534 *job_data->mat_displacement_copy.second,
535 *job_data->mat_output_copy,
536 *displacement_socket);
537 }
538 connect_node_to_surface_output(job_data->treepath_copy, nodesocket, *job_data->mat_output_copy);
539
540 if (depsgraph != nullptr) {
541 /* Used to refresh the dependency graph so that the material can be updated. */
542 for (bNodeTreePath *path_iter : job_data->treepath_copy) {
544 G.pr_main, depsgraph, &path_iter->nodetree->id, ID_RECALC_NTREE_OUTPUT);
545 }
546 }
547 return true;
548}
549
550/* Called by renderer, refresh the UI. */
551static void all_nodes_preview_update(void *npv, RenderResult *rr, rcti * /*rect*/)
552{
553 ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(npv);
554 *job_data->do_update = true;
555 if (bNode *node = job_data->rendering_node) {
556 ImBuf *&image_cached = job_data->tree_previews->previews_map.lookup_or_add(node->identifier,
557 nullptr);
558 ImBuf *image_latest = get_image_from_viewlayer_and_pass(*rr, node->name, nullptr);
559 if (image_latest == nullptr) {
560 return;
561 }
562 if (image_cached != image_latest) {
563 if (image_cached != nullptr) {
564 IMB_freeImBuf(image_cached);
565 }
566 IMB_refImBuf(image_latest);
567 image_cached = image_latest;
568 }
569 }
570 if (job_data->rendering_AOVs) {
571 for (NodeSocketPair nodesocket_iter : job_data->AOV_nodes) {
572 ImBuf *&image_cached = job_data->tree_previews->previews_map.lookup_or_add(
573 nodesocket_iter.first->identifier, nullptr);
575 *rr, nullptr, nodesocket_iter.first->name);
576 if (image_latest == nullptr) {
577 continue;
578 }
579 if (image_cached != image_latest) {
580 if (image_cached != nullptr) {
581 IMB_freeImBuf(image_cached);
582 }
583 IMB_refImBuf(image_latest);
584 image_cached = image_latest;
585 }
586 }
587 }
588}
589
591{
592 /* Get the stuff from the builtin preview dbase. */
594 job_data.bmain, job_data.scene, G.pr_main, job_data.mat_copy, job_data.preview_type);
595 if (scene == nullptr) {
596 return;
597 }
598 Span<bNodeTreePath *> treepath = job_data.treepath_copy;
599
600 /* AOV nodes are rendered in the first RenderLayer so we route them now. */
601 connect_nodes_to_aovs(treepath, job_data.AOV_nodes);
602
603 /* Create the AOV passes for the viewlayer. */
604 ViewLayer *AOV_layer = static_cast<ViewLayer *>(scene->view_layers.first);
605 for (NodeSocketPair nodesocket_iter : job_data.shader_nodes) {
607 scene, nodesocket_iter.first->name, AOV_layer, VIEWLAYER_ADD_COPY);
608 STRNCPY(vl->name, nodesocket_iter.first->name);
609 }
610 for (NodeSocketPair nodesocket_iter : job_data.AOV_nodes) {
611 ViewLayerAOV *aov = BKE_view_layer_add_aov(AOV_layer);
612 STRNCPY(aov->name, nodesocket_iter.first->name);
613 }
614 scene->r.xsch = job_data.tree_previews->preview_size;
615 scene->r.ysch = job_data.tree_previews->preview_size;
616 scene->r.size = 100;
617
618 if (job_data.tree_previews->previews_render == nullptr) {
619 char name[32];
620 SNPRINTF(name, "Preview %p", &job_data.tree_previews);
622 }
623 Render *re = job_data.tree_previews->previews_render;
624
625 /* `sce->r` gets copied in RE_InitState. */
627 scene->r.scemode &= ~R_NO_IMAGE_LOAD;
628
630
634
635 /* Lens adjust. */
636 float oldlens = reinterpret_cast<Camera *>(scene->camera->data)->lens;
637
638 RE_ClearResult(re);
639 RE_PreviewRender(re, G.pr_main, scene);
640
641 reinterpret_cast<Camera *>(scene->camera->data)->lens = oldlens;
642
643 /* Free the aov layers and the layers generated for each node. */
644 BLI_freelistN(&AOV_layer->aovs);
645 ViewLayer *vl = AOV_layer->next;
646 while (vl) {
647 ViewLayer *vl_rem = vl;
648 vl = vl->next;
649 BLI_remlink(&scene->view_layers, vl_rem);
650 BKE_view_layer_free(vl_rem);
651 }
652}
653
655
656/* -------------------------------------------------------------------- */
659
660static void update_needed_flag(NestedTreePreviews &tree_previews,
661 const bNodeTree &nt,
662 ePreviewType preview_type)
663{
664 if (tree_previews.rendering) {
665 if (nt.runtime->previews_refresh_state != tree_previews.rendering_previews_refresh_state) {
666 tree_previews.restart_needed = true;
667 return;
668 }
669 if (preview_type != tree_previews.rendering_preview_type) {
670 tree_previews.restart_needed = true;
671 return;
672 }
673 }
674 else {
675 if (nt.runtime->previews_refresh_state != tree_previews.cached_previews_refresh_state) {
676 tree_previews.restart_needed = true;
677 return;
678 }
679 if (preview_type != tree_previews.cached_preview_type) {
680 tree_previews.restart_needed = true;
681 return;
682 }
683 }
684 if (tree_previews.preview_size != U.node_preview_res) {
685 tree_previews.restart_needed = true;
686 return;
687 }
688}
689
690static void shader_preview_startjob(void *customdata, wmJobWorkerStatus *worker_status)
691{
692 ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(customdata);
693
694 job_data->stop = &worker_status->stop;
695 job_data->do_update = &worker_status->do_update;
696 worker_status->do_update = true;
697 bool size_changed = job_data->tree_previews->preview_size != U.node_preview_res;
698 if (size_changed) {
699 job_data->tree_previews->preview_size = U.node_preview_res;
700 }
701
702 for (bNode *node_iter : job_data->mat_copy->nodetree->all_nodes()) {
703 if (node_iter->flag & NODE_DO_OUTPUT) {
704 node_iter->flag &= ~NODE_DO_OUTPUT;
705 bNodeSocket *disp_socket = bke::node_find_socket(*node_iter, SOCK_IN, "Displacement");
706 if (disp_socket != nullptr && disp_socket->link != nullptr) {
707 job_data->mat_displacement_copy = std::make_pair(disp_socket->link->fromnode,
708 disp_socket->link->fromsock);
709 }
710 break;
711 }
712 }
713
714 /* Add a new output node used only for the previews. This is useful to keep the previously
715 * connected links (for previewing the output nodes for example). */
717 nullptr, *job_data->mat_copy->nodetree, SH_NODE_OUTPUT_MATERIAL);
718 job_data->mat_output_copy->flag |= NODE_DO_OUTPUT;
719
720 bNodeTree *active_nodetree = job_data->treepath_copy.last()->nodetree;
721 active_nodetree->ensure_topology_cache();
722 for (bNode *node : active_nodetree->all_nodes()) {
723 if (!(node->flag & NODE_PREVIEW)) {
724 /* Clear the cached preview for this node to be sure that the preview is re-rendered if
725 * needed. */
726 if (ImBuf **ibuf = job_data->tree_previews->previews_map.lookup_ptr(node->identifier)) {
727 IMB_freeImBuf(*ibuf);
728 *ibuf = nullptr;
729 }
730 continue;
731 }
732 bNodeSocket *preview_socket = node_find_preview_socket(*active_nodetree, *node);
733 if (socket_use_aov(preview_socket)) {
734 job_data->AOV_nodes.append({node, preview_socket});
735 }
736 else {
737 job_data->shader_nodes.append({node, preview_socket});
738 }
739 }
740
741 if (job_data->tree_previews->preview_size > 0) {
742 preview_render(*job_data);
743 }
744}
745
746static void shader_preview_free(void *customdata)
747{
748 ShaderNodesPreviewJob *job_data = static_cast<ShaderNodesPreviewJob *>(customdata);
749 for (bNodeTreePath *path : job_data->treepath_copy) {
750 MEM_freeN(path);
751 }
752 job_data->treepath_copy.clear();
753 job_data->tree_previews->rendering = false;
756 job_data->tree_previews->cached_preview_type = job_data->preview_type;
757 if (job_data->mat_copy != nullptr) {
758 BLI_remlink(&G.pr_main->materials, job_data->mat_copy);
759 BKE_id_free(G.pr_main, &job_data->mat_copy->id);
760 job_data->mat_copy = nullptr;
761 }
762 MEM_delete(job_data);
763}
764
766 NestedTreePreviews &tree_previews,
767 Material &material,
768 ListBase &treepath)
769{
770 Scene *scene = CTX_data_scene(&C);
772 return;
773 }
774
775 bNodeTree *displayed_nodetree = static_cast<bNodeTreePath *>(treepath.last)->nodetree;
776 ePreviewType preview_type = MA_FLAT;
777 if (CTX_wm_space_node(&C)->overlay.preview_shape == SN_OVERLAY_PREVIEW_3D) {
778 preview_type = (ePreviewType)material.pr_type;
779 }
780 update_needed_flag(tree_previews, *displayed_nodetree, preview_type);
781 if (!(tree_previews.restart_needed)) {
782 return;
783 }
784 if (tree_previews.rendering) {
786 return;
787 }
788 tree_previews.rendering = true;
789 tree_previews.restart_needed = false;
791 displayed_nodetree->runtime->previews_refresh_state;
792 tree_previews.rendering_preview_type = preview_type;
793
795
796 wmJob *wm_job = WM_jobs_get(CTX_wm_manager(&C),
799 "Shader Previews",
802 ShaderNodesPreviewJob *job_data = MEM_new<ShaderNodesPreviewJob>(__func__);
803
804 job_data->scene = scene;
805 job_data->tree_previews = &tree_previews;
806 job_data->bmain = CTX_data_main(&C);
807 job_data->mat_copy = duplicate_material(material);
808 job_data->rendering_node = nullptr;
809 job_data->rendering_AOVs = false;
810 job_data->preview_type = preview_type;
811
812 /* Update the treepath copied to fit the structure of the nodetree copied. */
813 bNodeTreePath *root_path = MEM_callocN<bNodeTreePath>(__func__);
814 root_path->nodetree = job_data->mat_copy->nodetree;
815 job_data->treepath_copy.append(root_path);
816 for (bNodeTreePath *original_path = static_cast<bNodeTreePath *>(treepath.first)->next;
817 original_path;
818 original_path = original_path->next)
819 {
821 original_path->node_name);
822 if (parent == nullptr) {
823 /* In some cases (e.g. muted nodes), there may not be an equivalent node in the copied
824 * nodetree. In that case, just skip the node. */
825 continue;
826 }
827 bNodeTreePath *new_path = MEM_callocN<bNodeTreePath>(__func__);
828 memcpy(new_path, original_path, sizeof(bNodeTreePath));
829 new_path->nodetree = reinterpret_cast<bNodeTree *>(parent->id);
830 job_data->treepath_copy.append(new_path);
831 }
832
834 WM_jobs_timer(wm_job, 0.2, NC_NODE, NC_NODE);
835 WM_jobs_callbacks(wm_job, shader_preview_startjob, nullptr, nullptr, nullptr);
836
837 WM_jobs_start(CTX_wm_manager(&C), wm_job);
838}
839
841{
842 /* This should not be called from the drawing pass, because it will result in a deadlock. */
844 snode.runtime->tree_previews_per_context.clear();
845}
846
848
849} // namespace blender::ed::space_node
void BKE_color_managed_display_settings_copy(ColorManagedDisplaySettings *new_settings, const ColorManagedDisplaySettings *settings)
void BKE_color_managed_view_settings_free(ColorManagedViewSettings *settings)
void BKE_color_managed_view_settings_copy(ColorManagedViewSettings *new_settings, const ColorManagedViewSettings *settings)
SpaceNode * CTX_wm_space_node(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
@ VIEWLAYER_ADD_COPY
Definition BKE_layer.hh:36
ViewLayer * BKE_view_layer_add(Scene *scene, const char *name, ViewLayer *view_layer_source, int type)
void BKE_view_layer_free(ViewLayer *view_layer)
ViewLayerAOV * BKE_view_layer_add_aov(ViewLayer *view_layer)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
@ LIB_ID_CREATE_LOCAL
@ LIB_ID_COPY_LOCALIZE
@ LIB_ID_COPY_NO_ANIMDATA
void BKE_id_free(Main *bmain, void *idv)
ID * BKE_id_copy_ex(Main *bmain, const ID *id, ID **new_id_p, int flag)
Definition lib_id.cc:767
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:877
General operations, lookup, etc. for materials.
Material *** BKE_object_material_array_p(Object *ob)
#define NODE_GROUP_OUTPUT
Definition BKE_node.hh:800
#define SH_NODE_OUTPUT_MATERIAL
#define SH_NODE_OUTPUT_AOV
void BKE_ntree_update_after_single_tree_change(Main &bmain, bNodeTree &modified_tree, const NodeTreeUpdateExtraParams &params={})
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
MINLINE int max_ii(int a, int b)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define ELEM(...)
#define STREQ(a, b)
void DEG_graph_id_tag_update(Main *bmain, Depsgraph *depsgraph, ID *id, unsigned int flags)
@ ID_RECALC_NTREE_OUTPUT
Definition DNA_ID.h:1063
@ ID_MA
@ BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT
@ SOCK_OUT
@ SOCK_IN
@ NODE_DO_OUTPUT
@ NODE_PREVIEW
@ SOCK_VECTOR
@ SOCK_SHADER
@ SOCK_FLOAT
@ SOCK_RGBA
@ OB_LAMP
#define OB_TYPE_SUPPORT_MATERIAL(_type)
@ R_ADDSKY
@ SCE_DISPLAY_AA_SAMPLES_8
@ R_PERSISTENT_DATA
@ R_MATNODE_PREVIEW
@ R_NO_IMAGE_LOAD
@ R_TEXNODE_PREVIEW
@ R_BUTS_PREVIEW
@ SCE_PASS_COMBINED
@ SN_OVERLAY_PREVIEW_3D
void ED_preview_ensure_dbase(bool with_gpencil)
World * ED_preview_prepare_world(Main *pr_main, const Scene *scene, const World *world, ID_Type id_type, ePreviewRenderMethod pr_method)
@ PR_BUTS_RENDER
Definition ED_render.hh:63
bool ED_check_engine_supports_preview(const Scene *scene)
void ED_preview_set_visibility(Main *pr_main, Scene *scene, ViewLayer *view_layer, ePreviewType pr_type, ePreviewRenderMethod pr_method)
void IMB_freeImBuf(ImBuf *ibuf)
void IMB_refImBuf(ImBuf *ibuf)
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_RENDER_PREVIEW
Definition WM_api.hh:1729
@ WM_JOB_EXCL_RENDER
Definition WM_api.hh:1715
#define NC_NODE
Definition WM_types.hh:391
#define U
BPy_StructRNA * depsgraph
const Value * lookup_ptr(const Key &key) const
Definition BLI_map.hh:508
Value & lookup_or_add(const Key &key, const Value &value)
Definition BLI_map.hh:588
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
const T & first() const
void clear()
const ComputeContextHash & hash() const
constexpr IndexRange drop_back(int64_t n) const
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:325
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
const GroupNodeComputeContext & for_group_node(const ComputeContext *parent, int32_t node_id, const bNodeTree *tree=nullptr)
KDTree_3d * tree
#define GS(a)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
bNode * node_find_node_by_name(bNodeTree &ntree, StringRefNull name)
Definition node.cc:3607
bNodeSocket * node_find_enabled_input_socket(bNode &node, StringRef name)
Definition node.cc:2898
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2864
void node_remove_link(bNodeTree *ntree, bNodeLink &link)
Definition node.cc:4124
bNodeSocket * node_find_enabled_output_socket(bNode &node, StringRef name)
Definition node.cc:2903
bNode * node_add_static_node(const bContext *C, bNodeTree &ntree, int type)
Definition node.cc:3804
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:4087
static bool node_use_aov(bNodeTree &ntree, const bNode *node)
static Scene * preview_prepare_scene(const Main *bmain, const Scene *scene_orig, Main *pr_main, Material *mat_copy, ePreviewType preview_type)
static void shader_preview_free(void *customdata)
static void shader_preview_startjob(void *customdata, wmJobWorkerStatus *worker_status)
static void connect_nodes_to_aovs(const Span< bNodeTreePath * > treepath, const Span< NodeSocketPair > nodesocket_span)
static void connect_nested_node_to_node(const Span< bNodeTreePath * > treepath, bNode &nested_node, bNodeSocket &nested_socket, bNode &final_node, bNodeSocket &final_socket, const char *route_name)
static bool socket_use_aov(const bNodeSocket *socket)
static Material * duplicate_material(const Material &mat)
void free_previews(wmWindowManager &wm, SpaceNode &snode)
ImBuf * node_preview_acquire_ibuf(bNodeTree &ntree, NestedTreePreviews &tree_previews, const bNode &node)
static std::optional< ComputeContextHash > get_compute_context_hash_for_node_editor(const SpaceNode &snode)
std::pair< bNode *, bNodeSocket * > NodeSocketPair
static void all_nodes_preview_update(void *npv, RenderResult *rr, rcti *)
static bool nodetree_previews_break(void *spv)
static bNodeSocket * node_find_preview_socket(bNodeTree &ntree, bNode &node)
static void connect_node_to_surface_output(const Span< bNodeTreePath * > treepath, NodeSocketPair nodesocket, bNode &output_node)
NestedTreePreviews * get_nested_previews(const bContext &C, SpaceNode &snode)
void node_release_preview_ibuf(NestedTreePreviews &tree_previews)
static void preview_render(ShaderNodesPreviewJob &job_data)
static void update_needed_flag(NestedTreePreviews &tree_previews, const bNodeTree &nt, ePreviewType preview_type)
static bool prepare_viewlayer_update(void *pvl_data, ViewLayer *vl, Depsgraph *depsgraph)
bNodeSocket * get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out)
static void ensure_nodetree_previews(const bContext &C, NestedTreePreviews &tree_previews, Material &material, ListBase &treepath)
static ImBuf * get_image_from_viewlayer_and_pass(RenderResult &rr, const char *layer_name, const char *pass_name)
#define hash
Definition noise_c.cc:154
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
void RE_PreviewRender(Render *re, Main *bmain, Scene *sce)
void RE_test_break_cb(Render *re, void *handle, bool(*f)(void *handle))
RenderPass * RE_pass_find_by_name(RenderLayer *rl, const char *name, const char *viewname)
void RE_prepare_viewlayer_cb(Render *re, void *handle, bool(*f)(void *handle, ViewLayer *vl, Depsgraph *depsgraph))
Render * RE_NewRender(const char *name)
void RE_ClearResult(Render *re)
RenderResult * RE_AcquireResultRead(Render *re)
RenderLayer * RE_GetRenderLayer(RenderResult *rr, const char *name)
void RE_ReleaseResult(Render *re)
void RE_display_update_cb(Render *re, void *handle, void(*f)(void *handle, RenderResult *rr, rcti *rect))
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
void * last
void * first
ListBase scenes
Definition BKE_main.hh:245
char filepath[1024]
Definition BKE_main.hh:155
ListBase materials
Definition BKE_main.hh:251
struct bNodeTree * nodetree
char engine[32]
ListBase passes
Definition RE_pipeline.h:89
struct ImBuf * ibuf
Definition RE_pipeline.h:60
ListBase layers
string name
Definition scene.h:117
struct SceneDisplay display
ColorManagedViewSettings view_settings
struct RenderData r
ListBase view_layers
struct Object * camera
struct World * world
ColorManagedDisplaySettings display_settings
SpaceNode_Runtime * runtime
ListBase treepath
struct ID * id
struct ViewLayerEEVEE eevee
struct ViewLayer * prev
struct ViewLayer * next
ListBase aovs
char name[64]
float horg
short use_nodes
float horb
float horr
struct bNodeLink * link
char idname[64]
struct bNodeTree * nodetree
struct bNodeTreePath * next
bNodeTreeRuntimeHandle * runtime
bNodeTreeInterface tree_interface
struct ID * id
char name[64]
void * storage
int32_t identifier
blender::Map< int32_t, ImBuf * > previews_map
Map< ComputeContextHash, std::unique_ptr< space_node::NestedTreePreviews > > tree_previews_per_context
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4227
void WM_jobs_stop_type(wmWindowManager *wm, const void *owner, eWM_JobType job_type)
Definition wm_jobs.cc:622
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:353
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:456
void WM_jobs_kill_type(wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:598
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:190
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:365
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:337