Cycles: implement partial constant folding for exponentiation.
authorAlexander Gavrilov <angavrilov@gmail.com>
Wed, 28 Sep 2016 21:35:53 +0000 (00:35 +0300)
committerAlexander Gavrilov <angavrilov@gmail.com>
Sat, 1 Oct 2016 11:37:03 +0000 (14:37 +0300)
This is also an important mathematical operation that can be folded
if it is known that one argument is a certain constant. For colors
the operation is provided as a Gamma node.

The SVM Gamma node needs a small fix to make it follow the 0 ^ 0 == 1
rule, same as the Power node, or the Gamma node itself in OSL mode.

Reviewers: #cycles

Differential Revision: https://developer.blender.org/D2263

intern/cycles/kernel/svm/svm_math_util.h
intern/cycles/render/constant_fold.cpp
intern/cycles/render/constant_fold.h
intern/cycles/render/nodes.cpp
intern/cycles/test/render_graph_finalize_test.cpp

index 6d13a0d8e0291ca86087b513207ceafe3802f5bb..01547b60014f08b1080b250c462226ad77df6b4c 100644 (file)
@@ -164,6 +164,9 @@ ccl_device float3 svm_math_blackbody_color(float t) {
 
 ccl_device_inline float3 svm_math_gamma_color(float3 color, float gamma)
 {
+       if(gamma == 0.0f)
+               return make_float3(1.0f, 1.0f, 1.0f);
+
        if(color.x > 0.0f)
                color.x = powf(color.x, gamma);
        if(color.y > 0.0f)
index 200a4c497cdc037f1aeb37d44eb780a6cb4488e1..b7f25663bc3c0a02444e2cbb7ccab4293735d446 100644 (file)
@@ -89,6 +89,19 @@ void ConstantFolder::make_zero() const
        }
 }
 
+void ConstantFolder::make_one() const
+{
+       if(output->type() == SocketType::FLOAT) {
+               make_constant(1.0f);
+       }
+       else if(SocketType::is_float3(output->type())) {
+               make_constant(make_float3(1.0f, 1.0f, 1.0f));
+       }
+       else {
+               assert(0);
+       }
+}
+
 void ConstantFolder::bypass(ShaderOutput *new_output) const
 {
        assert(new_output);
@@ -321,6 +334,15 @@ void ConstantFolder::fold_math(NodeMath type, bool clamp) const
                                make_zero();
                        }
                        break;
+               case NODE_MATH_POWER:
+                       /* 1 ^ X == X ^ 0 == 1 */
+                       if(is_one(value1_in) || is_zero(value2_in)) {
+                               make_one();
+                       }
+                       /* X ^ 1 == X */
+                       else if(is_one(value2_in)) {
+                               try_bypass_or_make_constant(value1_in, clamp);
+                       }
                default:
                        break;
        }
index 2b31c2a5887fe49b7f208953ecb781a92f4a06e3..7962698319f0887e970e811da318a6bd3d3bccf5 100644 (file)
@@ -43,6 +43,7 @@ public:
        void make_constant_clamp(float value, bool clamp) const;
        void make_constant_clamp(float3 value, bool clamp) const;
        void make_zero() const;
+       void make_one() const;
 
        /* Bypass node, relinking to another output socket. */
        void bypass(ShaderOutput *output) const;
index df43863ea7a5d1a9a1e3b32b79fe190fb8f4db1c..7ea52b28b9cf6035ff9eeb71438222fc5b5c3d4b 100644 (file)
@@ -3896,6 +3896,19 @@ void GammaNode::constant_fold(const ConstantFolder& folder)
        if(folder.all_inputs_constant()) {
                folder.make_constant(svm_math_gamma_color(color, gamma));
        }
+       else {
+               ShaderInput *color_in = input("Color");
+               ShaderInput *gamma_in = input("Gamma");
+
+               /* 1 ^ X == X ^ 0 == 1 */
+               if(folder.is_one(color_in) || folder.is_zero(gamma_in)) {
+                       folder.make_one();
+               }
+               /* X ^ 1 == X */
+               else if(folder.is_one(gamma_in)) {
+                       folder.try_bypass_or_make_constant(color_in, false);
+               }
+       }
 }
 
 void GammaNode::compile(SVMCompiler& compiler)
index 60e41be16aa3cdd1d1bb584320ce2a544b680539..6f1c0b88b51c67e5527a42e577d9377f679da39b 100644 (file)
@@ -930,6 +930,72 @@ TEST(render_graph, constant_fold_gamma)
        graph.finalize(&scene);
 }
 
+/*
+ * Tests: Gamma with one constant 0 input.
+ */
+TEST(render_graph, constant_fold_gamma_part_0)
+{
+       DEFINE_COMMON_VARIABLES(builder, log);
+
+       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<GammaNode>("Gamma_Cx")
+                         .set("Color", make_float3(0.0f, 0.0f, 0.0f)))
+               .add_connection("Attribute::Fac", "Gamma_Cx::Gamma")
+               /* constant on the right */
+               .add_node(ShaderNodeBuilder<GammaNode>("Gamma_xC")
+                         .set("Gamma", 0.0f))
+               .add_connection("Attribute::Color", "Gamma_xC::Color")
+               /* output sum */
+               .add_node(ShaderNodeBuilder<MixNode>("Out")
+                         .set(&MixNode::type, NODE_MIX_ADD)
+                         .set(&MixNode::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(render_graph, constant_fold_gamma_part_1)
+{
+       DEFINE_COMMON_VARIABLES(builder, log);
+
+       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<GammaNode>("Gamma_Cx")
+                         .set("Color", make_float3(1.0f, 1.0f, 1.0f)))
+               .add_connection("Attribute::Fac", "Gamma_Cx::Gamma")
+               /* constant on the right */
+               .add_node(ShaderNodeBuilder<GammaNode>("Gamma_xC")
+                         .set("Gamma", 1.0f))
+               .add_connection("Attribute::Color", "Gamma_xC::Color")
+               /* output sum */
+               .add_node(ShaderNodeBuilder<MixNode>("Out")
+                         .set(&MixNode::type, NODE_MIX_ADD)
+                         .set(&MixNode::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.
  */
@@ -1142,6 +1208,40 @@ TEST(render_graph, constant_fold_part_math_div_0)
        graph.finalize(&scene);
 }
 
+/*
+ * Tests: partial folding for Math Power with known 0.
+ */
+TEST(render_graph, constant_fold_part_math_pow_0)
+{
+       DEFINE_COMMON_VARIABLES(builder, log);
+
+       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 Out::");
+
+       build_math_partial_test_graph(builder, NODE_MATH_POWER, 0.0f);
+       graph.finalize(&scene);
+}
+
+/*
+ * Tests: partial folding for Math Power with known 1.
+ */
+TEST(render_graph, constant_fold_part_math_pow_1)
+{
+       DEFINE_COMMON_VARIABLES(builder, log);
+
+       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 Out::");
+
+       build_math_partial_test_graph(builder, NODE_MATH_POWER, 1.0f);
+       graph.finalize(&scene);
+}
+
 /*
  * Tests: Vector Math with all constant inputs.
  */