Blender V5.0
svm.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include <algorithm>
6
7#include "device/device.h"
8
9#include "scene/background.h"
10#include "scene/light.h"
11#include "scene/mesh.h"
12#include "scene/scene.h"
13#include "scene/shader.h"
14#include "scene/shader_graph.h"
15#include "scene/shader_nodes.h"
16#include "scene/stats.h"
17#include "scene/svm.h"
18
19#include "util/log.h"
20#include "util/progress.h"
21#include "util/task.h"
22
24
25/* Shader Manager */
26
28
30
32 Shader *shader,
33 Progress &progress,
34 array<int4> *svm_nodes)
35{
36 if (progress.get_cancel()) {
37 return;
38 }
39 assert(shader->graph);
40
42 SVMCompiler compiler(scene);
43 compiler.background = (shader == scene->background->get_shader(scene));
44 compiler.compile(shader, *svm_nodes, 0, &summary);
45
46 LOG_DEBUG << "Compilation summary:\n"
47 << "Shader name: " << shader->name << "\n"
48 << summary.full_report();
49}
50
52 DeviceScene *dscene,
53 Scene *scene,
54 Progress &progress)
55{
56 if (!need_update()) {
57 return;
58 }
59
60 const scoped_callback_timer timer([scene](double time) {
61 if (scene->update_stats) {
62 scene->update_stats->svm.times.add_entry({"device_update", time});
63 }
64 });
65
66 const int num_shaders = scene->shaders.size();
67
68 LOG_INFO << "Total " << num_shaders << " shaders.";
69
70 const double start_time = time_dt();
71
72 /* test if we need to update */
73 device_free(device, dscene, scene);
74
75 /* Build all shaders. */
77 vector<array<int4>> shader_svm_nodes(num_shaders);
78 for (int i = 0; i < num_shaders; i++) {
79 task_pool.push([this, scene, &progress, &shader_svm_nodes, i] {
80 device_update_shader(scene, scene->shaders[i], progress, &shader_svm_nodes[i]);
81 });
82 }
83 task_pool.wait_work();
84
85 if (progress.get_cancel()) {
86 return;
87 }
88
89 /* The global node list contains a jump table (one node per shader)
90 * followed by the nodes of all shaders. */
91 int svm_nodes_size = num_shaders;
92 for (int i = 0; i < num_shaders; i++) {
93 /* Since we're not copying the local jump node, the size ends up being one node lower. */
94 svm_nodes_size += shader_svm_nodes[i].size() - 1;
95 }
96
97 int4 *svm_nodes = dscene->svm_nodes.alloc(svm_nodes_size);
98
99 int node_offset = num_shaders;
100 for (int i = 0; i < num_shaders; i++) {
101 Shader *shader = scene->shaders[i];
102
103 shader->clear_modified();
105 scene->light_manager->tag_update(scene, LightManager::SHADER_COMPILED);
106 }
107
108 /* Update the global jump table.
109 * Each compiled shader starts with a jump node that has offsets local
110 * to the shader, so copy those and add the offset into the global node list. */
111 int4 &global_jump_node = svm_nodes[shader->id];
112 const int4 &local_jump_node = shader_svm_nodes[i][0];
113
114 global_jump_node.x = NODE_SHADER_JUMP;
115 global_jump_node.y = local_jump_node.y - 1 + node_offset;
116 global_jump_node.z = local_jump_node.z - 1 + node_offset;
117 global_jump_node.w = local_jump_node.w - 1 + node_offset;
118
119 node_offset += shader_svm_nodes[i].size() - 1;
120 }
121
122 /* Copy the nodes of each shader into the correct location. */
123 svm_nodes += num_shaders;
124 for (int i = 0; i < num_shaders; i++) {
125 const int shader_size = shader_svm_nodes[i].size() - 1;
126
127 std::copy_n(&shader_svm_nodes[i][1], shader_size, svm_nodes);
128 svm_nodes += shader_size;
129 }
130
131 if (progress.get_cancel()) {
132 return;
133 }
134
135 device_update_common(device, dscene, scene, progress);
136
138
139 LOG_INFO << "Shader manager updated " << num_shaders << " shaders in " << time_dt() - start_time
140 << " seconds.";
141}
142
144{
145 device_free_common(device, dscene, scene);
146
147 dscene->svm_nodes.free();
148}
149
150/* Graph Compiler */
151
153{
154 max_stack_use = 0;
156 current_shader = nullptr;
157 current_graph = nullptr;
158 background = false;
161 compile_failed = false;
162
163 /* This struct has one entry for every node, in order of ShaderNodeType definition. */
164 svm_node_types_used = (std::atomic_int *)&scene->dscene.data.svm_usage;
165}
166
168{
169 int size = 0;
170
171 switch (type) {
173 case SocketType::INT:
174 size = 1;
175 break;
180 size = 3;
181 break;
183 size = 0;
184 break;
185 default:
186 assert(0);
187 break;
188 }
189
190 return size;
191}
192
194{
195 int offset = -1;
196
197 /* find free space in stack & mark as used */
198 for (int i = 0, num_unused = 0; i < SVM_STACK_SIZE; i++) {
199 if (active_stack.users[i]) {
200 num_unused = 0;
201 }
202 else {
203 num_unused++;
204 }
205
206 if (num_unused == size) {
207 offset = i + 1 - size;
209
210 while (i >= offset) {
211 active_stack.users[i--] = 1;
212 }
213
214 return offset;
215 }
216 }
217
218 if (!compile_failed) {
219 compile_failed = true;
220 LOG_ERROR << "Shader graph: out of SVM stack space, shader \"" << current_shader->name
221 << "\" too big.";
222 }
223
224 return 0;
225}
226
231
233{
234 const int size = stack_size(type);
235
236 for (int i = 0; i < size; i++) {
237 active_stack.users[offset + i]--;
238 }
239}
240
242{
243 /* stack offset assign? */
244 if (input->stack_offset == SVM_STACK_INVALID) {
245 if (input->link) {
246 /* linked to output -> use output offset */
247 assert(input->link->stack_offset != SVM_STACK_INVALID);
248 input->stack_offset = input->link->stack_offset;
249 }
250 else {
251 Node *node = input->parent;
252
253 /* not linked to output -> add nodes to load default value */
254 input->stack_offset = stack_find_offset(input->type());
255
256 if (input->type() == SocketType::FLOAT) {
257 add_node(NODE_VALUE_F,
258 __float_as_int(node->get_float(input->socket_type)),
259 input->stack_offset);
260 }
261 else if (input->type() == SocketType::INT) {
262 add_node(NODE_VALUE_F, node->get_int(input->socket_type), input->stack_offset);
263 }
264 else if (input->type() == SocketType::VECTOR || input->type() == SocketType::NORMAL ||
265 input->type() == SocketType::POINT || input->type() == SocketType::COLOR)
266 {
267
268 add_node(NODE_VALUE_V, input->stack_offset);
269 add_node(NODE_VALUE_V, node->get_float3(input->socket_type));
270 }
271 else { /* should not get called for closure */
272 assert(0);
273 }
274 }
275 }
276
277 return input->stack_offset;
278}
279
281{
282 /* if no stack offset assigned yet, find one */
283 if (output->stack_offset == SVM_STACK_INVALID) {
284 output->stack_offset = stack_find_offset(output->type());
285 }
286
287 return output->stack_offset;
288}
289
291{
292 return (input->link || input->constant_folded_in);
293}
294
296{
297 if (is_linked(input)) {
298 return stack_assign(input);
299 }
300
301 return SVM_STACK_INVALID;
302}
303
305{
306 if (!output->links.empty()) {
307 return stack_assign(output);
308 }
309
310 return SVM_STACK_INVALID;
311}
312
314{
315 if (is_linked(input) || input->parent->get_float(input->socket_type) != value) {
316 return stack_assign(input);
317 }
318
319 return SVM_STACK_INVALID;
320}
321
323{
324 if (is_linked(input) || input->parent->get_float3(input->socket_type) != value) {
325 return stack_assign(input);
326 }
327
328 return SVM_STACK_INVALID;
329}
330
332{
333 if (output->stack_offset == SVM_STACK_INVALID) {
334 assert(input->link);
335 assert(stack_size(output->type()) == stack_size(input->link->type()));
336
337 output->stack_offset = input->link->stack_offset;
338
339 const int size = stack_size(output->type());
340
341 for (int i = 0; i < size; i++) {
342 active_stack.users[output->stack_offset + i]++;
343 }
344 }
345}
346
348{
349 /* optimization we should add:
350 * find and lower user counts for outputs for which all inputs are done.
351 * this is done before the node is compiled, under the assumption that the
352 * node will first load all inputs from the stack and then writes its
353 * outputs. this used to work, but was disabled because it gave trouble
354 * with inputs getting stack positions assigned */
355
356 for (ShaderInput *input : node->inputs) {
357 ShaderOutput *output = input->link;
358
359 if (output && output->stack_offset != SVM_STACK_INVALID) {
360 bool all_done = true;
361
362 /* optimization we should add: verify if in->parent is actually used */
363 for (ShaderInput *in : output->links) {
364 if (in->parent != node && done.find(in->parent) == done.end()) {
365 all_done = false;
366 }
367 }
368
369 if (all_done) {
370 stack_clear_offset(output->type(), output->stack_offset);
371 output->stack_offset = SVM_STACK_INVALID;
372
373 for (ShaderInput *in : output->links) {
374 in->stack_offset = SVM_STACK_INVALID;
375 }
376 }
377 }
378 }
379}
380
382{
383 for (ShaderInput *input : node->inputs) {
384 if (!input->link && input->stack_offset != SVM_STACK_INVALID) {
385 stack_clear_offset(input->type(), input->stack_offset);
386 input->stack_offset = SVM_STACK_INVALID;
387 }
388 }
389}
390
392{
393 assert(x <= 255);
394 assert(y <= 255);
395 assert(z <= 255);
396 assert(w <= 255);
397
398 return (x) | (y << 8) | (z << 16) | (w << 24);
399}
400
401void SVMCompiler::add_node(const int a, const int b, int c, const int d)
402{
403 current_svm_nodes.push_back_slow(make_int4(a, b, c, d));
404}
405
406void SVMCompiler::add_node(ShaderNodeType type, const int a, int b, const int c)
407{
408 svm_node_types_used[type] = true;
409 current_svm_nodes.push_back_slow(make_int4(type, a, b, c));
410}
411
413{
414 svm_node_types_used[type] = true;
415 current_svm_nodes.push_back_slow(
417}
418
420{
421 current_svm_nodes.push_back_slow(make_int4(
423}
424
426{
427 return scene->shader_manager->get_attribute_id(name);
428}
429
431{
432 return scene->shader_manager->get_attribute_id(std);
433}
434
436{
438 return (std) ? attribute(std) : attribute(name);
439}
440
442 const ShaderNodeSet &done,
444 ShaderNode *skip_node)
445{
446 ShaderNode *node = (input->link) ? input->link->parent : nullptr;
447 if (node != nullptr && done.find(node) == done.end() && node != skip_node &&
448 dependencies.find(node) == dependencies.end())
449 {
450 for (ShaderInput *in : node->inputs) {
451 find_dependencies(dependencies, done, in, skip_node);
452 }
453 dependencies.insert(node);
454 }
455}
456
458{
459 node->compile(*this);
460 stack_clear_users(node, done);
462
464 if (node->has_spatial_varying()) {
465 current_shader->has_surface_spatial_varying = true;
466 }
468 current_shader->has_surface_raytrace = true;
469 }
470 }
471 else if (current_type == SHADER_TYPE_VOLUME) {
472 if (node->has_spatial_varying()) {
473 current_shader->has_volume_spatial_varying = true;
474 }
475 if (node->has_attribute_dependency()) {
476 current_shader->has_volume_attribute_dependency = true;
477 }
478 }
479}
480
482{
483 ShaderNodeSet &done = state->nodes_done;
484 vector<bool> &done_flag = state->nodes_done_flag;
485
486 bool nodes_done;
487 do {
488 nodes_done = true;
489
490 for (ShaderNode *node : nodes) {
491 if (!done_flag[node->id]) {
492 bool inputs_done = true;
493
494 for (ShaderInput *input : node->inputs) {
495 if (input->link && !done_flag[input->link->parent->id]) {
496 inputs_done = false;
497 }
498 }
499 if (inputs_done) {
500 generate_node(node, done);
501 done.insert(node);
502 done_flag[node->id] = true;
503 }
504 else {
505 nodes_done = false;
506 }
507 }
508 }
509 } while (!nodes_done);
510}
511
513{
514 /* Skip generating closure that are not supported or needed for a particular
515 * type of shader. For example a BSDF in a volume shader. */
516 const uint node_feature = node->get_feature();
517 if ((state->node_feature_mask & node_feature) != node_feature) {
518 return;
519 }
520
521 /* execute dependencies for closure */
522 for (ShaderInput *in : node->inputs) {
523 if (in->link != nullptr) {
524 ShaderNodeSet dependencies;
525 find_dependencies(dependencies, state->nodes_done, in);
526 generate_svm_nodes(dependencies, state);
527 }
528 }
529
530 /* closure mix weight */
531 const char *weight_name = (current_type == SHADER_TYPE_VOLUME) ? "VolumeMixWeight" :
532 "SurfaceMixWeight";
533 ShaderInput *weight_in = node->input(weight_name);
534
535 if (weight_in && (weight_in->link || node->get_float(weight_in->socket_type) != 1.0f)) {
536 mix_weight_offset = stack_assign(weight_in);
537 }
538 else {
540 }
541
542 /* compile closure itself */
543 generate_node(node, state->nodes_done);
544
546
548 if (node->has_surface_transparent()) {
549 current_shader->has_surface_transparent = true;
550 }
551 if (node->has_surface_bssrdf()) {
552 current_shader->has_surface_bssrdf = true;
553 if (node->has_bssrdf_bump()) {
554 current_shader->has_bssrdf_bump = true;
555 }
556 }
557 if (node->has_bump()) {
558 current_shader->has_bump = true;
559 }
560 }
561}
562
564 ShaderNode *node,
566 const ShaderNodeSet &shared)
567{
568 if (shared.find(node) != shared.end()) {
569 generate_multi_closure(root_node, node, state);
570 }
571 else {
572 for (ShaderInput *in : node->inputs) {
573 if (in->type() == SocketType::CLOSURE && in->link) {
574 generated_shared_closure_nodes(root_node, in->link->parent, state, shared);
575 }
576 }
577 }
578}
579
581 ShaderGraph *graph,
583{
584 for (ShaderNode *node : graph->nodes) {
586 OutputAOVNode *aov_node = static_cast<OutputAOVNode *>(node);
587 if (aov_node->offset >= 0) {
588 aov_nodes.insert(aov_node);
589 for (ShaderInput *in : node->inputs) {
590 if (in->link != nullptr) {
591 find_dependencies(aov_nodes, state->nodes_done, in);
592 }
593 }
594 }
595 }
596 }
597}
598
600 ShaderNode *node,
602{
603 /* only generate once */
604 if (state->closure_done.find(node) != state->closure_done.end()) {
605 return;
606 }
607
608 state->closure_done.insert(node);
609
611 /* weighting is already taken care of in ShaderGraph::transform_multi_closure */
612 ShaderInput *cl1in = node->input("Closure1");
613 ShaderInput *cl2in = node->input("Closure2");
614 ShaderInput *facin = node->input("Fac");
615
616 /* skip empty mix/add closure nodes */
617 if (!cl1in->link && !cl2in->link) {
618 return;
619 }
620
621 if (facin && facin->link) {
622 /* mix closure: generate instructions to compute mix weight */
623 ShaderNodeSet dependencies;
624 find_dependencies(dependencies, state->nodes_done, facin);
625 generate_svm_nodes(dependencies, state);
626
627 /* execute shared dependencies. this is needed to allow skipping
628 * of zero weight closures and their dependencies later, so we
629 * ensure that they only skip dependencies that are unique to them */
630 ShaderNodeSet cl1deps;
631 ShaderNodeSet cl2deps;
632 ShaderNodeSet shareddeps;
633
634 find_dependencies(cl1deps, state->nodes_done, cl1in);
635 find_dependencies(cl2deps, state->nodes_done, cl2in);
636
637 const ShaderNodeIDComparator node_id_comp;
638 set_intersection(cl1deps.begin(),
639 cl1deps.end(),
640 cl2deps.begin(),
641 cl2deps.end(),
642 std::inserter(shareddeps, shareddeps.begin()),
643 node_id_comp);
644
645 /* it's possible some nodes are not shared between this mix node
646 * inputs, but still needed to be always executed, this mainly
647 * happens when a node of current subbranch is used by a parent
648 * node or so */
649 if (root_node != node) {
650 for (ShaderInput *in : root_node->inputs) {
651 ShaderNodeSet rootdeps;
652 find_dependencies(rootdeps, state->nodes_done, in, node);
653 set_intersection(rootdeps.begin(),
654 rootdeps.end(),
655 cl1deps.begin(),
656 cl1deps.end(),
657 std::inserter(shareddeps, shareddeps.begin()),
658 node_id_comp);
659 set_intersection(rootdeps.begin(),
660 rootdeps.end(),
661 cl2deps.begin(),
662 cl2deps.end(),
663 std::inserter(shareddeps, shareddeps.begin()),
664 node_id_comp);
665 }
666 }
667
668 /* For dependencies AOV nodes, prevent them from being categorized
669 * as exclusive deps of one or the other closure, since the need to
670 * execute them for AOV writing is not dependent on the closure
671 * weights. */
672 if (!state->aov_nodes.empty()) {
673 set_intersection(state->aov_nodes.begin(),
674 state->aov_nodes.end(),
675 cl1deps.begin(),
676 cl1deps.end(),
677 std::inserter(shareddeps, shareddeps.begin()),
678 node_id_comp);
679 set_intersection(state->aov_nodes.begin(),
680 state->aov_nodes.end(),
681 cl2deps.begin(),
682 cl2deps.end(),
683 std::inserter(shareddeps, shareddeps.begin()),
684 node_id_comp);
685 }
686
687 if (!shareddeps.empty()) {
688 if (cl1in->link) {
689 generated_shared_closure_nodes(root_node, cl1in->link->parent, state, shareddeps);
690 }
691 if (cl2in->link) {
692 generated_shared_closure_nodes(root_node, cl2in->link->parent, state, shareddeps);
693 }
694
695 generate_svm_nodes(shareddeps, state);
696 }
697
698 /* generate instructions for input closure 1 */
699 if (cl1in->link) {
700 /* Add instruction to skip closure and its dependencies if mix
701 * weight is zero.
702 */
703 svm_node_types_used[NODE_JUMP_IF_ONE] = true;
704 current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ONE, 0, stack_assign(facin), 0));
705 const int node_jump_skip_index = current_svm_nodes.size() - 1;
706
707 generate_multi_closure(root_node, cl1in->link->parent, state);
708
709 /* Fill in jump instruction location to be after closure. */
710 current_svm_nodes[node_jump_skip_index].y = current_svm_nodes.size() -
711 node_jump_skip_index - 1;
712 }
713
714 /* generate instructions for input closure 2 */
715 if (cl2in->link) {
716 /* Add instruction to skip closure and its dependencies if mix
717 * weight is zero.
718 */
719 svm_node_types_used[NODE_JUMP_IF_ZERO] = true;
720 current_svm_nodes.push_back_slow(make_int4(NODE_JUMP_IF_ZERO, 0, stack_assign(facin), 0));
721 const int node_jump_skip_index = current_svm_nodes.size() - 1;
722
723 generate_multi_closure(root_node, cl2in->link->parent, state);
724
725 /* Fill in jump instruction location to be after closure. */
726 current_svm_nodes[node_jump_skip_index].y = current_svm_nodes.size() -
727 node_jump_skip_index - 1;
728 }
729
730 /* unassign */
732 }
733 else {
734 /* execute closures and their dependencies, no runtime checks
735 * to skip closures here because was already optimized due to
736 * fixed weight or add closure that always needs both */
737 if (cl1in->link) {
738 generate_multi_closure(root_node, cl1in->link->parent, state);
739 }
740 if (cl2in->link) {
741 generate_multi_closure(root_node, cl2in->link->parent, state);
742 }
743 }
744 }
745 else {
747 }
748
749 state->nodes_done.insert(node);
750 state->nodes_done_flag[node->id] = true;
751}
752
754{
755 /* Converting a shader graph into svm_nodes that can be executed
756 * sequentially on the virtual machine is fairly simple. We can keep
757 * looping over nodes and each time all the inputs of a node are
758 * ready, we add svm_nodes for it that read the inputs from the
759 * stack and write outputs back to the stack.
760 *
761 * With the SVM, we always sample only a single closure. We can think
762 * of all closures nodes as a binary tree with mix closures as inner
763 * nodes and other closures as leafs. The SVM will traverse that tree,
764 * each time deciding to go left or right depending on the mix weights,
765 * until a closure is found.
766 *
767 * We only execute nodes that are needed for the mix weights and chosen
768 * closure.
769 */
770
771 current_type = type;
772 current_graph = graph;
773
774 /* get input in output node */
775 ShaderNode *output = graph->output();
776 ShaderInput *clin = nullptr;
777
778 switch (type) {
780 clin = output->input("Surface");
781 break;
783 clin = output->input("Volume");
784 break;
786 clin = output->input("Displacement");
787 break;
788 case SHADER_TYPE_BUMP:
789 clin = output->input("Normal");
790 break;
791 default:
792 assert(0);
793 break;
794 }
795
796 /* clear all compiler state */
797 memset((void *)&active_stack, 0, sizeof(active_stack));
798 current_svm_nodes.clear();
799
800 for (ShaderNode *node : graph->nodes) {
801 for (ShaderInput *input : node->inputs) {
802 input->stack_offset = SVM_STACK_INVALID;
803 }
804 for (ShaderOutput *output : node->outputs) {
805 output->stack_offset = SVM_STACK_INVALID;
806 }
807 }
808
809 /* for the bump shader we need add a node to store the shader state */
810 const bool need_bump_state = (type == SHADER_TYPE_BUMP) &&
811 (shader->get_displacement_method() == DISPLACE_BOTH);
812 if (need_bump_state) {
814 add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset);
815 }
816
817 if (shader->reference_count()) {
818 CompilerState state(graph);
819
820 switch (type) {
821 case SHADER_TYPE_SURFACE: /* generate surface shader */
822 find_aov_nodes_and_dependencies(state.aov_nodes, graph, &state);
823 if (shader->has_surface) {
824 state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_SURFACE;
825 }
826 break;
827 case SHADER_TYPE_VOLUME: /* generate volume shader */
828 if (shader->has_volume) {
829 state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_VOLUME;
830 }
831 break;
832 case SHADER_TYPE_DISPLACEMENT: /* generate displacement shader */
833 if (shader->has_displacement) {
835 }
836 break;
837 case SHADER_TYPE_BUMP: /* generate bump shader */
838 if (clin->link) {
839 state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_BUMP;
840 }
841 break;
842 default:
843 break;
844 }
845
846 if (clin->link) {
848 }
849
850 /* compile output node */
851 output->compile(*this);
852
853 if (!state.aov_nodes.empty()) {
854 /* AOV passes are only written if the object is directly visible, so
855 * there is no point in evaluating all the nodes generated only for the
856 * AOV outputs if that's not the case. Therefore, we insert
857 * NODE_AOV_START into the shader before the AOV-only nodes are
858 * generated which tells the kernel that it can stop evaluation
859 * early if AOVs will not be written. */
860 add_node(NODE_AOV_START, 0, 0, 0);
861 generate_svm_nodes(state.aov_nodes, &state);
862 }
863 }
864
865 /* add node to restore state after bump shader has finished */
866 if (need_bump_state) {
867 add_node(NODE_LEAVE_BUMP_EVAL, bump_state_offset);
869 }
870
871 /* if compile failed, generate empty shader */
872 if (compile_failed) {
873 current_svm_nodes.clear();
874 compile_failed = false;
875 }
876
877 /* for bump shaders we fall thru to the surface shader, but if this is any other kind of shader
878 * it ends here */
879 if (type != SHADER_TYPE_BUMP) {
880 add_node(NODE_END, 0, 0, 0);
881 }
882}
883
885 array<int4> &svm_nodes,
886 const int index,
887 Summary *summary)
888{
889 svm_node_types_used[NODE_SHADER_JUMP] = true;
890 svm_nodes.push_back_slow(make_int4(NODE_SHADER_JUMP, 0, 0, 0));
891
892 /* copy graph for shader with bump mapping */
893 const int start_num_svm_nodes = svm_nodes.size();
894
895 const double time_start = time_dt();
896
897 const bool has_bump = shader->has_bump;
898
899 current_shader = shader;
900
901 /* generate bump shader */
902 if (has_bump) {
903 const scoped_timer timer((summary != nullptr) ? &summary->time_generate_bump : nullptr);
904 compile_type(shader, shader->graph.get(), SHADER_TYPE_BUMP);
905 svm_nodes[index].y = svm_nodes.size();
906 svm_nodes.append(current_svm_nodes);
907 }
908
909 /* generate surface shader */
910 {
911 const scoped_timer timer((summary != nullptr) ? &summary->time_generate_surface : nullptr);
912 compile_type(shader, shader->graph.get(), SHADER_TYPE_SURFACE);
913 /* only set jump offset if there's no bump shader, as the bump shader will fall thru to this
914 * one if it exists */
915 if (!has_bump) {
916 svm_nodes[index].y = svm_nodes.size();
917 }
918 svm_nodes.append(current_svm_nodes);
919 }
920
921 /* generate volume shader */
922 {
923 const scoped_timer timer((summary != nullptr) ? &summary->time_generate_volume : nullptr);
924 compile_type(shader, shader->graph.get(), SHADER_TYPE_VOLUME);
925 svm_nodes[index].z = svm_nodes.size();
926 svm_nodes.append(current_svm_nodes);
927 }
928
929 /* generate displacement shader */
930 {
931 const scoped_timer timer((summary != nullptr) ? &summary->time_generate_displacement :
932 nullptr);
933 compile_type(shader, shader->graph.get(), SHADER_TYPE_DISPLACEMENT);
934 svm_nodes[index].w = svm_nodes.size();
935 svm_nodes.append(current_svm_nodes);
936 }
937
938 /* Fill in summary information. */
939 if (summary != nullptr) {
940 summary->time_total = time_dt() - time_start;
942 summary->num_svm_nodes = svm_nodes.size() - start_num_svm_nodes;
943 }
944
945 /* Estimate emission for MIS. */
946 shader->estimate_emission();
947}
948
949/* Compiler summary implementation. */
950
961
963{
964 string report;
965 report += string_printf("Number of SVM nodes: %d\n", num_svm_nodes);
966 report += string_printf("Peak stack usage: %d\n", peak_stack_usage);
967
968 report += string_printf("Time (in seconds):\n");
969 report += string_printf("Generate: %f\n",
972 report += string_printf(" Surface: %f\n", time_generate_surface);
973 report += string_printf(" Bump: %f\n", time_generate_bump);
974 report += string_printf(" Volume: %f\n", time_generate_volume);
975 report += string_printf(" Displacement: %f\n", time_generate_displacement);
976
977 return report;
978}
979
980/* Global state of the compiler. */
981
983{
984 int max_id = 0;
985 for (ShaderNode *node : graph->nodes) {
986 max_id = max(node->id, max_id);
987 }
988 nodes_done_flag.resize(max_id + 1, false);
990}
991
unsigned int uint
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
Shader * get_shader(const Scene *scene)
device_vector< int4 > svm_nodes
Definition devicescene.h:76
bool get_cancel() const
Definition progress.h:77
ShaderGraph * current_graph
Definition scene/svm.h:119
void compile_type(Shader *shader, ShaderGraph *graph, ShaderType type)
Definition svm.cpp:753
int max_stack_use
Definition scene/svm.h:226
void find_aov_nodes_and_dependencies(ShaderNodeSet &aov_nodes, ShaderGraph *graph, CompilerState *state)
Definition svm.cpp:580
bool background
Definition scene/svm.h:120
SVMCompiler(Scene *scene)
Definition svm.cpp:152
int stack_assign_if_not_equal(ShaderInput *input, const float value)
Definition svm.cpp:313
array< int4 > current_svm_nodes
Definition scene/svm.h:222
int stack_find_offset(const int size)
Definition svm.cpp:193
void generate_closure_node(ShaderNode *node, CompilerState *state)
Definition svm.cpp:512
ShaderType current_type
Definition scene/svm.h:223
uint bump_state_offset
Definition scene/svm.h:228
void compile(Shader *shader, array< int4 > &svm_nodes, const int index, Summary *summary=nullptr)
Definition svm.cpp:884
void generate_node(ShaderNode *node, ShaderNodeSet &done)
Definition svm.cpp:457
uint encode_uchar4(const uint x, const uint y=0, const uint z=0, const uint w=0)
Definition svm.cpp:391
int stack_assign_if_linked(ShaderInput *input)
Definition svm.cpp:295
Shader * current_shader
Definition scene/svm.h:224
void stack_clear_users(ShaderNode *node, ShaderNodeSet &done)
Definition svm.cpp:347
int stack_size(SocketType::Type type)
Definition svm.cpp:167
void find_dependencies(ShaderNodeSet &dependencies, const ShaderNodeSet &done, ShaderInput *input, ShaderNode *skip_node=nullptr)
Definition svm.cpp:441
void stack_clear_temporary(ShaderNode *node)
Definition svm.cpp:381
uint attribute_standard(ustring name)
Definition svm.cpp:435
void add_node(ShaderNodeType type, const int a=0, const int b=0, const int c=0)
Definition svm.cpp:406
Stack active_stack
Definition scene/svm.h:225
void stack_link(ShaderInput *input, ShaderOutput *output)
Definition svm.cpp:331
void generate_svm_nodes(const ShaderNodeSet &nodes, CompilerState *state)
Definition svm.cpp:481
bool is_linked(ShaderInput *input)
Definition svm.cpp:290
void generate_multi_closure(ShaderNode *root_node, ShaderNode *node, CompilerState *state)
Definition svm.cpp:599
uint attribute(ustring name)
Definition svm.cpp:425
void stack_clear_offset(SocketType::Type type, const int offset)
Definition svm.cpp:232
std::atomic_int * svm_node_types_used
Definition scene/svm.h:221
Scene * scene
Definition scene/svm.h:118
uint mix_weight_offset
Definition scene/svm.h:227
bool compile_failed
Definition scene/svm.h:229
void generated_shared_closure_nodes(ShaderNode *root_node, ShaderNode *node, CompilerState *state, const ShaderNodeSet &shared)
Definition svm.cpp:563
int stack_assign(ShaderOutput *output)
Definition svm.cpp:280
void device_update_shader(Scene *scene, Shader *shader, Progress &progress, array< int4 > *svm_nodes)
Definition svm.cpp:31
void device_free(Device *device, DeviceScene *dscene, Scene *scene) override
Definition svm.cpp:143
void device_update_specific(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress) override
Definition svm.cpp:51
~SVMShaderManager() override
OutputNode * output()
unique_ptr_vector< ShaderNode > nodes
ShaderOutput * link
const SocketType & socket_type
void device_update_common(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress)
bool need_update() const
void device_free_common(Device *device, DeviceScene *dscene, Scene *scene)
uint32_t update_flags
ShaderInput * input(const char *name)
virtual bool has_surface_transparent()
virtual uint get_feature()
ShaderNodeSpecialType special_type
virtual bool has_bssrdf_bump()
virtual bool has_spatial_varying()
virtual bool has_attribute_dependency()
virtual bool has_bump()
unique_ptr_vector< ShaderInput > inputs
virtual bool has_surface_bssrdf()
virtual void compile(SVMCompiler &compiler)=0
unique_ptr_vector< ShaderOutput > outputs
ShaderNode * parent
void estimate_emission()
bool has_volume
bool has_surface
EmissionSampling emission_sampling
bool has_displacement
bool has_bump
NODE_DECLARE unique_ptr< ShaderGraph > graph
void append(const array< T > &from)
size_t size() const
void push_back_slow(const T &t)
T * alloc(const size_t width, const size_t height=0)
size_t size() const
#define SVM_STACK_SIZE
#define KERNEL_FEATURE_NODE_MASK_DISPLACEMENT
#define KERNEL_FEATURE_NODE_MASK_BUMP
#define KERNEL_FEATURE_NODE_MASK_VOLUME
#define SVM_BUMP_EVAL_STATE_SIZE
#define KERNEL_FEATURE_NODE_MASK_SURFACE
#define SVM_STACK_INVALID
#define KERNEL_FEATURE_NODE_RAYTRACE
#define CCL_NAMESPACE_END
#define __float_as_int(x)
ccl_device_forceinline int4 make_int4(const int x, const int y, const int z, const int w)
TaskPool * task_pool
#define input
#define assert(assertion)
#define in
#define output
#define shared
ShaderNodeType
@ SHADER_TYPE_BUMP
@ SHADER_TYPE_SURFACE
@ SHADER_TYPE_VOLUME
@ SHADER_TYPE_DISPLACEMENT
AttributeStandard
@ EMISSION_SAMPLING_NONE
#define LOG_DEBUG
Definition log.h:107
#define LOG_ERROR
Definition log.h:101
#define LOG_INFO
Definition log.h:106
static ulong state[N]
const char * name
@ DISPLACE_BOTH
@ SHADER_SPECIAL_TYPE_OUTPUT_AOV
@ SHADER_SPECIAL_TYPE_COMBINE_CLOSURE
set< ShaderNode *, ShaderNodeIDComparator > ShaderNodeSet
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
static AttributeStandard name_standard(const char *name)
float get_float(const SocketType &input) const
float3 get_float3(const SocketType &input) const
ustring name
Definition graph/node.h:177
int reference_count() const
Definition graph/node.h:183
void clear_modified()
int get_int(const SocketType &input) const
vector< bool > nodes_done_flag
Definition scene/svm.h:188
CompilerState(ShaderGraph *graph)
Definition svm.cpp:982
double time_generate_volume
Definition scene/svm.h:66
double time_generate_surface
Definition scene/svm.h:60
double time_generate_bump
Definition scene/svm.h:63
string full_report() const
Definition svm.cpp:962
double time_generate_displacement
Definition scene/svm.h:69
unique_ptr< LightManager > light_manager
Definition scene.h:146
unique_ptr< SceneUpdateStats > update_stats
Definition scene.h:175
Background * background
Definition scene.h:129
unique_ptr_vector< Shader > shaders
Definition scene.h:137
float z
Definition sky_math.h:136
float y
Definition sky_math.h:136
float x
Definition sky_math.h:136
float y
Definition sky_math.h:225
float z
Definition sky_math.h:225
float x
Definition sky_math.h:225
float w
Definition sky_math.h:225
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
CCL_NAMESPACE_BEGIN double time_dt()
Definition time.cpp:47
wmTimer * timer