/* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ #include "testing/mock_log.h" #include "testing/testing.h" #include "device/device.h" #include "scene/scene.h" #include "scene/shader_graph.h" #include "scene/shader_nodes.h" #include "util/array.h" #include "util/log.h" #include "util/stats.h" #include "util/string.h" #include "util/vector.h" using testing::_; using testing::AnyNumber; using testing::HasSubstr; using testing::ScopedMockLog; CCL_NAMESPACE_BEGIN namespace { template class ShaderNodeBuilder { public: ShaderNodeBuilder(ShaderGraph &graph, const string &name) : name_(name) { node_ = graph.create_node(); node_->name = name; } const string &name() const { return name_; } ShaderNode *node() const { return node_; } template ShaderNodeBuilder &set(const string &input_name, V value) { ShaderInput *input_socket = node_->input(input_name.c_str()); EXPECT_NE((void *)NULL, input_socket); input_socket->set(value); return *this; } template ShaderNodeBuilder &set_param(const string &input_name, V value) { const SocketType *input_socket = node_->type->find_input(ustring(input_name.c_str())); EXPECT_NE((void *)NULL, input_socket); node_->set(*input_socket, value); return *this; } protected: string name_; ShaderNode *node_; }; class ShaderGraphBuilder { public: ShaderGraphBuilder(ShaderGraph *graph) : graph_(graph) { node_map_["Output"] = graph->output(); } ShaderNode *find_node(const string &name) { map::iterator it = node_map_.find(name); if (it == node_map_.end()) { return NULL; } return it->second; } template ShaderGraphBuilder &add_node(const T &node) { EXPECT_EQ(find_node(node.name()), (void *)NULL); graph_->add(node.node()); node_map_[node.name()] = node.node(); return *this; } ShaderGraphBuilder &add_connection(const string &from, const string &to) { vector tokens_from, tokens_to; string_split(tokens_from, from, "::"); string_split(tokens_to, to, "::"); EXPECT_EQ(tokens_from.size(), 2); EXPECT_EQ(tokens_to.size(), 2); ShaderNode *node_from = find_node(tokens_from[0]), *node_to = find_node(tokens_to[0]); EXPECT_NE((void *)NULL, node_from); EXPECT_NE((void *)NULL, node_to); EXPECT_NE(node_from, node_to); ShaderOutput *socket_from = node_from->output(tokens_from[1].c_str()); ShaderInput *socket_to = node_to->input(tokens_to[1].c_str()); EXPECT_NE((void *)NULL, socket_from); EXPECT_NE((void *)NULL, socket_to); graph_->connect(socket_from, socket_to); return *this; } /* Common input/output boilerplate. */ ShaderGraphBuilder &add_attribute(const string &name) { return (*this).add_node( ShaderNodeBuilder(*graph_, name).set_param("attribute", ustring(name))); } ShaderGraphBuilder &output_closure(const string &from) { return (*this).add_connection(from, "Output::Surface"); } ShaderGraphBuilder &output_color(const string &from) { return (*this) .add_node(ShaderNodeBuilder(*graph_, "EmissionNode")) .add_connection(from, "EmissionNode::Color") .output_closure("EmissionNode::Emission"); } ShaderGraphBuilder &output_value(const string &from) { return (*this) .add_node(ShaderNodeBuilder(*graph_, "EmissionNode")) .add_connection(from, "EmissionNode::Strength") .output_closure("EmissionNode::Emission"); } ShaderGraph &graph() { return *graph_; } protected: ShaderGraph *graph_; map node_map_; }; } // namespace class RenderGraph : public testing::Test { protected: ScopedMockLog log; Stats stats; Profiler profiler; DeviceInfo device_info; Device *device_cpu; SceneParams scene_params; Scene *scene; ShaderGraph graph; ShaderGraphBuilder builder; RenderGraph() : testing::Test(), builder(&graph) { } virtual void SetUp() { util_logging_start(); util_logging_verbosity_set(5); device_cpu = Device::create(device_info, stats, profiler); scene = new Scene(scene_params, device_cpu); } virtual void TearDown() { delete scene; delete device_cpu; } }; #define EXPECT_ANY_MESSAGE(log) EXPECT_CALL(log, Log(_, _, _)).Times(AnyNumber()); #define CORRECT_INFO_MESSAGE(log, message) \ EXPECT_CALL(log, Log(google::INFO, _, HasSubstr(message))); #define INVALID_INFO_MESSAGE(log, message) \ EXPECT_CALL(log, Log(google::INFO, _, HasSubstr(message))).Times(0); /* * Test deduplication of nodes that have inputs, some of them folded. */ TEST_F(RenderGraph, deduplicate_deep) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Value1::Value to constant (0.8)."); CORRECT_INFO_MESSAGE(log, "Folding Value2::Value to constant (0.8)."); CORRECT_INFO_MESSAGE(log, "Deduplicated 2 nodes."); builder.add_node(ShaderNodeBuilder(graph, "Geometry1")) .add_node(ShaderNodeBuilder(graph, "Geometry2")) .add_node(ShaderNodeBuilder(graph, "Value1").set_param("value", 0.8f)) .add_node(ShaderNodeBuilder(graph, "Value2").set_param("value", 0.8f)) .add_node(ShaderNodeBuilder(graph, "Noise1")) .add_node(ShaderNodeBuilder(graph, "Noise2")) .add_node(ShaderNodeBuilder(graph, "Mix") .set_param("mix_type", NODE_MIX_BLEND) .set("Fac", 0.5f)) .add_connection("Geometry1::Parametric", "Noise1::Vector") .add_connection("Value1::Value", "Noise1::Scale") .add_connection("Noise1::Color", "Mix::Color1") .add_connection("Geometry2::Parametric", "Noise2::Vector") .add_connection("Value2::Value", "Noise2::Scale") .add_connection("Noise2::Color", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); EXPECT_EQ(graph.nodes.size(), 5); } /* * Test RGB to BW node. */ TEST_F(RenderGraph, constant_fold_rgb_to_bw) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding RGBToBWNodeNode::Val to constant (0.8)."); CORRECT_INFO_MESSAGE(log, "Folding convert_float_to_color::value_color to constant (0.8, 0.8, 0.8)."); builder .add_node(ShaderNodeBuilder(graph, "RGBToBWNodeNode") .set("Color", make_float3(0.8f, 0.8f, 0.8f))) .output_color("RGBToBWNodeNode::Val"); graph.finalize(scene); } /* * Tests: * - folding of Emission nodes that don't emit to nothing. */ TEST_F(RenderGraph, constant_fold_emission1) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Discarding closure Emission."); builder.add_node(ShaderNodeBuilder(graph, "Emission").set("Color", zero_float3())) .output_closure("Emission::Emission"); graph.finalize(scene); } TEST_F(RenderGraph, constant_fold_emission2) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Discarding closure Emission."); builder.add_node(ShaderNodeBuilder(graph, "Emission").set("Strength", 0.0f)) .output_closure("Emission::Emission"); graph.finalize(scene); } /* * Tests: * - folding of Background nodes that don't emit to nothing. */ TEST_F(RenderGraph, constant_fold_background1) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Discarding closure Background."); builder .add_node(ShaderNodeBuilder(graph, "Background").set("Color", zero_float3())) .output_closure("Background::Background"); graph.finalize(scene); } TEST_F(RenderGraph, constant_fold_background2) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Discarding closure Background."); builder.add_node(ShaderNodeBuilder(graph, "Background").set("Strength", 0.0f)) .output_closure("Background::Background"); graph.finalize(scene); } /* * Tests: * - Folding of Add Closure with only one input. */ TEST_F(RenderGraph, constant_fold_shader_add) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding AddClosure1::Closure to socket Diffuse::BSDF."); CORRECT_INFO_MESSAGE(log, "Folding AddClosure2::Closure to socket Diffuse::BSDF."); INVALID_INFO_MESSAGE(log, "Folding AddClosure3"); builder.add_node(ShaderNodeBuilder(graph, "Diffuse")) .add_node(ShaderNodeBuilder(graph, "AddClosure1")) .add_node(ShaderNodeBuilder(graph, "AddClosure2")) .add_node(ShaderNodeBuilder(graph, "AddClosure3")) .add_connection("Diffuse::BSDF", "AddClosure1::Closure1") .add_connection("Diffuse::BSDF", "AddClosure2::Closure2") .add_connection("AddClosure1::Closure", "AddClosure3::Closure1") .add_connection("AddClosure2::Closure", "AddClosure3::Closure2") .output_closure("AddClosure3::Closure"); graph.finalize(scene); } /* * Tests: * - Folding of Mix Closure with 0 or 1 fac. * - Folding of Mix Closure with both inputs folded to the same node. */ TEST_F(RenderGraph, constant_fold_shader_mix) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding MixClosure1::Closure to socket Diffuse::BSDF."); CORRECT_INFO_MESSAGE(log, "Folding MixClosure2::Closure to socket Diffuse::BSDF."); CORRECT_INFO_MESSAGE(log, "Folding MixClosure3::Closure to socket Diffuse::BSDF."); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "Diffuse")) /* choose left */ .add_node(ShaderNodeBuilder(graph, "MixClosure1").set("Fac", 0.0f)) .add_connection("Diffuse::BSDF", "MixClosure1::Closure1") /* choose right */ .add_node(ShaderNodeBuilder(graph, "MixClosure2").set("Fac", 1.0f)) .add_connection("Diffuse::BSDF", "MixClosure2::Closure2") /* both inputs folded the same */ .add_node(ShaderNodeBuilder(graph, "MixClosure3")) .add_connection("Attribute::Fac", "MixClosure3::Fac") .add_connection("MixClosure1::Closure", "MixClosure3::Closure1") .add_connection("MixClosure2::Closure", "MixClosure3::Closure2") .output_closure("MixClosure3::Closure"); graph.finalize(scene); } /* * Tests: * - Folding of Invert with all constant inputs. */ TEST_F(RenderGraph, constant_fold_invert) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Invert::Color to constant (0.68, 0.5, 0.32)."); builder .add_node(ShaderNodeBuilder(graph, "Invert") .set("Fac", 0.8f) .set("Color", make_float3(0.2f, 0.5f, 0.8f))) .output_color("Invert::Color"); graph.finalize(scene); } /* * Tests: * - Folding of Invert with zero Fac. */ TEST_F(RenderGraph, constant_fold_invert_fac_0) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Invert::Color to socket Attribute::Color."); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "Invert").set("Fac", 0.0f)) .add_connection("Attribute::Color", "Invert::Color") .output_color("Invert::Color"); graph.finalize(scene); } /* * Tests: * - Folding of Invert with zero Fac and constant input. */ TEST_F(RenderGraph, constant_fold_invert_fac_0_const) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Invert::Color to constant (0.2, 0.5, 0.8)."); builder .add_node(ShaderNodeBuilder(graph, "Invert") .set("Fac", 0.0f) .set("Color", make_float3(0.2f, 0.5f, 0.8f))) .output_color("Invert::Color"); graph.finalize(scene); } /* * Tests: * - Folding of MixRGB Add with all constant inputs (clamp false). */ TEST_F(RenderGraph, constant_fold_mix_add) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding MixAdd::Color to constant (0.62, 1.14, 1.42)."); builder .add_node(ShaderNodeBuilder(graph, "MixAdd") .set_param("mix_type", NODE_MIX_ADD) .set_param("use_clamp", false) .set("Fac", 0.8f) .set("Color1", make_float3(0.3f, 0.5f, 0.7f)) .set("Color2", make_float3(0.4f, 0.8f, 0.9f))) .output_color("MixAdd::Color"); graph.finalize(scene); } /* * Tests: * - Folding of MixRGB Add with all constant inputs (clamp true). */ TEST_F(RenderGraph, constant_fold_mix_add_clamp) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding MixAdd::Color to constant (0.62, 1, 1)."); builder .add_node(ShaderNodeBuilder(graph, "MixAdd") .set_param("mix_type", NODE_MIX_ADD) .set_param("use_clamp", true) .set("Fac", 0.8f) .set("Color1", make_float3(0.3f, 0.5f, 0.7f)) .set("Color2", make_float3(0.4f, 0.8f, 0.9f))) .output_color("MixAdd::Color"); graph.finalize(scene); } /* * Tests: * - No folding on fac 0 for dodge. */ TEST_F(RenderGraph, constant_fold_part_mix_dodge_no_fac_0) { EXPECT_ANY_MESSAGE(log); INVALID_INFO_MESSAGE(log, "Folding "); builder.add_attribute("Attribute1") .add_attribute("Attribute2") .add_node(ShaderNodeBuilder(graph, "Mix") .set_param("mix_type", NODE_MIX_DODGE) .set_param("use_clamp", false) .set("Fac", 0.0f)) .add_connection("Attribute1::Color", "Mix::Color1") .add_connection("Attribute2::Color", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); } /* * Tests: * - No folding on fac 0 for light. */ TEST_F(RenderGraph, constant_fold_part_mix_light_no_fac_0) { EXPECT_ANY_MESSAGE(log); INVALID_INFO_MESSAGE(log, "Folding "); builder.add_attribute("Attribute1") .add_attribute("Attribute2") .add_node(ShaderNodeBuilder(graph, "Mix") .set_param("mix_type", NODE_MIX_LIGHT) .set_param("use_clamp", false) .set("Fac", 0.0f)) .add_connection("Attribute1::Color", "Mix::Color1") .add_connection("Attribute2::Color", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); } /* * Tests: * - No folding on fac 0 for burn. */ TEST_F(RenderGraph, constant_fold_part_mix_burn_no_fac_0) { EXPECT_ANY_MESSAGE(log); INVALID_INFO_MESSAGE(log, "Folding "); builder.add_attribute("Attribute1") .add_attribute("Attribute2") .add_node(ShaderNodeBuilder(graph, "Mix") .set_param("mix_type", NODE_MIX_BURN) .set_param("use_clamp", false) .set("Fac", 0.0f)) .add_connection("Attribute1::Color", "Mix::Color1") .add_connection("Attribute2::Color", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); } /* * Tests: * - No folding on fac 0 for clamped blend. */ TEST_F(RenderGraph, constant_fold_part_mix_blend_clamped_no_fac_0) { EXPECT_ANY_MESSAGE(log); INVALID_INFO_MESSAGE(log, "Folding "); builder.add_attribute("Attribute1") .add_attribute("Attribute2") .add_node(ShaderNodeBuilder(graph, "Mix") .set_param("mix_type", NODE_MIX_BLEND) .set_param("use_clamp", true) .set("Fac", 0.0f)) .add_connection("Attribute1::Color", "Mix::Color1") .add_connection("Attribute2::Color", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); } /* * Tests: * - Folding of Mix with 0 or 1 Fac. * - Folding of Mix with both inputs folded to the same node. */ TEST_F(RenderGraph, constant_fold_part_mix_blend) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding MixBlend1::Color to socket Attribute1::Color."); CORRECT_INFO_MESSAGE(log, "Folding MixBlend2::Color to socket Attribute1::Color."); CORRECT_INFO_MESSAGE(log, "Folding MixBlend3::Color to socket Attribute1::Color."); builder.add_attribute("Attribute1") .add_attribute("Attribute2") /* choose left */ .add_node(ShaderNodeBuilder(graph, "MixBlend1") .set_param("mix_type", NODE_MIX_BLEND) .set_param("use_clamp", false) .set("Fac", 0.0f)) .add_connection("Attribute1::Color", "MixBlend1::Color1") .add_connection("Attribute2::Color", "MixBlend1::Color2") /* choose right */ .add_node(ShaderNodeBuilder(graph, "MixBlend2") .set_param("mix_type", NODE_MIX_BLEND) .set_param("use_clamp", false) .set("Fac", 1.0f)) .add_connection("Attribute1::Color", "MixBlend2::Color2") .add_connection("Attribute2::Color", "MixBlend2::Color1") /* both inputs folded to Attribute1 */ .add_node(ShaderNodeBuilder(graph, "MixBlend3") .set_param("mix_type", NODE_MIX_BLEND) .set_param("use_clamp", false)) .add_connection("Attribute1::Fac", "MixBlend3::Fac") .add_connection("MixBlend1::Color", "MixBlend3::Color1") .add_connection("MixBlend2::Color", "MixBlend3::Color2") .output_color("MixBlend3::Color"); graph.finalize(scene); } /* * Tests: * - NOT folding of MixRGB Sub with the same inputs and fac NOT 1. */ TEST_F(RenderGraph, constant_fold_part_mix_sub_same_fac_bad) { EXPECT_ANY_MESSAGE(log); INVALID_INFO_MESSAGE(log, "Folding Mix::"); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "Mix") .set_param("mix_type", NODE_MIX_SUB) .set_param("use_clamp", true) .set("Fac", 0.5f)) .add_connection("Attribute::Color", "Mix::Color1") .add_connection("Attribute::Color", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); } /* * Tests: * - Folding of MixRGB Sub with the same inputs and fac 1. */ TEST_F(RenderGraph, constant_fold_part_mix_sub_same_fac_1) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Mix::Color to constant (0, 0, 0)."); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "Mix") .set_param("mix_type", NODE_MIX_SUB) .set_param("use_clamp", true) .set("Fac", 1.0f)) .add_connection("Attribute::Color", "Mix::Color1") .add_connection("Attribute::Color", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); } /* * Graph for testing partial folds of MixRGB with one constant argument. * Includes 4 tests: constant on each side with fac either unknown or 1. */ static void build_mix_partial_test_graph(ShaderGraphBuilder &builder, NodeMix type, float3 constval) { builder .add_attribute("Attribute") /* constant on the left */ .add_node(ShaderNodeBuilder(builder.graph(), "Mix_Cx_Fx") .set_param("mix_type", type) .set_param("use_clamp", false) .set("Color1", constval)) .add_node(ShaderNodeBuilder(builder.graph(), "Mix_Cx_F1") .set_param("mix_type", type) .set_param("use_clamp", false) .set("Color1", constval) .set("Fac", 1.0f)) .add_connection("Attribute::Fac", "Mix_Cx_Fx::Fac") .add_connection("Attribute::Color", "Mix_Cx_Fx::Color2") .add_connection("Attribute::Color", "Mix_Cx_F1::Color2") /* constant on the right */ .add_node(ShaderNodeBuilder(builder.graph(), "Mix_xC_Fx") .set_param("mix_type", type) .set_param("use_clamp", false) .set("Color2", constval)) .add_node(ShaderNodeBuilder(builder.graph(), "Mix_xC_F1") .set_param("mix_type", type) .set_param("use_clamp", false) .set("Color2", constval) .set("Fac", 1.0f)) .add_connection("Attribute::Fac", "Mix_xC_Fx::Fac") .add_connection("Attribute::Color", "Mix_xC_Fx::Color1") .add_connection("Attribute::Color", "Mix_xC_F1::Color1") /* results of actual tests simply added up to connect to output */ .add_node(ShaderNodeBuilder(builder.graph(), "Out12") .set_param("mix_type", NODE_MIX_ADD) .set_param("use_clamp", true) .set("Fac", 1.0f)) .add_node(ShaderNodeBuilder(builder.graph(), "Out34") .set_param("mix_type", NODE_MIX_ADD) .set_param("use_clamp", true) .set("Fac", 1.0f)) .add_node(ShaderNodeBuilder(builder.graph(), "Out1234") .set_param("mix_type", NODE_MIX_ADD) .set_param("use_clamp", true) .set("Fac", 1.0f)) .add_connection("Mix_Cx_Fx::Color", "Out12::Color1") .add_connection("Mix_Cx_F1::Color", "Out12::Color2") .add_connection("Mix_xC_Fx::Color", "Out34::Color1") .add_connection("Mix_xC_F1::Color", "Out34::Color2") .add_connection("Out12::Color", "Out1234::Color1") .add_connection("Out34::Color", "Out1234::Color2") .output_color("Out1234::Color"); } /* * Tests: partial folding for RGB Add with known 0. */ TEST_F(RenderGraph, constant_fold_part_mix_add_0) { EXPECT_ANY_MESSAGE(log); /* 0 + X (fac 1) == X */ INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color"); CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color to socket Attribute::Color."); /* X + 0 (fac ?) == X */ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color to socket Attribute::Color."); CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to socket Attribute::Color."); INVALID_INFO_MESSAGE(log, "Folding Out"); build_mix_partial_test_graph(builder, NODE_MIX_ADD, make_float3(0, 0, 0)); graph.finalize(scene); } /* * Tests: partial folding for RGB Sub with known 0. */ TEST_F(RenderGraph, constant_fold_part_mix_sub_0) { EXPECT_ANY_MESSAGE(log); INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color"); INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color"); /* X - 0 (fac ?) == X */ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color to socket Attribute::Color."); CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to socket Attribute::Color."); INVALID_INFO_MESSAGE(log, "Folding Out"); build_mix_partial_test_graph(builder, NODE_MIX_SUB, make_float3(0, 0, 0)); graph.finalize(scene); } /* * Tests: partial folding for RGB Mul with known 1. */ TEST_F(RenderGraph, constant_fold_part_mix_mul_1) { EXPECT_ANY_MESSAGE(log); /* 1 * X (fac 1) == X */ INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color"); CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color to socket Attribute::Color."); /* X * 1 (fac ?) == X */ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color to socket Attribute::Color."); CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to socket Attribute::Color."); INVALID_INFO_MESSAGE(log, "Folding Out"); build_mix_partial_test_graph(builder, NODE_MIX_MUL, make_float3(1, 1, 1)); graph.finalize(scene); } /* * Tests: partial folding for RGB Div with known 1. */ TEST_F(RenderGraph, constant_fold_part_mix_div_1) { EXPECT_ANY_MESSAGE(log); INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color"); INVALID_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color"); /* X / 1 (fac ?) == X */ CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color to socket Attribute::Color."); CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to socket Attribute::Color."); INVALID_INFO_MESSAGE(log, "Folding Out"); build_mix_partial_test_graph(builder, NODE_MIX_DIV, make_float3(1, 1, 1)); graph.finalize(scene); } /* * Tests: partial folding for RGB Mul with known 0. */ TEST_F(RenderGraph, constant_fold_part_mix_mul_0) { EXPECT_ANY_MESSAGE(log); /* 0 * ? (fac ?) == 0 */ CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color to constant (0, 0, 0)."); CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color to constant (0, 0, 0)."); /* ? * 0 (fac 1) == 0 */ INVALID_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color"); CORRECT_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color to constant (0, 0, 0)."); CORRECT_INFO_MESSAGE(log, "Folding Out12::Color to constant (0, 0, 0)."); INVALID_INFO_MESSAGE(log, "Folding Out1234"); build_mix_partial_test_graph(builder, NODE_MIX_MUL, make_float3(0, 0, 0)); graph.finalize(scene); } /* * Tests: partial folding for RGB Div with known 0. */ TEST_F(RenderGraph, constant_fold_part_mix_div_0) { EXPECT_ANY_MESSAGE(log); /* 0 / ? (fac ?) == 0 */ CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_Fx::Color to constant (0, 0, 0)."); CORRECT_INFO_MESSAGE(log, "Folding Mix_Cx_F1::Color to constant (0, 0, 0)."); INVALID_INFO_MESSAGE(log, "Folding Mix_xC_Fx::Color"); INVALID_INFO_MESSAGE(log, "Folding Mix_xC_F1::Color"); CORRECT_INFO_MESSAGE(log, "Folding Out12::Color to constant (0, 0, 0)."); INVALID_INFO_MESSAGE(log, "Folding Out1234"); build_mix_partial_test_graph(builder, NODE_MIX_DIV, make_float3(0, 0, 0)); graph.finalize(scene); } /* * Tests: Separate/Combine RGB with all constant inputs. */ TEST_F(RenderGraph, constant_fold_separate_combine_rgb) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding SeparateRGB::R to constant (0.3)."); CORRECT_INFO_MESSAGE(log, "Folding SeparateRGB::G to constant (0.5)."); CORRECT_INFO_MESSAGE(log, "Folding SeparateRGB::B to constant (0.7)."); CORRECT_INFO_MESSAGE(log, "Folding CombineRGB::Image to constant (0.3, 0.5, 0.7)."); builder .add_node(ShaderNodeBuilder(graph, "SeparateRGB") .set("Image", make_float3(0.3f, 0.5f, 0.7f))) .add_node(ShaderNodeBuilder(graph, "CombineRGB")) .add_connection("SeparateRGB::R", "CombineRGB::R") .add_connection("SeparateRGB::G", "CombineRGB::G") .add_connection("SeparateRGB::B", "CombineRGB::B") .output_color("CombineRGB::Image"); graph.finalize(scene); } /* * Tests: Separate/Combine XYZ with all constant inputs. */ TEST_F(RenderGraph, constant_fold_separate_combine_xyz) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding SeparateXYZ::X to constant (0.3)."); CORRECT_INFO_MESSAGE(log, "Folding SeparateXYZ::Y to constant (0.5)."); CORRECT_INFO_MESSAGE(log, "Folding SeparateXYZ::Z to constant (0.7)."); CORRECT_INFO_MESSAGE(log, "Folding CombineXYZ::Vector to constant (0.3, 0.5, 0.7)."); CORRECT_INFO_MESSAGE( log, "Folding convert_vector_to_color::value_color to constant (0.3, 0.5, 0.7)."); builder .add_node(ShaderNodeBuilder(graph, "SeparateXYZ") .set("Vector", make_float3(0.3f, 0.5f, 0.7f))) .add_node(ShaderNodeBuilder(graph, "CombineXYZ")) .add_connection("SeparateXYZ::X", "CombineXYZ::X") .add_connection("SeparateXYZ::Y", "CombineXYZ::Y") .add_connection("SeparateXYZ::Z", "CombineXYZ::Z") .output_color("CombineXYZ::Vector"); graph.finalize(scene); } /* * Tests: Separate/Combine HSV with all constant inputs. */ TEST_F(RenderGraph, constant_fold_separate_combine_hsv) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding SeparateHSV::H to constant (0.583333)."); CORRECT_INFO_MESSAGE(log, "Folding SeparateHSV::S to constant (0.571429)."); CORRECT_INFO_MESSAGE(log, "Folding SeparateHSV::V to constant (0.7)."); CORRECT_INFO_MESSAGE(log, "Folding CombineHSV::Color to constant (0.3, 0.5, 0.7)."); builder .add_node(ShaderNodeBuilder(graph, "SeparateHSV") .set("Color", make_float3(0.3f, 0.5f, 0.7f))) .add_node(ShaderNodeBuilder(graph, "CombineHSV")) .add_connection("SeparateHSV::H", "CombineHSV::H") .add_connection("SeparateHSV::S", "CombineHSV::S") .add_connection("SeparateHSV::V", "CombineHSV::V") .output_color("CombineHSV::Color"); graph.finalize(scene); } /* * Tests: Gamma with all constant inputs. */ TEST_F(RenderGraph, constant_fold_gamma) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Gamma::Color to constant (0.164317, 0.353553, 0.585662)."); builder .add_node(ShaderNodeBuilder(graph, "Gamma") .set("Color", make_float3(0.3f, 0.5f, 0.7f)) .set("Gamma", 1.5f)) .output_color("Gamma::Color"); graph.finalize(scene); } /* * Tests: Gamma with one constant 0 input. */ TEST_F(RenderGraph, constant_fold_gamma_part_0) { EXPECT_ANY_MESSAGE(log); INVALID_INFO_MESSAGE(log, "Folding Gamma_Cx::"); CORRECT_INFO_MESSAGE(log, "Folding Gamma_xC::Color to constant (1, 1, 1)."); builder .add_attribute("Attribute") /* constant on the left */ .add_node(ShaderNodeBuilder(graph, "Gamma_Cx").set("Color", zero_float3())) .add_connection("Attribute::Fac", "Gamma_Cx::Gamma") /* constant on the right */ .add_node(ShaderNodeBuilder(graph, "Gamma_xC").set("Gamma", 0.0f)) .add_connection("Attribute::Color", "Gamma_xC::Color") /* output sum */ .add_node(ShaderNodeBuilder(graph, "Out") .set_param("mix_type", NODE_MIX_ADD) .set_param("use_clamp", true) .set("Fac", 1.0f)) .add_connection("Gamma_Cx::Color", "Out::Color1") .add_connection("Gamma_xC::Color", "Out::Color2") .output_color("Out::Color"); graph.finalize(scene); } /* * Tests: Gamma with one constant 1 input. */ TEST_F(RenderGraph, constant_fold_gamma_part_1) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Gamma_Cx::Color to constant (1, 1, 1)."); CORRECT_INFO_MESSAGE(log, "Folding Gamma_xC::Color to socket Attribute::Color."); builder .add_attribute("Attribute") /* constant on the left */ .add_node(ShaderNodeBuilder(graph, "Gamma_Cx").set("Color", one_float3())) .add_connection("Attribute::Fac", "Gamma_Cx::Gamma") /* constant on the right */ .add_node(ShaderNodeBuilder(graph, "Gamma_xC").set("Gamma", 1.0f)) .add_connection("Attribute::Color", "Gamma_xC::Color") /* output sum */ .add_node(ShaderNodeBuilder(graph, "Out") .set_param("mix_type", NODE_MIX_ADD) .set_param("use_clamp", true) .set("Fac", 1.0f)) .add_connection("Gamma_Cx::Color", "Out::Color1") .add_connection("Gamma_xC::Color", "Out::Color2") .output_color("Out::Color"); graph.finalize(scene); } /* * Tests: BrightnessContrast with all constant inputs. */ TEST_F(RenderGraph, constant_fold_bright_contrast) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding BrightContrast::Color to constant (0.16, 0.6, 1.04)."); builder .add_node(ShaderNodeBuilder(graph, "BrightContrast") .set("Color", make_float3(0.3f, 0.5f, 0.7f)) .set("Bright", 0.1f) .set("Contrast", 1.2f)) .output_color("BrightContrast::Color"); graph.finalize(scene); } /* * Tests: blackbody with all constant inputs. */ TEST_F(RenderGraph, constant_fold_blackbody) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Blackbody::Color to constant (3.96553, 0.227897, 0)."); builder .add_node(ShaderNodeBuilder(graph, "Blackbody").set("Temperature", 1200.0f)) .output_color("Blackbody::Color"); graph.finalize(scene); } /* A Note About The Math Node * * The clamp option is implemented using graph expansion, where a * Clamp node named "clamp" is added and connected to the output. * So the final result is actually from the node "clamp". */ /* * Tests: Math with all constant inputs (clamp false). */ TEST_F(RenderGraph, constant_fold_math) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Math::Value to constant (1.6)."); builder .add_node(ShaderNodeBuilder(graph, "Math") .set_param("math_type", NODE_MATH_ADD) .set_param("use_clamp", false) .set("Value1", 0.7f) .set("Value2", 0.9f)) .output_value("Math::Value"); graph.finalize(scene); } /* * Tests: Math with all constant inputs (clamp true). */ TEST_F(RenderGraph, constant_fold_math_clamp) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding clamp::Result to constant (1)."); builder .add_node(ShaderNodeBuilder(graph, "Math") .set_param("math_type", NODE_MATH_ADD) .set_param("use_clamp", true) .set("Value1", 0.7f) .set("Value2", 0.9f)) .output_value("Math::Value"); graph.finalize(scene); } /* * Graph for testing partial folds of Math with one constant argument. * Includes 2 tests: constant on each side. */ static void build_math_partial_test_graph(ShaderGraphBuilder &builder, NodeMathType type, float constval) { builder .add_attribute("Attribute") /* constant on the left */ .add_node(ShaderNodeBuilder(builder.graph(), "Math_Cx") .set_param("math_type", type) .set_param("use_clamp", false) .set("Value1", constval)) .add_connection("Attribute::Fac", "Math_Cx::Value2") /* constant on the right */ .add_node(ShaderNodeBuilder(builder.graph(), "Math_xC") .set_param("math_type", type) .set_param("use_clamp", false) .set("Value2", constval)) .add_connection("Attribute::Fac", "Math_xC::Value1") /* output sum */ .add_node(ShaderNodeBuilder(builder.graph(), "Out") .set_param("math_type", NODE_MATH_ADD) .set_param("use_clamp", true)) .add_connection("Math_Cx::Value", "Out::Value1") .add_connection("Math_xC::Value", "Out::Value2") .output_value("Out::Value"); } /* * Tests: partial folding for Math Add with known 0. */ TEST_F(RenderGraph, constant_fold_part_math_add_0) { EXPECT_ANY_MESSAGE(log); /* X + 0 == 0 + X == X */ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to socket Attribute::Fac."); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac."); INVALID_INFO_MESSAGE(log, "Folding clamp::"); build_math_partial_test_graph(builder, NODE_MATH_ADD, 0.0f); graph.finalize(scene); } /* * Tests: partial folding for Math Sub with known 0. */ TEST_F(RenderGraph, constant_fold_part_math_sub_0) { EXPECT_ANY_MESSAGE(log); /* X - 0 == X */ INVALID_INFO_MESSAGE(log, "Folding Math_Cx::"); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac."); INVALID_INFO_MESSAGE(log, "Folding clamp::"); build_math_partial_test_graph(builder, NODE_MATH_SUBTRACT, 0.0f); graph.finalize(scene); } /* * Tests: partial folding for Math Mul with known 1. */ TEST_F(RenderGraph, constant_fold_part_math_mul_1) { EXPECT_ANY_MESSAGE(log); /* X * 1 == 1 * X == X */ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to socket Attribute::Fac."); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac."); INVALID_INFO_MESSAGE(log, "Folding clamp::"); build_math_partial_test_graph(builder, NODE_MATH_MULTIPLY, 1.0f); graph.finalize(scene); } /* * Tests: partial folding for Math Div with known 1. */ TEST_F(RenderGraph, constant_fold_part_math_div_1) { EXPECT_ANY_MESSAGE(log); /* X / 1 == X */ INVALID_INFO_MESSAGE(log, "Folding Math_Cx::"); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac."); INVALID_INFO_MESSAGE(log, "Folding clamp::"); build_math_partial_test_graph(builder, NODE_MATH_DIVIDE, 1.0f); graph.finalize(scene); } /* * Tests: partial folding for Math Mul with known 0. */ TEST_F(RenderGraph, constant_fold_part_math_mul_0) { EXPECT_ANY_MESSAGE(log); /* X * 0 == 0 * X == 0 */ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to constant (0)."); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to constant (0)."); CORRECT_INFO_MESSAGE(log, "Folding clamp::Result to constant (0)"); CORRECT_INFO_MESSAGE(log, "Discarding closure EmissionNode."); build_math_partial_test_graph(builder, NODE_MATH_MULTIPLY, 0.0f); graph.finalize(scene); } /* * Tests: partial folding for Math Div with known 0. */ TEST_F(RenderGraph, constant_fold_part_math_div_0) { EXPECT_ANY_MESSAGE(log); /* 0 / X == 0 */ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to constant (0)."); INVALID_INFO_MESSAGE(log, "Folding Math_xC::"); INVALID_INFO_MESSAGE(log, "Folding clamp::"); build_math_partial_test_graph(builder, NODE_MATH_DIVIDE, 0.0f); graph.finalize(scene); } /* * Tests: partial folding for Math Power with known 0. */ TEST_F(RenderGraph, constant_fold_part_math_pow_0) { EXPECT_ANY_MESSAGE(log); /* X ^ 0 == 1 */ INVALID_INFO_MESSAGE(log, "Folding Math_Cx::"); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to constant (1)."); INVALID_INFO_MESSAGE(log, "Folding clamp::"); build_math_partial_test_graph(builder, NODE_MATH_POWER, 0.0f); graph.finalize(scene); } /* * Tests: partial folding for Math Power with known 1. */ TEST_F(RenderGraph, constant_fold_part_math_pow_1) { EXPECT_ANY_MESSAGE(log); /* 1 ^ X == 1; X ^ 1 == X */ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Value to constant (1)"); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Value to socket Attribute::Fac."); INVALID_INFO_MESSAGE(log, "Folding clamp::"); build_math_partial_test_graph(builder, NODE_MATH_POWER, 1.0f); graph.finalize(scene); } /* * Tests: Vector Math with all constant inputs. */ TEST_F(RenderGraph, constant_fold_vector_math) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding VectorMath::Vector to constant (3, 0, 0)."); builder .add_node(ShaderNodeBuilder(graph, "VectorMath") .set_param("math_type", NODE_VECTOR_MATH_SUBTRACT) .set("Vector1", make_float3(1.3f, 0.5f, 0.7f)) .set("Vector2", make_float3(-1.7f, 0.5f, 0.7f))) .output_color("VectorMath::Vector"); graph.finalize(scene); } /* * Graph for testing partial folds of Vector Math with one constant argument. * Includes 2 tests: constant on each side. */ static void build_vecmath_partial_test_graph(ShaderGraphBuilder &builder, NodeVectorMathType type, float3 constval) { builder .add_attribute("Attribute") /* constant on the left */ .add_node(ShaderNodeBuilder(builder.graph(), "Math_Cx") .set_param("math_type", type) .set("Vector1", constval)) .add_connection("Attribute::Vector", "Math_Cx::Vector2") /* constant on the right */ .add_node(ShaderNodeBuilder(builder.graph(), "Math_xC") .set_param("math_type", type) .set("Vector2", constval)) .add_connection("Attribute::Vector", "Math_xC::Vector1") /* output sum */ .add_node(ShaderNodeBuilder(builder.graph(), "Out") .set_param("math_type", NODE_VECTOR_MATH_ADD)) .add_connection("Math_Cx::Vector", "Out::Vector1") .add_connection("Math_xC::Vector", "Out::Vector2") .output_color("Out::Vector"); } /* * Tests: partial folding for Vector Math Add with known 0. */ TEST_F(RenderGraph, constant_fold_part_vecmath_add_0) { EXPECT_ANY_MESSAGE(log); /* X + 0 == 0 + X == X */ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Vector to socket Attribute::Vector."); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Vector to socket Attribute::Vector."); INVALID_INFO_MESSAGE(log, "Folding Out::"); build_vecmath_partial_test_graph(builder, NODE_VECTOR_MATH_ADD, make_float3(0, 0, 0)); graph.finalize(scene); } /* * Tests: partial folding for Vector Math Sub with known 0. */ TEST_F(RenderGraph, constant_fold_part_vecmath_sub_0) { EXPECT_ANY_MESSAGE(log); /* X - 0 == X */ INVALID_INFO_MESSAGE(log, "Folding Math_Cx::"); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Vector to socket Attribute::Vector."); INVALID_INFO_MESSAGE(log, "Folding Out::"); build_vecmath_partial_test_graph(builder, NODE_VECTOR_MATH_SUBTRACT, make_float3(0, 0, 0)); graph.finalize(scene); } /* * Tests: partial folding for Vector Math Cross Product with known 0. */ TEST_F(RenderGraph, constant_fold_part_vecmath_cross_0) { EXPECT_ANY_MESSAGE(log); /* X * 0 == 0 * X == X */ CORRECT_INFO_MESSAGE(log, "Folding Math_Cx::Vector to constant (0, 0, 0)."); CORRECT_INFO_MESSAGE(log, "Folding Math_xC::Vector to constant (0, 0, 0)."); CORRECT_INFO_MESSAGE(log, "Folding Out::Vector to constant (0, 0, 0)."); CORRECT_INFO_MESSAGE(log, "Discarding closure EmissionNode."); build_vecmath_partial_test_graph(builder, NODE_VECTOR_MATH_CROSS_PRODUCT, make_float3(0, 0, 0)); graph.finalize(scene); } /* * Tests: Bump with no height input folded to Normal input. */ TEST_F(RenderGraph, constant_fold_bump) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Bump::Normal to socket Geometry1::Normal."); builder.add_node(ShaderNodeBuilder(graph, "Geometry1")) .add_node(ShaderNodeBuilder(graph, "Bump")) .add_connection("Geometry1::Normal", "Bump::Normal") .output_color("Bump::Normal"); graph.finalize(scene); } /* * Tests: Bump with no inputs folded to Geometry::Normal. */ TEST_F(RenderGraph, constant_fold_bump_no_input) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Bump::Normal to socket geometry::Normal."); builder.add_node(ShaderNodeBuilder(graph, "Bump")).output_color("Bump::Normal"); graph.finalize(scene); } template void init_test_curve(array &buffer, T start, T end, int steps) { buffer.resize(steps); for (int i = 0; i < steps; i++) { buffer[i] = lerp(start, end, float(i) / (steps - 1)); } } /* * Tests: * - Folding of RGB Curves with all constant inputs. */ TEST_F(RenderGraph, constant_fold_rgb_curves) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Curves::Color to constant (0.275, 0.5, 0.475)."); array curve; init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257); builder .add_node(ShaderNodeBuilder(graph, "Curves") .set_param("curves", curve) .set_param("min_x", 0.1f) .set_param("max_x", 0.9f) .set("Fac", 0.5f) .set("Color", make_float3(0.3f, 0.5f, 0.7f))) .output_color("Curves::Color"); graph.finalize(scene); } /* * Tests: * - Folding of RGB Curves with zero Fac. */ TEST_F(RenderGraph, constant_fold_rgb_curves_fac_0) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Curves::Color to socket Attribute::Color."); array curve; init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "Curves") .set_param("curves", curve) .set_param("min_x", 0.1f) .set_param("max_x", 0.9f) .set("Fac", 0.0f)) .add_connection("Attribute::Color", "Curves::Color") .output_color("Curves::Color"); graph.finalize(scene); } /* * Tests: * - Folding of RGB Curves with zero Fac and all constant inputs. */ TEST_F(RenderGraph, constant_fold_rgb_curves_fac_0_const) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Curves::Color to constant (0.3, 0.5, 0.7)."); array curve; init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257); builder .add_node(ShaderNodeBuilder(graph, "Curves") .set_param("curves", curve) .set_param("min_x", 0.1f) .set_param("max_x", 0.9f) .set("Fac", 0.0f) .set("Color", make_float3(0.3f, 0.5f, 0.7f))) .output_color("Curves::Color"); graph.finalize(scene); } /* * Tests: * - Folding of Vector Curves with all constant inputs. */ TEST_F(RenderGraph, constant_fold_vector_curves) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Curves::Vector to constant (0.275, 0.5, 0.475)."); array curve; init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257); builder .add_node(ShaderNodeBuilder(graph, "Curves") .set_param("curves", curve) .set_param("min_x", 0.1f) .set_param("max_x", 0.9f) .set("Fac", 0.5f) .set("Vector", make_float3(0.3f, 0.5f, 0.7f))) .output_color("Curves::Vector"); graph.finalize(scene); } /* * Tests: * - Folding of Vector Curves with zero Fac. */ TEST_F(RenderGraph, constant_fold_vector_curves_fac_0) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Curves::Vector to socket Attribute::Vector."); array curve; init_test_curve(curve, make_float3(0.0f, 0.25f, 1.0f), make_float3(1.0f, 0.75f, 0.0f), 257); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "Curves") .set_param("curves", curve) .set_param("min_x", 0.1f) .set_param("max_x", 0.9f) .set("Fac", 0.0f)) .add_connection("Attribute::Vector", "Curves::Vector") .output_color("Curves::Vector"); graph.finalize(scene); } /* * Tests: * - Folding of Color Ramp with all constant inputs. */ TEST_F(RenderGraph, constant_fold_rgb_ramp) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Ramp::Color to constant (0.14, 0.39, 0.64)."); CORRECT_INFO_MESSAGE(log, "Folding Ramp::Alpha to constant (0.89)."); array curve; array alpha; init_test_curve(curve, make_float3(0.0f, 0.25f, 0.5f), make_float3(0.25f, 0.5f, 0.75f), 9); init_test_curve(alpha, 0.75f, 1.0f, 9); builder .add_node(ShaderNodeBuilder(graph, "Ramp") .set_param("ramp", curve) .set_param("ramp_alpha", alpha) .set_param("interpolate", true) .set("Fac", 0.56f)) .add_node(ShaderNodeBuilder(graph, "Mix").set_param("mix_type", NODE_MIX_ADD)) .add_connection("Ramp::Color", "Mix::Color1") .add_connection("Ramp::Alpha", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); } /* * Tests: * - Folding of Color Ramp with all constant inputs (interpolate false). */ TEST_F(RenderGraph, constant_fold_rgb_ramp_flat) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Ramp::Color to constant (0.125, 0.375, 0.625)."); CORRECT_INFO_MESSAGE(log, "Folding Ramp::Alpha to constant (0.875)."); array curve; array alpha; init_test_curve(curve, make_float3(0.0f, 0.25f, 0.5f), make_float3(0.25f, 0.5f, 0.75f), 9); init_test_curve(alpha, 0.75f, 1.0f, 9); builder .add_node(ShaderNodeBuilder(graph, "Ramp") .set_param("ramp", curve) .set_param("ramp_alpha", alpha) .set_param("interpolate", false) .set("Fac", 0.56f)) .add_node(ShaderNodeBuilder(graph, "Mix").set_param("mix_type", NODE_MIX_ADD)) .add_connection("Ramp::Color", "Mix::Color1") .add_connection("Ramp::Alpha", "Mix::Color2") .output_color("Mix::Color"); graph.finalize(scene); } /* * Tests: * - Folding of redundant conversion of float to color to float. */ TEST_F(RenderGraph, constant_fold_convert_float_color_float) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding Invert::Color to socket convert_float_to_color::value_color."); CORRECT_INFO_MESSAGE(log, "Folding convert_color_to_float::value_float to socket Attribute::Fac."); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "Invert").set("Fac", 0.0f)) .add_connection("Attribute::Fac", "Invert::Color") .output_value("Invert::Color"); graph.finalize(scene); } /* * Tests: * - Folding of redundant conversion of color to vector to color. */ TEST_F(RenderGraph, constant_fold_convert_color_vector_color) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding VecAdd::Vector to socket convert_color_to_vector::value_vector."); CORRECT_INFO_MESSAGE(log, "Folding convert_vector_to_color::value_color to socket Attribute::Color."); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "VecAdd") .set_param("math_type", NODE_VECTOR_MATH_ADD) .set("Vector2", make_float3(0, 0, 0))) .add_connection("Attribute::Color", "VecAdd::Vector1") .output_color("VecAdd::Vector"); graph.finalize(scene); } /* * Tests: * - NOT folding conversion of color to float to color. */ TEST_F(RenderGraph, constant_fold_convert_color_float_color) { EXPECT_ANY_MESSAGE(log); CORRECT_INFO_MESSAGE(log, "Folding MathAdd::Value to socket convert_color_to_float::value_float."); INVALID_INFO_MESSAGE(log, "Folding convert_float_to_color::"); builder.add_attribute("Attribute") .add_node(ShaderNodeBuilder(graph, "MathAdd") .set_param("math_type", NODE_MATH_ADD) .set("Value2", 0.0f)) .add_connection("Attribute::Color", "MathAdd::Value1") .output_color("MathAdd::Value"); graph.finalize(scene); } CCL_NAMESPACE_END