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