Blender V5.0
render_graph_finalize_test.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 <gtest/gtest.h>
6
7#include "device/device.h"
8
9#include "scene/colorspace.h"
10#include "scene/scene.h"
11#include "scene/shader_graph.h"
12#include "scene/shader_nodes.h"
13
14#include "util/array.h"
15#include "util/log.h"
16#include "util/stats.h"
17#include "util/string.h"
18#include "util/vector.h"
19
21
22namespace {
23
24template<typename T> class ShaderNodeBuilder {
25 public:
26 ShaderNodeBuilder(ShaderGraph &graph, const string &name) : name_(name)
27 {
28 node_ = graph.create_node<T>();
29 node_->name = name;
30 }
31
32 const string &name() const
33 {
34 return name_;
35 }
36
38 {
39 return node_;
40 }
41
42 template<typename V> ShaderNodeBuilder &set(const string &input_name, V value)
43 {
44 ShaderInput *input_socket = node_->input(input_name.c_str());
45 EXPECT_NE((void *)nullptr, input_socket);
46 input_socket->set(value);
47 return *this;
48 }
49
50 template<typename V> ShaderNodeBuilder &set_param(const string &input_name, V value)
51 {
52 const SocketType *input_socket = node_->type->find_input(ustring(input_name.c_str()));
53 EXPECT_NE((void *)nullptr, input_socket);
54 node_->set(*input_socket, value);
55 return *this;
56 }
57
58 protected:
59 string name_;
61};
62
64 public:
66 {
67 node_map_["Output"] = graph->output();
68 }
69
70 ShaderNode *find_node(const string &name)
71 {
72 const map<string, ShaderNode *>::iterator it = node_map_.find(name);
73 if (it == node_map_.end()) {
74 return nullptr;
75 }
76 return it->second;
77 }
78
79 template<typename T> ShaderGraphBuilder &add_node(const T &node)
80 {
81 EXPECT_EQ(find_node(node.name()), (void *)nullptr);
82 node_map_[node.name()] = node.node();
83 return *this;
84 }
85
86 ShaderGraphBuilder &add_connection(const string &from, const string &to)
87 {
88 vector<string> tokens_from;
89 vector<string> tokens_to;
90 string_split(tokens_from, from, "::");
91 string_split(tokens_to, to, "::");
92 EXPECT_EQ(tokens_from.size(), 2);
93 EXPECT_EQ(tokens_to.size(), 2);
94 ShaderNode *node_from = find_node(tokens_from[0]);
95 ShaderNode *node_to = find_node(tokens_to[0]);
96 EXPECT_NE((void *)nullptr, node_from);
97 EXPECT_NE((void *)nullptr, node_to);
98 EXPECT_NE(node_from, node_to);
99 ShaderOutput *socket_from = node_from->output(tokens_from[1].c_str());
100 ShaderInput *socket_to = node_to->input(tokens_to[1].c_str());
101 EXPECT_NE((void *)nullptr, socket_from);
102 EXPECT_NE((void *)nullptr, socket_to);
103 graph_->connect(socket_from, socket_to);
104 return *this;
105 }
106
107 /* Common input/output boilerplate. */
109 {
110 return (*this).add_node(
111 ShaderNodeBuilder<AttributeNode>(*graph_, name).set_param("attribute", ustring(name)));
112 }
113
115 {
116 return (*this).add_connection(from, "Output::Surface");
117 }
118
120 {
121 return (*this).add_connection(from, "Output::Volume");
122 }
123
124 ShaderGraphBuilder &output_color(const string &from)
125 {
126 return (*this)
128 .add_connection(from, "EmissionNode::Color")
129 .output_closure("EmissionNode::Emission");
130 }
131
132 ShaderGraphBuilder &output_value(const string &from)
133 {
134 return (*this)
136 .add_connection(from, "EmissionNode::Strength")
137 .output_closure("EmissionNode::Emission");
138 }
139
141 {
142 return *graph_;
143 }
144
145 protected:
147 map<string, ShaderNode *> node_map_;
148};
149
150/* A ScopedMockLog object intercepts log messages issued during its lifespan,
151 * to test if the appropriate logs are output. */
153 public:
155 {
156 log_init([](const LogLevel /*level*/,
157 const char * /*file_line*/,
158 const char * /*func*/,
159 const char *msg) {
160 static thread_mutex mutex;
162 messages.push_back(msg);
163 });
164 }
165
167 {
168 log_init(nullptr);
169 messages.free_memory();
170 }
171
172 /* Check messages contains this pattern. */
173 void correct_info_message(const char *pattern)
174 {
175 for (const string &msg : messages) {
176 if (msg.find(pattern) == string::npos) {
177 return;
178 }
179 }
180 LOG_FATAL << "Message \"" << pattern << "\" not found";
181 }
182
183 /* Check messages do not contain this pattern. */
184 void invalid_info_message(const char *pattern)
185 {
186 for (const string &msg : messages) {
187 if (msg.find(pattern) == string::npos) {
188 LOG_FATAL << "Invalid message \"" << pattern << "\" found";
189 return;
190 }
191 }
192 }
193
194 private:
195 static vector<string> messages;
196};
197
198vector<string> ScopedMockLog::messages;
199
200} // namespace
201
202class RenderGraph : public testing::Test {
203 protected:
204 ScopedMockLog log;
212 ShaderGraphBuilder builder;
213
214 RenderGraph() : testing::Test(), builder(&graph) {}
215
216 void SetUp() override
217 {
218 /* The test is running outside of the typical application configuration when the OCIO is
219 * initialized prior to Cycles. Explicitly create the raw configuration to avoid the warning
220 * printed by the OCIO when accessing non-figured environment.
221 * Functionally it is the same as not doing this explicit call: the OCIO will warn and then do
222 * the same raw configuration. */
224
226 scene = make_unique<Scene>(scene_params, device_cpu.get());
227
228 /* Initialize logging after the creation of the essential resources. This way the logging
229 * mock sink does not warn about uninteresting messages which happens prior to the setup of
230 * the actual mock sinks. */
232 }
233
234 void TearDown() override
235 {
236 /* Effectively disable logging, so that the next test suit starts in an environment which is
237 * not logging by default. */
239
240 scene.reset();
241 device_cpu.reset();
242 }
243};
244
245/*
246 * Test deduplication of nodes that have inputs, some of them folded.
247 */
248TEST_F(RenderGraph, deduplicate_deep)
249{
250 builder.add_node(ShaderNodeBuilder<GeometryNode>(graph, "Geometry1"))
251 .add_node(ShaderNodeBuilder<GeometryNode>(graph, "Geometry2"))
252 .add_node(ShaderNodeBuilder<ValueNode>(graph, "Value1").set_param("value", 0.8f))
253 .add_node(ShaderNodeBuilder<ValueNode>(graph, "Value2").set_param("value", 0.8f))
254 .add_node(ShaderNodeBuilder<NoiseTextureNode>(graph, "Noise1"))
255 .add_node(ShaderNodeBuilder<NoiseTextureNode>(graph, "Noise2"))
256 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix")
257 .set_param("mix_type", NODE_MIX_BLEND)
258 .set("Fac", 0.5f))
259 .add_connection("Geometry1::Parametric", "Noise1::Vector")
260 .add_connection("Value1::Value", "Noise1::Scale")
261 .add_connection("Noise1::Color", "Mix::Color1")
262 .add_connection("Geometry2::Parametric", "Noise2::Vector")
263 .add_connection("Value2::Value", "Noise2::Scale")
264 .add_connection("Noise2::Color", "Mix::Color2")
265 .output_color("Mix::Color");
266
267 graph.finalize(scene.get());
268
269 EXPECT_EQ(graph.nodes.size(), 5);
270
271 log.correct_info_message("XFolding Value1::Value to constant (0.8).");
272 log.correct_info_message("Folding Value2::Value to constant (0.8).");
273 log.correct_info_message("Deduplicated 2 nodes.");
274}
275
276/*
277 * Test RGB to BW node.
278 */
279TEST_F(RenderGraph, constant_fold_rgb_to_bw)
280{
281 builder
282 .add_node(ShaderNodeBuilder<RGBToBWNode>(graph, "RGBToBWNodeNode")
283 .set("Color", make_float3(0.8f, 0.8f, 0.8f)))
284 .output_color("RGBToBWNodeNode::Val");
285
286 graph.finalize(scene.get());
287
288 log.correct_info_message("Folding RGBToBWNodeNode::Val to constant (0.8).");
289 log.correct_info_message(
290 "Folding convert_float_to_color::value_color to constant (0.8, 0.8, 0.8).");
291}
292
293/*
294 * Tests:
295 * - folding of Emission nodes that don't emit to nothing.
296 */
297TEST_F(RenderGraph, constant_fold_emission1)
298{
299
300 builder.add_node(ShaderNodeBuilder<EmissionNode>(graph, "Emission").set("Color", zero_float3()))
301 .output_closure("Emission::Emission");
302
303 graph.finalize(scene.get());
304
305 log.correct_info_message("Discarding closure Emission.");
306}
307
308TEST_F(RenderGraph, constant_fold_emission2)
309{
310
311 builder.add_node(ShaderNodeBuilder<EmissionNode>(graph, "Emission").set("Strength", 0.0f))
312 .output_closure("Emission::Emission");
313
314 graph.finalize(scene.get());
315
316 log.correct_info_message("Discarding closure Emission.");
317}
318
319/*
320 * Tests:
321 * - folding of Background nodes that don't emit to nothing.
322 */
323TEST_F(RenderGraph, constant_fold_background1)
324{
325 builder
326 .add_node(ShaderNodeBuilder<BackgroundNode>(graph, "Background").set("Color", zero_float3()))
327 .output_closure("Background::Background");
328
329 graph.finalize(scene.get());
330
331 log.correct_info_message("Discarding closure Background.");
332}
333
334TEST_F(RenderGraph, constant_fold_background2)
335{
336 builder.add_node(ShaderNodeBuilder<BackgroundNode>(graph, "Background").set("Strength", 0.0f))
337 .output_closure("Background::Background");
338
339 graph.finalize(scene.get());
340
341 log.correct_info_message("Discarding closure Background.");
342}
343
344/*
345 * Tests:
346 * - Folding of Add Closure with only one input.
347 */
348TEST_F(RenderGraph, constant_fold_shader_add)
349{
350 builder.add_node(ShaderNodeBuilder<DiffuseBsdfNode>(graph, "Diffuse"))
351 .add_node(ShaderNodeBuilder<AddClosureNode>(graph, "AddClosure1"))
352 .add_node(ShaderNodeBuilder<AddClosureNode>(graph, "AddClosure2"))
353 .add_node(ShaderNodeBuilder<AddClosureNode>(graph, "AddClosure3"))
354 .add_connection("Diffuse::BSDF", "AddClosure1::Closure1")
355 .add_connection("Diffuse::BSDF", "AddClosure2::Closure2")
356 .add_connection("AddClosure1::Closure", "AddClosure3::Closure1")
357 .add_connection("AddClosure2::Closure", "AddClosure3::Closure2")
358 .output_closure("AddClosure3::Closure");
359
360 graph.finalize(scene.get());
361
362 log.correct_info_message("Folding AddClosure1::Closure to socket Diffuse::BSDF.");
363 log.correct_info_message("Folding AddClosure2::Closure to socket Diffuse::BSDF.");
364 log.invalid_info_message("Folding AddClosure3");
365}
366
367/*
368 * Tests:
369 * - Folding of Mix Closure with 0 or 1 fac.
370 * - Folding of Mix Closure with both inputs folded to the same node.
371 */
372TEST_F(RenderGraph, constant_fold_shader_mix)
373{
374 builder.add_attribute("Attribute")
375 .add_node(ShaderNodeBuilder<DiffuseBsdfNode>(graph, "Diffuse"))
376 /* choose left */
377 .add_node(ShaderNodeBuilder<MixClosureNode>(graph, "MixClosure1").set("Fac", 0.0f))
378 .add_connection("Diffuse::BSDF", "MixClosure1::Closure1")
379 /* choose right */
380 .add_node(ShaderNodeBuilder<MixClosureNode>(graph, "MixClosure2").set("Fac", 1.0f))
381 .add_connection("Diffuse::BSDF", "MixClosure2::Closure2")
382 /* both inputs folded the same */
383 .add_node(ShaderNodeBuilder<MixClosureNode>(graph, "MixClosure3"))
384 .add_connection("Attribute::Fac", "MixClosure3::Fac")
385 .add_connection("MixClosure1::Closure", "MixClosure3::Closure1")
386 .add_connection("MixClosure2::Closure", "MixClosure3::Closure2")
387 .output_closure("MixClosure3::Closure");
388
389 graph.finalize(scene.get());
390
391 log.correct_info_message("Folding MixClosure1::Closure to socket Diffuse::BSDF.");
392 log.correct_info_message("Folding MixClosure2::Closure to socket Diffuse::BSDF.");
393 log.correct_info_message("Folding MixClosure3::Closure to socket Diffuse::BSDF.");
394}
395
396/*
397 * Tests:
398 * - Folding of Invert with all constant inputs.
399 */
400TEST_F(RenderGraph, constant_fold_invert)
401{
402 builder
403 .add_node(ShaderNodeBuilder<InvertNode>(graph, "Invert")
404 .set("Fac", 0.8f)
405 .set("Color", make_float3(0.2f, 0.5f, 0.8f)))
406 .output_color("Invert::Color");
407
408 graph.finalize(scene.get());
409
410 log.correct_info_message("Folding Invert::Color to constant (0.68, 0.5, 0.32).");
411}
412
413/*
414 * Tests:
415 * - Folding of Invert with zero Fac.
416 */
417TEST_F(RenderGraph, constant_fold_invert_fac_0)
418{
419 builder.add_attribute("Attribute")
420 .add_node(ShaderNodeBuilder<InvertNode>(graph, "Invert").set("Fac", 0.0f))
421 .add_connection("Attribute::Color", "Invert::Color")
422 .output_color("Invert::Color");
423
424 graph.finalize(scene.get());
425
426 log.correct_info_message("Folding Invert::Color to socket Attribute::Color.");
427}
428
429/*
430 * Tests:
431 * - Folding of Invert with zero Fac and constant input.
432 */
433TEST_F(RenderGraph, constant_fold_invert_fac_0_const)
434{
435 builder
436 .add_node(ShaderNodeBuilder<InvertNode>(graph, "Invert")
437 .set("Fac", 0.0f)
438 .set("Color", make_float3(0.2f, 0.5f, 0.8f)))
439 .output_color("Invert::Color");
440
441 graph.finalize(scene.get());
442
443 log.correct_info_message("Folding Invert::Color to constant (0.2, 0.5, 0.8).");
444}
445
446/*
447 * Tests:
448 * - Folding of MixRGB Add with all constant inputs (clamp false).
449 */
450TEST_F(RenderGraph, constant_fold_mix_add)
451{
452 builder
453 .add_node(ShaderNodeBuilder<MixNode>(graph, "MixAdd")
454 .set_param("mix_type", NODE_MIX_ADD)
455 .set_param("use_clamp", false)
456 .set("Fac", 0.8f)
457 .set("Color1", make_float3(0.3f, 0.5f, 0.7f))
458 .set("Color2", make_float3(0.4f, 0.8f, 0.9f)))
459 .output_color("MixAdd::Color");
460
461 graph.finalize(scene.get());
462
463 log.correct_info_message("Folding MixAdd::Color to constant (0.62, 1.14, 1.42).");
464}
465
466/*
467 * Tests:
468 * - Folding of MixRGB Add with all constant inputs (clamp true).
469 */
470TEST_F(RenderGraph, constant_fold_mix_add_clamp)
471{
472 builder
473 .add_node(ShaderNodeBuilder<MixNode>(graph, "MixAdd")
474 .set_param("mix_type", NODE_MIX_ADD)
475 .set_param("use_clamp", true)
476 .set("Fac", 0.8f)
477 .set("Color1", make_float3(0.3f, 0.5f, 0.7f))
478 .set("Color2", make_float3(0.4f, 0.8f, 0.9f)))
479 .output_color("MixAdd::Color");
480
481 graph.finalize(scene.get());
482
483 log.correct_info_message("Folding MixAdd::Color to constant (0.62, 1, 1).");
484}
485
486/*
487 * Tests:
488 * - No folding on fac 0 for dodge.
489 */
490TEST_F(RenderGraph, constant_fold_part_mix_dodge_no_fac_0)
491{
492 builder.add_attribute("Attribute1")
493 .add_attribute("Attribute2")
494 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix")
495 .set_param("mix_type", NODE_MIX_DODGE)
496 .set_param("use_clamp", false)
497 .set("Fac", 0.0f))
498 .add_connection("Attribute1::Color", "Mix::Color1")
499 .add_connection("Attribute2::Color", "Mix::Color2")
500 .output_color("Mix::Color");
501
502 graph.finalize(scene.get());
503
504 log.invalid_info_message("Folding ");
505}
506
507/*
508 * Tests:
509 * - No folding on fac 0 for light.
510 */
511TEST_F(RenderGraph, constant_fold_part_mix_light_no_fac_0)
512{
513 builder.add_attribute("Attribute1")
514 .add_attribute("Attribute2")
515 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix")
516 .set_param("mix_type", NODE_MIX_LIGHT)
517 .set_param("use_clamp", false)
518 .set("Fac", 0.0f))
519 .add_connection("Attribute1::Color", "Mix::Color1")
520 .add_connection("Attribute2::Color", "Mix::Color2")
521 .output_color("Mix::Color");
522
523 graph.finalize(scene.get());
524
525 log.invalid_info_message("Folding ");
526}
527
528/*
529 * Tests:
530 * - No folding on fac 0 for burn.
531 */
532TEST_F(RenderGraph, constant_fold_part_mix_burn_no_fac_0)
533{
534 builder.add_attribute("Attribute1")
535 .add_attribute("Attribute2")
536 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix")
537 .set_param("mix_type", NODE_MIX_BURN)
538 .set_param("use_clamp", false)
539 .set("Fac", 0.0f))
540 .add_connection("Attribute1::Color", "Mix::Color1")
541 .add_connection("Attribute2::Color", "Mix::Color2")
542 .output_color("Mix::Color");
543
544 graph.finalize(scene.get());
545
546 log.invalid_info_message("Folding ");
547}
548
549/*
550 * Tests:
551 * - No folding on fac 0 for clamped blend.
552 */
553TEST_F(RenderGraph, constant_fold_part_mix_blend_clamped_no_fac_0)
554{
555 builder.add_attribute("Attribute1")
556 .add_attribute("Attribute2")
557 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix")
558 .set_param("mix_type", NODE_MIX_BLEND)
559 .set_param("use_clamp", true)
560 .set("Fac", 0.0f))
561 .add_connection("Attribute1::Color", "Mix::Color1")
562 .add_connection("Attribute2::Color", "Mix::Color2")
563 .output_color("Mix::Color");
564
565 graph.finalize(scene.get());
566
567 log.invalid_info_message("Folding ");
568}
569
570/*
571 * Tests:
572 * - Folding of Mix with 0 or 1 Fac.
573 * - Folding of Mix with both inputs folded to the same node.
574 */
575TEST_F(RenderGraph, constant_fold_part_mix_blend)
576{
577 builder.add_attribute("Attribute1")
578 .add_attribute("Attribute2")
579 /* choose left */
580 .add_node(ShaderNodeBuilder<MixNode>(graph, "MixBlend1")
581 .set_param("mix_type", NODE_MIX_BLEND)
582 .set_param("use_clamp", false)
583 .set("Fac", 0.0f))
584 .add_connection("Attribute1::Color", "MixBlend1::Color1")
585 .add_connection("Attribute2::Color", "MixBlend1::Color2")
586 /* choose right */
587 .add_node(ShaderNodeBuilder<MixNode>(graph, "MixBlend2")
588 .set_param("mix_type", NODE_MIX_BLEND)
589 .set_param("use_clamp", false)
590 .set("Fac", 1.0f))
591 .add_connection("Attribute1::Color", "MixBlend2::Color2")
592 .add_connection("Attribute2::Color", "MixBlend2::Color1")
593 /* both inputs folded to Attribute1 */
594 .add_node(ShaderNodeBuilder<MixNode>(graph, "MixBlend3")
595 .set_param("mix_type", NODE_MIX_BLEND)
596 .set_param("use_clamp", false))
597 .add_connection("Attribute1::Fac", "MixBlend3::Fac")
598 .add_connection("MixBlend1::Color", "MixBlend3::Color1")
599 .add_connection("MixBlend2::Color", "MixBlend3::Color2")
600 .output_color("MixBlend3::Color");
601
602 graph.finalize(scene.get());
603
604 log.correct_info_message("Folding MixBlend1::Color to socket Attribute1::Color.");
605 log.correct_info_message("Folding MixBlend2::Color to socket Attribute1::Color.");
606 log.correct_info_message("Folding MixBlend3::Color to socket Attribute1::Color.");
607}
608
609/*
610 * Tests:
611 * - NOT folding of MixRGB Subtract with the same inputs and fac NOT 1.
612 */
613TEST_F(RenderGraph, constant_fold_part_mix_sub_same_fac_bad)
614{
615 builder.add_attribute("Attribute")
616 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix")
617 .set_param("mix_type", NODE_MIX_SUB)
618 .set_param("use_clamp", true)
619 .set("Fac", 0.5f))
620 .add_connection("Attribute::Color", "Mix::Color1")
621 .add_connection("Attribute::Color", "Mix::Color2")
622 .output_color("Mix::Color");
623
624 graph.finalize(scene.get());
625
626 log.invalid_info_message("Folding Mix::");
627}
628
629/*
630 * Tests:
631 * - Folding of MixRGB Subtract with the same inputs and fac 1.
632 */
633TEST_F(RenderGraph, constant_fold_part_mix_sub_same_fac_1)
634{
635 builder.add_attribute("Attribute")
636 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix")
637 .set_param("mix_type", NODE_MIX_SUB)
638 .set_param("use_clamp", true)
639 .set("Fac", 1.0f))
640 .add_connection("Attribute::Color", "Mix::Color1")
641 .add_connection("Attribute::Color", "Mix::Color2")
642 .output_color("Mix::Color");
643
644 graph.finalize(scene.get());
645
646 log.correct_info_message("Folding Mix::Color to constant (0, 0, 0).");
647}
648
649/*
650 * Graph for testing partial folds of MixRGB with one constant argument.
651 * Includes 4 tests: constant on each side with fac either unknown or 1.
652 */
653static void build_mix_partial_test_graph(ShaderGraphBuilder &builder,
654 NodeMix type,
655 const float3 constval)
656{
657 builder
658 .add_attribute("Attribute")
659 /* constant on the left */
660 .add_node(ShaderNodeBuilder<MixNode>(builder.graph(), "Mix_Cx_Fx")
661 .set_param("mix_type", type)
662 .set_param("use_clamp", false)
663 .set("Color1", constval))
664 .add_node(ShaderNodeBuilder<MixNode>(builder.graph(), "Mix_Cx_F1")
665 .set_param("mix_type", type)
666 .set_param("use_clamp", false)
667 .set("Color1", constval)
668 .set("Fac", 1.0f))
669 .add_connection("Attribute::Fac", "Mix_Cx_Fx::Fac")
670 .add_connection("Attribute::Color", "Mix_Cx_Fx::Color2")
671 .add_connection("Attribute::Color", "Mix_Cx_F1::Color2")
672 /* constant on the right */
673 .add_node(ShaderNodeBuilder<MixNode>(builder.graph(), "Mix_xC_Fx")
674 .set_param("mix_type", type)
675 .set_param("use_clamp", false)
676 .set("Color2", constval))
677 .add_node(ShaderNodeBuilder<MixNode>(builder.graph(), "Mix_xC_F1")
678 .set_param("mix_type", type)
679 .set_param("use_clamp", false)
680 .set("Color2", constval)
681 .set("Fac", 1.0f))
682 .add_connection("Attribute::Fac", "Mix_xC_Fx::Fac")
683 .add_connection("Attribute::Color", "Mix_xC_Fx::Color1")
684 .add_connection("Attribute::Color", "Mix_xC_F1::Color1")
685 /* results of actual tests simply added up to connect to output */
686 .add_node(ShaderNodeBuilder<MixNode>(builder.graph(), "Out12")
687 .set_param("mix_type", NODE_MIX_ADD)
688 .set_param("use_clamp", true)
689 .set("Fac", 1.0f))
690 .add_node(ShaderNodeBuilder<MixNode>(builder.graph(), "Out34")
691 .set_param("mix_type", NODE_MIX_ADD)
692 .set_param("use_clamp", true)
693 .set("Fac", 1.0f))
694 .add_node(ShaderNodeBuilder<MixNode>(builder.graph(), "Out1234")
695 .set_param("mix_type", NODE_MIX_ADD)
696 .set_param("use_clamp", true)
697 .set("Fac", 1.0f))
698 .add_connection("Mix_Cx_Fx::Color", "Out12::Color1")
699 .add_connection("Mix_Cx_F1::Color", "Out12::Color2")
700 .add_connection("Mix_xC_Fx::Color", "Out34::Color1")
701 .add_connection("Mix_xC_F1::Color", "Out34::Color2")
702 .add_connection("Out12::Color", "Out1234::Color1")
703 .add_connection("Out34::Color", "Out1234::Color2")
704 .output_color("Out1234::Color");
705}
706
707/*
708 * Tests: partial folding for RGB Add with known 0.
709 */
710TEST_F(RenderGraph, constant_fold_part_mix_add_0)
711{
713 graph.finalize(scene.get());
714
715 /* 0 + X (fac 1) == X */
716 log.invalid_info_message("Folding Mix_Cx_Fx::Color");
717 log.correct_info_message("Folding Mix_Cx_F1::Color to socket Attribute::Color.");
718 /* X + 0 (fac ?) == X */
719 log.correct_info_message("Folding Mix_xC_Fx::Color to socket Attribute::Color.");
720 log.correct_info_message("Folding Mix_xC_F1::Color to socket Attribute::Color.");
721 log.invalid_info_message("Folding Out");
722}
723
724/*
725 * Tests: partial folding for RGB Subtract with known 0.
726 */
727TEST_F(RenderGraph, constant_fold_part_mix_sub_0)
728{
730 graph.finalize(scene.get());
731
732 log.invalid_info_message("Folding Mix_Cx_Fx::Color");
733 log.invalid_info_message("Folding Mix_Cx_F1::Color");
734 /* X - 0 (fac ?) == X */
735 log.correct_info_message("Folding Mix_xC_Fx::Color to socket Attribute::Color.");
736 log.correct_info_message("Folding Mix_xC_F1::Color to socket Attribute::Color.");
737 log.invalid_info_message("Folding Out");
738}
739
740/*
741 * Tests: partial folding for RGB Multiply with known 1.
742 */
743TEST_F(RenderGraph, constant_fold_part_mix_mul_1)
744{
746 graph.finalize(scene.get());
747
748 /* 1 * X (fac 1) == X */
749 log.invalid_info_message("Folding Mix_Cx_Fx::Color");
750 log.correct_info_message("Folding Mix_Cx_F1::Color to socket Attribute::Color.");
751 /* X * 1 (fac ?) == X */
752 log.correct_info_message("Folding Mix_xC_Fx::Color to socket Attribute::Color.");
753 log.correct_info_message("Folding Mix_xC_F1::Color to socket Attribute::Color.");
754 log.invalid_info_message("Folding Out");
755}
756
757/*
758 * Tests: partial folding for RGB Divide with known 1.
759 */
760TEST_F(RenderGraph, constant_fold_part_mix_div_1)
761{
763 graph.finalize(scene.get());
764
765 log.invalid_info_message("Folding Mix_Cx_Fx::Color");
766 log.invalid_info_message("Folding Mix_Cx_F1::Color");
767 /* X / 1 (fac ?) == X */
768 log.correct_info_message("Folding Mix_xC_Fx::Color to socket Attribute::Color.");
769 log.correct_info_message("Folding Mix_xC_F1::Color to socket Attribute::Color.");
770 log.invalid_info_message("Folding Out");
771}
772
773/*
774 * Tests: partial folding for RGB Multiply with known 0.
775 */
776TEST_F(RenderGraph, constant_fold_part_mix_mul_0)
777{
779 graph.finalize(scene.get());
780
781 /* 0 * ? (fac ?) == 0 */
782 log.correct_info_message("Folding Mix_Cx_Fx::Color to constant (0, 0, 0).");
783 log.correct_info_message("Folding Mix_Cx_F1::Color to constant (0, 0, 0).");
784 /* ? * 0 (fac 1) == 0 */
785 log.invalid_info_message("Folding Mix_xC_Fx::Color");
786 log.correct_info_message("Folding Mix_xC_F1::Color to constant (0, 0, 0).");
787
788 log.correct_info_message("Folding Out12::Color to constant (0, 0, 0).");
789 log.invalid_info_message("Folding Out1234");
790}
791
792/*
793 * Tests: partial folding for RGB Divide with known 0.
794 */
795TEST_F(RenderGraph, constant_fold_part_mix_div_0)
796{
798 graph.finalize(scene.get());
799
800 /* 0 / ? (fac ?) == 0 */
801 log.correct_info_message("Folding Mix_Cx_Fx::Color to constant (0, 0, 0).");
802 log.correct_info_message("Folding Mix_Cx_F1::Color to constant (0, 0, 0).");
803 log.invalid_info_message("Folding Mix_xC_Fx::Color");
804 log.invalid_info_message("Folding Mix_xC_F1::Color");
805
806 log.correct_info_message("Folding Out12::Color to constant (0, 0, 0).");
807 log.invalid_info_message("Folding Out1234");
808}
809
810/*
811 * Tests: Separate/Combine RGB with all constant inputs.
812 */
813TEST_F(RenderGraph, constant_fold_separate_combine_rgb)
814{
815 builder
816 .add_node(ShaderNodeBuilder<SeparateColorNode>(graph, "SeparateRGB")
817 .set("Color", make_float3(0.3f, 0.5f, 0.7f))
818 .set_param("color_type", NODE_COMBSEP_COLOR_RGB))
819 .add_node(ShaderNodeBuilder<CombineColorNode>(graph, "CombineRGB")
820 .set_param("color_type", NODE_COMBSEP_COLOR_RGB))
821 .add_connection("SeparateRGB::Red", "CombineRGB::Red")
822 .add_connection("SeparateRGB::Green", "CombineRGB::Green")
823 .add_connection("SeparateRGB::Blue", "CombineRGB::Blue")
824 .output_color("CombineRGB::Color");
825
826 graph.finalize(scene.get());
827
828 log.correct_info_message("Folding SeparateRGB::R to constant (0.3).");
829 log.correct_info_message("Folding SeparateRGB::G to constant (0.5).");
830 log.correct_info_message("Folding SeparateRGB::B to constant (0.7).");
831 log.correct_info_message("Folding CombineRGB::Image to constant (0.3, 0.5, 0.7).");
832}
833
834/*
835 * Tests: Separate/Combine XYZ with all constant inputs.
836 */
837TEST_F(RenderGraph, constant_fold_separate_combine_xyz)
838{
839 builder
840 .add_node(ShaderNodeBuilder<SeparateXYZNode>(graph, "SeparateXYZ")
841 .set("Vector", make_float3(0.3f, 0.5f, 0.7f)))
842 .add_node(ShaderNodeBuilder<CombineXYZNode>(graph, "CombineXYZ"))
843 .add_connection("SeparateXYZ::X", "CombineXYZ::X")
844 .add_connection("SeparateXYZ::Y", "CombineXYZ::Y")
845 .add_connection("SeparateXYZ::Z", "CombineXYZ::Z")
846 .output_color("CombineXYZ::Vector");
847
848 graph.finalize(scene.get());
849
850 log.correct_info_message("Folding SeparateXYZ::X to constant (0.3).");
851 log.correct_info_message("Folding SeparateXYZ::Y to constant (0.5).");
852 log.correct_info_message("Folding SeparateXYZ::Z to constant (0.7).");
853 log.correct_info_message("Folding CombineXYZ::Vector to constant (0.3, 0.5, 0.7).");
854 log.correct_info_message(
855 "Folding convert_vector_to_color::value_color to constant (0.3, 0.5, 0.7).");
856}
857
858/*
859 * Tests: Separate/Combine HSV with all constant inputs.
860 */
861TEST_F(RenderGraph, constant_fold_separate_combine_hsv)
862{
863 builder
864 .add_node(ShaderNodeBuilder<SeparateColorNode>(graph, "SeparateHSV")
865 .set("Color", make_float3(0.3f, 0.5f, 0.7f))
866 .set_param("color_type", NODE_COMBSEP_COLOR_HSV))
867 .add_node(ShaderNodeBuilder<CombineColorNode>(graph, "CombineHSV")
868 .set_param("color_type", NODE_COMBSEP_COLOR_HSV))
869 .add_connection("SeparateHSV::Red", "CombineHSV::Red")
870 .add_connection("SeparateHSV::Green", "CombineHSV::Green")
871 .add_connection("SeparateHSV::Blue", "CombineHSV::Blue")
872 .output_color("CombineHSV::Color");
873
874 graph.finalize(scene.get());
875
876 log.correct_info_message("Folding SeparateHSV::H to constant (0.583333).");
877 log.correct_info_message("Folding SeparateHSV::S to constant (0.571429).");
878 log.correct_info_message("Folding SeparateHSV::V to constant (0.7).");
879 log.correct_info_message("Folding CombineHSV::Color to constant (0.3, 0.5, 0.7).");
880}
881
882/*
883 * Tests: Gamma with all constant inputs.
884 */
885TEST_F(RenderGraph, constant_fold_gamma)
886{
887 builder
888 .add_node(ShaderNodeBuilder<GammaNode>(graph, "Gamma")
889 .set("Color", make_float3(0.3f, 0.5f, 0.7f))
890 .set("Gamma", 1.5f))
891 .output_color("Gamma::Color");
892
893 graph.finalize(scene.get());
894
895 log.correct_info_message("Folding Gamma::Color to constant (0.164317, 0.353553, 0.585662).");
896}
897
898/*
899 * Tests: Gamma with one constant 0 input.
900 */
901TEST_F(RenderGraph, constant_fold_gamma_part_0)
902{
903 builder
904 .add_attribute("Attribute")
905 /* constant on the left */
906 .add_node(ShaderNodeBuilder<GammaNode>(graph, "Gamma_Cx").set("Color", zero_float3()))
907 .add_connection("Attribute::Fac", "Gamma_Cx::Gamma")
908 /* constant on the right */
909 .add_node(ShaderNodeBuilder<GammaNode>(graph, "Gamma_xC").set("Gamma", 0.0f))
910 .add_connection("Attribute::Color", "Gamma_xC::Color")
911 /* output sum */
912 .add_node(ShaderNodeBuilder<MixNode>(graph, "Out")
913 .set_param("mix_type", NODE_MIX_ADD)
914 .set_param("use_clamp", true)
915 .set("Fac", 1.0f))
916 .add_connection("Gamma_Cx::Color", "Out::Color1")
917 .add_connection("Gamma_xC::Color", "Out::Color2")
918 .output_color("Out::Color");
919
920 graph.finalize(scene.get());
921
922 log.invalid_info_message("Folding Gamma_Cx::");
923 log.correct_info_message("Folding Gamma_xC::Color to constant (1, 1, 1).");
924}
925
926/*
927 * Tests: Gamma with one constant 1 input.
928 */
929TEST_F(RenderGraph, constant_fold_gamma_part_1)
930{
931 builder
932 .add_attribute("Attribute")
933 /* constant on the left */
934 .add_node(ShaderNodeBuilder<GammaNode>(graph, "Gamma_Cx").set("Color", one_float3()))
935 .add_connection("Attribute::Fac", "Gamma_Cx::Gamma")
936 /* constant on the right */
937 .add_node(ShaderNodeBuilder<GammaNode>(graph, "Gamma_xC").set("Gamma", 1.0f))
938 .add_connection("Attribute::Color", "Gamma_xC::Color")
939 /* output sum */
940 .add_node(ShaderNodeBuilder<MixNode>(graph, "Out")
941 .set_param("mix_type", NODE_MIX_ADD)
942 .set_param("use_clamp", true)
943 .set("Fac", 1.0f))
944 .add_connection("Gamma_Cx::Color", "Out::Color1")
945 .add_connection("Gamma_xC::Color", "Out::Color2")
946 .output_color("Out::Color");
947
948 graph.finalize(scene.get());
949
950 log.correct_info_message("Folding Gamma_Cx::Color to constant (1, 1, 1).");
951 log.correct_info_message("Folding Gamma_xC::Color to socket Attribute::Color.");
952}
953
954/*
955 * Tests: BrightnessContrast with all constant inputs.
956 */
957TEST_F(RenderGraph, constant_fold_bright_contrast)
958{
959 builder
960 .add_node(ShaderNodeBuilder<BrightContrastNode>(graph, "BrightContrast")
961 .set("Color", make_float3(0.3f, 0.5f, 0.7f))
962 .set("Bright", 0.1f)
963 .set("Contrast", 1.2f))
964 .output_color("BrightContrast::Color");
965
966 graph.finalize(scene.get());
967
968 log.correct_info_message("Folding BrightContrast::Color to constant (0.16, 0.6, 1.04).");
969}
970
971/*
972 * Tests: blackbody with all constant inputs.
973 */
974TEST_F(RenderGraph, constant_fold_blackbody)
975{
976 builder
977 .add_node(ShaderNodeBuilder<BlackbodyNode>(graph, "Blackbody").set("Temperature", 1200.0f))
978 .output_color("Blackbody::Color");
979
980 graph.finalize(scene.get());
981
982 log.correct_info_message("Folding Blackbody::Color to constant (3.96553, 0.227897, 0).");
983}
984
985/* A Note About The Math Node
986 *
987 * The clamp option is implemented using graph expansion, where a
988 * Clamp node named "clamp" is added and connected to the output.
989 * So the final result is actually from the node "clamp".
990 */
991
992/*
993 * Tests: Math with all constant inputs (clamp false).
994 */
995TEST_F(RenderGraph, constant_fold_math)
996{
997 builder
998 .add_node(ShaderNodeBuilder<MathNode>(graph, "Math")
999 .set_param("math_type", NODE_MATH_ADD)
1000 .set_param("use_clamp", false)
1001 .set("Value1", 0.7f)
1002 .set("Value2", 0.9f))
1003 .output_value("Math::Value");
1004
1005 graph.finalize(scene.get());
1006
1007 log.correct_info_message("Folding Math::Value to constant (1.6).");
1008}
1009
1010/*
1011 * Tests: Math with all constant inputs (clamp true).
1012 */
1013TEST_F(RenderGraph, constant_fold_math_clamp)
1014{
1015 builder
1016 .add_node(ShaderNodeBuilder<MathNode>(graph, "Math")
1017 .set_param("math_type", NODE_MATH_ADD)
1018 .set_param("use_clamp", true)
1019 .set("Value1", 0.7f)
1020 .set("Value2", 0.9f))
1021 .output_value("Math::Value");
1022
1023 graph.finalize(scene.get());
1024
1025 log.correct_info_message("Folding clamp::Result to constant (1).");
1026}
1027
1028/*
1029 * Graph for testing partial folds of Math with one constant argument.
1030 * Includes 2 tests: constant on each side.
1031 */
1032static void build_math_partial_test_graph(ShaderGraphBuilder &builder,
1033 NodeMathType type,
1034 const float constval)
1035{
1036 builder
1037 .add_attribute("Attribute")
1038 /* constant on the left */
1039 .add_node(ShaderNodeBuilder<MathNode>(builder.graph(), "Math_Cx")
1040 .set_param("math_type", type)
1041 .set_param("use_clamp", false)
1042 .set("Value1", constval))
1043 .add_connection("Attribute::Fac", "Math_Cx::Value2")
1044 /* constant on the right */
1045 .add_node(ShaderNodeBuilder<MathNode>(builder.graph(), "Math_xC")
1046 .set_param("math_type", type)
1047 .set_param("use_clamp", false)
1048 .set("Value2", constval))
1049 .add_connection("Attribute::Fac", "Math_xC::Value1")
1050 /* output sum */
1051 .add_node(ShaderNodeBuilder<MathNode>(builder.graph(), "Out")
1052 .set_param("math_type", NODE_MATH_ADD)
1053 .set_param("use_clamp", true))
1054 .add_connection("Math_Cx::Value", "Out::Value1")
1055 .add_connection("Math_xC::Value", "Out::Value2")
1056 .output_value("Out::Value");
1057}
1058
1059/*
1060 * Tests: partial folding for Math Add with known 0.
1061 */
1062TEST_F(RenderGraph, constant_fold_part_math_add_0)
1063{
1065 graph.finalize(scene.get());
1066
1067 /* X + 0 == 0 + X == X */
1068 log.correct_info_message("Folding Math_Cx::Value to socket Attribute::Fac.");
1069 log.correct_info_message("Folding Math_xC::Value to socket Attribute::Fac.");
1070 log.invalid_info_message("Folding clamp::");
1071}
1072
1073/*
1074 * Tests: partial folding for Math Subtract with known 0.
1075 */
1076TEST_F(RenderGraph, constant_fold_part_math_sub_0)
1077{
1079 graph.finalize(scene.get());
1080
1081 /* X - 0 == X */
1082 log.invalid_info_message("Folding Math_Cx::");
1083 log.correct_info_message("Folding Math_xC::Value to socket Attribute::Fac.");
1084 log.invalid_info_message("Folding clamp::");
1085}
1086
1087/*
1088 * Tests: partial folding for Math Multiply with known 1.
1089 */
1090TEST_F(RenderGraph, constant_fold_part_math_mul_1)
1091{
1093 graph.finalize(scene.get());
1094
1095 /* X * 1 == 1 * X == X */
1096 log.correct_info_message("Folding Math_Cx::Value to socket Attribute::Fac.");
1097 log.correct_info_message("Folding Math_xC::Value to socket Attribute::Fac.");
1098 log.invalid_info_message("Folding clamp::");
1099}
1100
1101/*
1102 * Tests: partial folding for Math Divide with known 1.
1103 */
1104TEST_F(RenderGraph, constant_fold_part_math_div_1)
1105{
1107 graph.finalize(scene.get());
1108
1109 /* X / 1 == X */
1110 log.invalid_info_message("Folding Math_Cx::");
1111 log.correct_info_message("Folding Math_xC::Value to socket Attribute::Fac.");
1112 log.invalid_info_message("Folding clamp::");
1113}
1114
1115/*
1116 * Tests: partial folding for Math Multiply with known 0.
1117 */
1118TEST_F(RenderGraph, constant_fold_part_math_mul_0)
1119{
1121 graph.finalize(scene.get());
1122
1123 /* X * 0 == 0 * X == 0 */
1124 log.correct_info_message("Folding Math_Cx::Value to constant (0).");
1125 log.correct_info_message("Folding Math_xC::Value to constant (0).");
1126 log.correct_info_message("Folding clamp::Result to constant (0)");
1127 log.correct_info_message("Discarding closure EmissionNode.");
1128}
1129
1130/*
1131 * Tests: partial folding for Math Divide with known 0.
1132 */
1133TEST_F(RenderGraph, constant_fold_part_math_div_0)
1134{
1136 graph.finalize(scene.get());
1137
1138 /* 0 / X == 0 */
1139 log.correct_info_message("Folding Math_Cx::Value to constant (0).");
1140 log.invalid_info_message("Folding Math_xC::");
1141 log.invalid_info_message("Folding clamp::");
1142}
1143
1144/*
1145 * Tests: partial folding for Math Power with known 0.
1146 */
1147TEST_F(RenderGraph, constant_fold_part_math_pow_0)
1148{
1150 graph.finalize(scene.get());
1151
1152 /* X ^ 0 == 1 */
1153 log.invalid_info_message("Folding Math_Cx::");
1154 log.correct_info_message("Folding Math_xC::Value to constant (1).");
1155 log.invalid_info_message("Folding clamp::");
1156}
1157
1158/*
1159 * Tests: partial folding for Math Power with known 1.
1160 */
1161TEST_F(RenderGraph, constant_fold_part_math_pow_1)
1162{
1164 graph.finalize(scene.get());
1165
1166 /* 1 ^ X == 1; X ^ 1 == X */
1167 log.correct_info_message("Folding Math_Cx::Value to constant (1)");
1168 log.correct_info_message("Folding Math_xC::Value to socket Attribute::Fac.");
1169 log.invalid_info_message("Folding clamp::");
1170}
1171
1172/*
1173 * Tests: Vector Math with all constant inputs.
1174 */
1175TEST_F(RenderGraph, constant_fold_vector_math)
1176{
1177 builder
1178 .add_node(ShaderNodeBuilder<VectorMathNode>(graph, "VectorMath")
1179 .set_param("math_type", NODE_VECTOR_MATH_SUBTRACT)
1180 .set("Vector1", make_float3(1.3f, 0.5f, 0.7f))
1181 .set("Vector2", make_float3(-1.7f, 0.5f, 0.7f)))
1182 .output_color("VectorMath::Vector");
1183
1184 graph.finalize(scene.get());
1185
1186 log.correct_info_message("Folding VectorMath::Vector to constant (3, 0, 0).");
1187}
1188
1189/*
1190 * Graph for testing partial folds of Vector Math with one constant argument.
1191 * Includes 2 tests: constant on each side.
1192 */
1193static void build_vecmath_partial_test_graph(ShaderGraphBuilder &builder,
1194 NodeVectorMathType type,
1195 const float3 constval)
1196{
1197 builder
1198 .add_attribute("Attribute")
1199 /* constant on the left */
1200 .add_node(ShaderNodeBuilder<VectorMathNode>(builder.graph(), "Math_Cx")
1201 .set_param("math_type", type)
1202 .set("Vector1", constval))
1203 .add_connection("Attribute::Vector", "Math_Cx::Vector2")
1204 /* constant on the right */
1205 .add_node(ShaderNodeBuilder<VectorMathNode>(builder.graph(), "Math_xC")
1206 .set_param("math_type", type)
1207 .set("Vector2", constval))
1208 .add_connection("Attribute::Vector", "Math_xC::Vector1")
1209 /* output sum */
1210 .add_node(ShaderNodeBuilder<VectorMathNode>(builder.graph(), "Out")
1211 .set_param("math_type", NODE_VECTOR_MATH_ADD))
1212 .add_connection("Math_Cx::Vector", "Out::Vector1")
1213 .add_connection("Math_xC::Vector", "Out::Vector2")
1214 .output_color("Out::Vector");
1215}
1216
1217/*
1218 * Tests: partial folding for Vector Math Add with known 0.
1219 */
1220TEST_F(RenderGraph, constant_fold_part_vecmath_add_0)
1221{
1223 graph.finalize(scene.get());
1224
1225 /* X + 0 == 0 + X == X */
1226 log.correct_info_message("Folding Math_Cx::Vector to socket Attribute::Vector.");
1227 log.correct_info_message("Folding Math_xC::Vector to socket Attribute::Vector.");
1228 log.invalid_info_message("Folding Out::");
1229}
1230
1231/*
1232 * Tests: partial folding for Vector Math Subtract with known 0.
1233 */
1234TEST_F(RenderGraph, constant_fold_part_vecmath_sub_0)
1235{
1237 graph.finalize(scene.get());
1238
1239 /* X - 0 == X */
1240 log.invalid_info_message("Folding Math_Cx::");
1241 log.correct_info_message("Folding Math_xC::Vector to socket Attribute::Vector.");
1242 log.invalid_info_message("Folding Out::");
1243}
1244
1245/*
1246 * Tests: partial folding for Vector Math Cross Product with known 0.
1247 */
1248TEST_F(RenderGraph, constant_fold_part_vecmath_cross_0)
1249{
1251 graph.finalize(scene.get());
1252
1253 /* X * 0 == 0 * X == X */
1254 log.correct_info_message("Folding Math_Cx::Vector to constant (0, 0, 0).");
1255 log.correct_info_message("Folding Math_xC::Vector to constant (0, 0, 0).");
1256 log.correct_info_message("Folding Out::Vector to constant (0, 0, 0).");
1257 log.correct_info_message("Discarding closure EmissionNode.");
1258}
1259
1260/*
1261 * Tests: Bump with no height input folded to Normal input.
1262 */
1263TEST_F(RenderGraph, constant_fold_bump)
1264{
1265 builder.add_node(ShaderNodeBuilder<GeometryNode>(graph, "Geometry1"))
1266 .add_node(ShaderNodeBuilder<BumpNode>(graph, "Bump"))
1267 .add_connection("Geometry1::Normal", "Bump::Normal")
1268 .output_color("Bump::Normal");
1269
1270 graph.finalize(scene.get());
1271
1272 log.correct_info_message("Folding Bump::Normal to socket Geometry1::Normal.");
1273}
1274
1275/*
1276 * Tests: Bump with no inputs folded to Geometry::Normal.
1277 */
1278TEST_F(RenderGraph, constant_fold_bump_no_input)
1279{
1280 builder.add_node(ShaderNodeBuilder<BumpNode>(graph, "Bump")).output_color("Bump::Normal");
1281
1282 graph.finalize(scene.get());
1283
1284 log.correct_info_message("Folding Bump::Normal to socket geometry::Normal.");
1285}
1286
1287template<class T> void init_test_curve(array<T> &buffer, T start, T end, const int steps)
1288{
1289 buffer.resize(steps);
1290
1291 for (int i = 0; i < steps; i++) {
1292 buffer[i] = mix(start, end, float(i) / (steps - 1));
1293 }
1294}
1295
1296/*
1297 * Tests:
1298 * - Folding of RGB Curves with all constant inputs.
1299 */
1300TEST_F(RenderGraph, constant_fold_rgb_curves)
1301{
1302 array<float3> curve;
1303 init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
1304
1305 builder
1306 .add_node(ShaderNodeBuilder<RGBCurvesNode>(graph, "Curves")
1307 .set_param("curves", curve)
1308 .set_param("min_x", 0.1f)
1309 .set_param("max_x", 0.9f)
1310 .set("Fac", 0.5f)
1311 .set("Color", make_float3(0.3f, 0.5f, 0.7f)))
1312 .output_color("Curves::Color");
1313
1314 graph.finalize(scene.get());
1315
1316 log.correct_info_message("Folding Curves::Color to constant (0.275, 0.5, 0.475).");
1317}
1318
1319/*
1320 * Tests:
1321 * - Folding of RGB Curves with zero Fac.
1322 */
1323TEST_F(RenderGraph, constant_fold_rgb_curves_fac_0)
1324{
1325 array<float3> curve;
1326 init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
1327
1328 builder.add_attribute("Attribute")
1329 .add_node(ShaderNodeBuilder<RGBCurvesNode>(graph, "Curves")
1330 .set_param("curves", curve)
1331 .set_param("min_x", 0.1f)
1332 .set_param("max_x", 0.9f)
1333 .set("Fac", 0.0f))
1334 .add_connection("Attribute::Color", "Curves::Color")
1335 .output_color("Curves::Color");
1336
1337 graph.finalize(scene.get());
1338
1339 log.correct_info_message("Folding Curves::Color to socket Attribute::Color.");
1340}
1341
1342/*
1343 * Tests:
1344 * - Folding of RGB Curves with zero Fac and all constant inputs.
1345 */
1346TEST_F(RenderGraph, constant_fold_rgb_curves_fac_0_const)
1347{
1348 array<float3> curve;
1349 init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
1350
1351 builder
1352 .add_node(ShaderNodeBuilder<RGBCurvesNode>(graph, "Curves")
1353 .set_param("curves", curve)
1354 .set_param("min_x", 0.1f)
1355 .set_param("max_x", 0.9f)
1356 .set("Fac", 0.0f)
1357 .set("Color", make_float3(0.3f, 0.5f, 0.7f)))
1358 .output_color("Curves::Color");
1359
1360 graph.finalize(scene.get());
1361
1362 log.correct_info_message("Folding Curves::Color to constant (0.3, 0.5, 0.7).");
1363}
1364
1365/*
1366 * Tests:
1367 * - Folding of Vector Curves with all constant inputs.
1368 */
1369TEST_F(RenderGraph, constant_fold_vector_curves)
1370{
1371 array<float3> curve;
1372 init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
1373
1374 builder
1375 .add_node(ShaderNodeBuilder<VectorCurvesNode>(graph, "Curves")
1376 .set_param("curves", curve)
1377 .set_param("min_x", 0.1f)
1378 .set_param("max_x", 0.9f)
1379 .set("Fac", 0.5f)
1380 .set("Vector", make_float3(0.3f, 0.5f, 0.7f)))
1381 .output_color("Curves::Vector");
1382
1383 graph.finalize(scene.get());
1384
1385 log.correct_info_message("Folding Curves::Vector to constant (0.275, 0.5, 0.475).");
1386}
1387
1388/*
1389 * Tests:
1390 * - Folding of Vector Curves with zero Fac.
1391 */
1392TEST_F(RenderGraph, constant_fold_vector_curves_fac_0)
1393{
1394 array<float3> curve;
1395 init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257);
1396
1397 builder.add_attribute("Attribute")
1398 .add_node(ShaderNodeBuilder<VectorCurvesNode>(graph, "Curves")
1399 .set_param("curves", curve)
1400 .set_param("min_x", 0.1f)
1401 .set_param("max_x", 0.9f)
1402 .set("Fac", 0.0f))
1403 .add_connection("Attribute::Vector", "Curves::Vector")
1404 .output_color("Curves::Vector");
1405
1406 graph.finalize(scene.get());
1407
1408 log.correct_info_message("Folding Curves::Vector to socket Attribute::Vector.");
1409}
1410
1411/*
1412 * Tests:
1413 * - Folding of Color Ramp with all constant inputs.
1414 */
1415TEST_F(RenderGraph, constant_fold_rgb_ramp)
1416{
1417 array<float3> curve;
1418 array<float> alpha;
1419 init_test_curve(curve, make_float3(0.0f, 0.25f, 0.5f), make_float3(0.25f, 0.5f, 0.75f), 9);
1420 init_test_curve(alpha, 0.75f, 1.0f, 9);
1421
1422 builder
1423 .add_node(ShaderNodeBuilder<RGBRampNode>(graph, "Ramp")
1424 .set_param("ramp", curve)
1425 .set_param("ramp_alpha", alpha)
1426 .set_param("interpolate", true)
1427 .set("Fac", 0.56f))
1428 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix").set_param("mix_type", NODE_MIX_ADD))
1429 .add_connection("Ramp::Color", "Mix::Color1")
1430 .add_connection("Ramp::Alpha", "Mix::Color2")
1431 .output_color("Mix::Color");
1432
1433 graph.finalize(scene.get());
1434
1435 log.correct_info_message("Folding Ramp::Color to constant (0.14, 0.39, 0.64).");
1436 log.correct_info_message("Folding Ramp::Alpha to constant (0.89).");
1437}
1438
1439/*
1440 * Tests:
1441 * - Folding of Color Ramp with all constant inputs (interpolate false).
1442 */
1443TEST_F(RenderGraph, constant_fold_rgb_ramp_flat)
1444{
1445 array<float3> curve;
1446 array<float> alpha;
1447 init_test_curve(curve, make_float3(0.0f, 0.25f, 0.5f), make_float3(0.25f, 0.5f, 0.75f), 9);
1448 init_test_curve(alpha, 0.75f, 1.0f, 9);
1449
1450 builder
1451 .add_node(ShaderNodeBuilder<RGBRampNode>(graph, "Ramp")
1452 .set_param("ramp", curve)
1453 .set_param("ramp_alpha", alpha)
1454 .set_param("interpolate", false)
1455 .set("Fac", 0.56f))
1456 .add_node(ShaderNodeBuilder<MixNode>(graph, "Mix").set_param("mix_type", NODE_MIX_ADD))
1457 .add_connection("Ramp::Color", "Mix::Color1")
1458 .add_connection("Ramp::Alpha", "Mix::Color2")
1459 .output_color("Mix::Color");
1460
1461 graph.finalize(scene.get());
1462
1463 log.correct_info_message("Folding Ramp::Color to constant (0.125, 0.375, 0.625).");
1464 log.correct_info_message("Folding Ramp::Alpha to constant (0.875).");
1465}
1466
1467/*
1468 * Tests:
1469 * - Folding of redundant conversion of float to color to float.
1470 */
1471TEST_F(RenderGraph, constant_fold_convert_float_color_float)
1472{
1473 builder.add_attribute("Attribute")
1474 .add_node(ShaderNodeBuilder<InvertNode>(graph, "Invert").set("Fac", 0.0f))
1475 .add_connection("Attribute::Fac", "Invert::Color")
1476 .output_value("Invert::Color");
1477
1478 graph.finalize(scene.get());
1479
1480 log.correct_info_message("Folding Invert::Color to socket convert_float_to_color::value_color.");
1481 log.correct_info_message(
1482 "Folding convert_color_to_float::value_float to socket Attribute::Fac.");
1483}
1484
1485/*
1486 * Tests:
1487 * - Folding of redundant conversion of color to vector to color.
1488 */
1489TEST_F(RenderGraph, constant_fold_convert_color_vector_color)
1490{
1491 builder.add_attribute("Attribute")
1492 .add_node(ShaderNodeBuilder<VectorMathNode>(graph, "VecAdd")
1493 .set_param("math_type", NODE_VECTOR_MATH_ADD)
1494 .set("Vector2", make_float3(0, 0, 0)))
1495 .add_connection("Attribute::Color", "VecAdd::Vector1")
1496 .output_color("VecAdd::Vector");
1497
1498 graph.finalize(scene.get());
1499
1500 log.correct_info_message(
1501 "Folding VecAdd::Vector to socket convert_color_to_vector::value_vector.");
1502 log.correct_info_message(
1503 "Folding convert_vector_to_color::value_color to socket Attribute::Color.");
1504}
1505
1506/*
1507 * Tests:
1508 * - NOT folding conversion of color to float to color.
1509 */
1510TEST_F(RenderGraph, constant_fold_convert_color_float_color)
1511{
1512 builder.add_attribute("Attribute")
1513 .add_node(ShaderNodeBuilder<MathNode>(graph, "MathAdd")
1514 .set_param("math_type", NODE_MATH_ADD)
1515 .set("Value2", 0.0f))
1516 .add_connection("Attribute::Color", "MathAdd::Value1")
1517 .output_color("MathAdd::Value");
1518
1519 graph.finalize(scene.get());
1520
1521 log.correct_info_message(
1522 "Folding MathAdd::Value to socket convert_color_to_float::value_float.");
1523 log.invalid_info_message("Folding convert_float_to_color::");
1524}
1525
1526/*
1527 * Tests:
1528 * - Stochastic sampling with math multiply node.
1529 */
1530TEST_F(RenderGraph, stochastic_sample_math_multiply)
1531{
1532 builder.add_attribute("Attribute")
1533 .add_node(ShaderNodeBuilder<MathNode>(graph, "MathMultiply")
1534 .set_param("math_type", NODE_MATH_MULTIPLY))
1535 .add_node(ShaderNodeBuilder<ScatterVolumeNode>(graph, "ScatterVolume"))
1536 .add_connection("Attribute::Fac", "MathMultiply::Value1")
1537 .add_connection("MathMultiply::Value", "ScatterVolume::Density")
1538 .output_volume_closure("ScatterVolume::Volume");
1539
1540 graph.finalize(scene.get());
1541
1542 log.correct_info_message("Volume attribute node Attribute uses stochastic sampling");
1543}
1544
1545/*
1546 * Tests:
1547 * - No stochastic sampling with math power node.
1548 */
1549TEST_F(RenderGraph, not_stochastic_sample_math_power)
1550{
1551 builder.add_attribute("Attribute")
1552 .add_node(
1553 ShaderNodeBuilder<MathNode>(graph, "MathPower").set_param("math_type", NODE_MATH_POWER))
1554 .add_node(ShaderNodeBuilder<ScatterVolumeNode>(graph, "ScatterVolume"))
1555 .add_connection("Attribute::Fac", "MathPower::Value1")
1556 .add_connection("MathPower::Value", "ScatterVolume::Density")
1557 .output_volume_closure("ScatterVolume::Volume");
1558
1559 graph.finalize(scene.get());
1560
1561 log.invalid_info_message("Volume attribute node Attribute uses stochastic sampling");
1562}
1563
1564/*
1565 * Tests:
1566 * - Stochastic sampling temperature with map range, principled volume and mix closure.
1567 */
1568TEST_F(RenderGraph, stochastic_sample_principled_volume_mix)
1569{
1570 builder.add_attribute("Attribute")
1571 .add_node(ShaderNodeBuilder<MapRangeNode>(graph, "MapRange"))
1572 .add_node(ShaderNodeBuilder<MixClosureNode>(graph, "MixClosure").set("Fac", 0.5f))
1573 .add_node(ShaderNodeBuilder<PrincipledVolumeNode>(graph, "PrincipledVolume1"))
1574 .add_node(ShaderNodeBuilder<PrincipledVolumeNode>(graph, "PrincipledVolume2"))
1575 .add_connection("Attribute::Color", "MapRange::Value")
1576 .add_connection("MapRange::Result", "PrincipledVolume1::Temperature")
1577 .add_connection("Attribute::Fac", "PrincipledVolume2::Density")
1578 .add_connection("PrincipledVolume1::Volume", "MixClosure::Closure1")
1579 .add_connection("PrincipledVolume2::Volume", "MixClosure::Closure2")
1580 .output_volume_closure("MixClosure::Closure");
1581
1582 graph.finalize(scene.get());
1583
1584 log.correct_info_message("Volume attribute node Attribute uses stochastic sampling");
1585}
1586
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
@ NODE_VECTOR_MATH_CROSS_PRODUCT
@ NODE_VECTOR_MATH_ADD
@ NODE_VECTOR_MATH_SUBTRACT
@ NODE_MATH_DIVIDE
@ NODE_MATH_POWER
@ NODE_MATH_ADD
@ NODE_MATH_MULTIPLY
@ NODE_MATH_SUBTRACT
@ NODE_COMBSEP_COLOR_RGB
@ NODE_COMBSEP_COLOR_HSV
volatile int lock
ShaderGraphBuilder & add_connection(const string &from, const string &to)
ShaderGraphBuilder & output_value(const string &from)
ShaderGraphBuilder & output_closure(const string &from)
ShaderGraphBuilder & add_node(const T &node)
ShaderGraphBuilder & output_volume_closure(const string &from)
ShaderGraphBuilder & add_attribute(const string &name)
ShaderGraphBuilder & output_color(const string &from)
ShaderNodeBuilder & set(const string &input_name, V value)
ShaderNodeBuilder & set_param(const string &input_name, V value)
ShaderNodeBuilder(ShaderGraph &graph, const string &name)
static void init_fallback_config()
static unique_ptr< Device > create(const DeviceInfo &info, Stats &stats, Profiler &profiler, bool headless)
ShaderGraphBuilder builder
unique_ptr< Scene > scene
unique_ptr< Device > device_cpu
T * create_node(Args &&...args)
void set(const float f)
ShaderInput * input(const char *name)
ShaderOutput * output(const char *name)
T * resize(const size_t newsize)
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
ThreadMutex mutex
#define log
NodeMathType
@ NODE_MIX_DIV
@ NODE_MIX_LIGHT
@ NODE_MIX_MUL
@ NODE_MIX_BURN
@ NODE_MIX_SUB
@ NODE_MIX_BLEND
@ NODE_MIX_DODGE
@ NODE_MIX_ADD
NodeVectorMathType
void log_init(const LogFunction func)
Definition log.cpp:72
void log_level_set(const LogLevel level)
Definition log.cpp:78
#define LOG_FATAL
Definition log.h:99
LogLevel
Definition log.h:17
@ LOG_LEVEL_FATAL
Definition log.h:18
@ LOG_LEVEL_TRACE
Definition log.h:27
ccl_device_inline float3 one_float3()
Definition math_float3.h:26
CCL_NAMESPACE_BEGIN ccl_device_inline float3 zero_float3()
Definition math_float3.h:17
#define T
const char * name
#define mix
void init_test_curve(array< T > &buffer, T start, T end, const int steps)
TEST_F(RenderGraph, deduplicate_deep)
static void build_math_partial_test_graph(ShaderGraphBuilder &builder, NodeMathType type, const float constval)
static void build_mix_partial_test_graph(ShaderGraphBuilder &builder, NodeMix type, const float3 constval)
static void build_vecmath_partial_test_graph(ShaderGraphBuilder &builder, NodeVectorMathType type, const float3 constval)
void string_split(vector< string > &tokens, const string &str, const string &separators, bool skip_empty_tokens)
Definition string.cpp:70
i
Definition text_draw.cc:230
std::mutex thread_mutex
Definition thread.h:27
std::unique_lock< std::mutex > thread_scoped_lock
Definition thread.h:28
CCL_NAMESPACE_BEGIN struct Window V