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