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