Cycles: constant folding for RGB/Vector Curves and Color Ramp.
[blender.git] / intern / cycles / render / nodes.cpp
index 5e53b66f7104a3186ab2ec81570d71da666a3c55..b3b010841e99a388378f2f0feb26b37c86f8c2e1 100644 (file)
 #include "scene.h"
 #include "svm.h"
 #include "svm_color_util.h"
+#include "svm_ramp_util.h"
 #include "svm_math_util.h"
 #include "osl.h"
+#include "constant_fold.h"
 
 #include "util_sky_model.h"
 #include "util_foreach.h"
@@ -611,10 +613,10 @@ static float sky_perez_function(float lam[6], float theta, float gamma)
 static void sky_texture_precompute_old(SunSky *sunsky, float3 dir, float turbidity)
 {
        /*
-       * We re-use the SunSky struct of the new model, to avoid extra variables
-       * zenith_Y/x/y is now radiance_x/y/z
-       * perez_Y/x/y is now config_x/y/z
-       */
+        * We re-use the SunSky struct of the new model, to avoid extra variables
+        * zenith_Y/x/y is now radiance_x/y/z
+        * perez_Y/x/y is now config_x/y/z
+        */
        
        float2 spherical = sky_spherical_coordinates(dir);
        float theta = spherical.x;
@@ -1576,14 +1578,11 @@ RGBToBWNode::RGBToBWNode()
 {
 }
 
-bool RGBToBWNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void RGBToBWNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
-               optimized->set(linear_rgb_to_gray(color));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(linear_rgb_to_gray(color));
        }
-
-       return false;
 }
 
 void RGBToBWNode::compile(SVMCompiler& compiler)
@@ -1596,7 +1595,7 @@ void RGBToBWNode::compile(SVMCompiler& compiler)
 
 void RGBToBWNode::compile(OSLCompiler& compiler)
 {
-       compiler.add(this, "node_convert_from_color");
+       compiler.add(this, "node_rgb_to_bw");
 }
 
 /* Convert */
@@ -1661,38 +1660,35 @@ ConvertNode::ConvertNode(SocketType::Type from_, SocketType::Type to_, bool auto
                special_type = SHADER_SPECIAL_TYPE_AUTOCONVERT;
 }
 
-bool ConvertNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void ConvertNode::constant_fold(const ConstantFolder& folder)
 {
        /* proxy nodes should have been removed at this point */
        assert(special_type != SHADER_SPECIAL_TYPE_PROXY);
 
        /* TODO(DingTo): conversion from/to int is not supported yet, don't fold in that case */
 
-       if(all_inputs_constant()) {
+       if(folder.all_inputs_constant()) {
                if(from == SocketType::FLOAT) {
                        if(SocketType::is_float3(to)) {
-                               optimized->set(make_float3(value_float, value_float, value_float));
-                               return true;
+                               folder.make_constant(make_float3(value_float, value_float, value_float));
                        }
                }
                else if(SocketType::is_float3(from)) {
                        if(to == SocketType::FLOAT) {
-                               if(from == SocketType::COLOR)
+                               if(from == SocketType::COLOR) {
                                        /* color to float */
-                                       optimized->set(linear_rgb_to_gray(value_color));
-                               else
+                                       folder.make_constant(linear_rgb_to_gray(value_color));
+                               }
+                               else {
                                        /* vector/point/normal to float */
-                                       optimized->set(average(value_vector));
-                               return true;
+                                       folder.make_constant(average(value_vector));
+                               }
                        }
                        else if(SocketType::is_float3(to)) {
-                               optimized->set(value_color);
-                               return true;
+                               folder.make_constant(value_color);
                        }
                }
        }
-
-       return false;
 }
 
 void ConvertNode::compile(SVMCompiler& compiler)
@@ -1828,6 +1824,7 @@ NODE_DEFINE(AnisotropicBsdfNode)
        static NodeEnum distribution_enum;
        distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID);
        distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID);
+       distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID);
        distribution_enum.insert("ashikhmin_shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID);
        SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID);
 
@@ -1864,7 +1861,10 @@ void AnisotropicBsdfNode::compile(SVMCompiler& compiler)
 {
        closure = distribution;
 
-       BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"));
+       if(closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID)
+               BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"), input("Color"));
+       else
+               BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"));
 }
 
 void AnisotropicBsdfNode::compile(OSLCompiler& compiler)
@@ -1888,6 +1888,7 @@ NODE_DEFINE(GlossyBsdfNode)
        distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID);
        distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ID);
        distribution_enum.insert("ashikhmin_shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID);
+       distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID);
        SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_ID);
        SOCKET_IN_FLOAT(roughness, "Roughness", 0.2f);
 
@@ -1937,6 +1938,8 @@ void GlossyBsdfNode::compile(SVMCompiler& compiler)
 
        if(closure == CLOSURE_BSDF_REFLECTION_ID)
                BsdfNode::compile(compiler, NULL, NULL);
+       else if(closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID)
+               BsdfNode::compile(compiler, input("Roughness"), NULL, input("Color"));
        else
                BsdfNode::compile(compiler, input("Roughness"), NULL);
 }
@@ -1961,6 +1964,7 @@ NODE_DEFINE(GlassBsdfNode)
        distribution_enum.insert("sharp", CLOSURE_BSDF_SHARP_GLASS_ID);
        distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID);
        distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID);
+       distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID);
        SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID);
        SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f);
        SOCKET_IN_FLOAT(IOR, "IOR", 0.3f);
@@ -2011,6 +2015,8 @@ void GlassBsdfNode::compile(SVMCompiler& compiler)
 
        if(closure == CLOSURE_BSDF_SHARP_GLASS_ID)
                BsdfNode::compile(compiler, NULL, input("IOR"));
+       else if(closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
+               BsdfNode::compile(compiler, input("Roughness"), input("IOR"), input("Color"));
        else
                BsdfNode::compile(compiler, input("Roughness"), input("IOR"));
 }
@@ -2290,6 +2296,7 @@ NODE_DEFINE(SubsurfaceScatteringNode)
 SubsurfaceScatteringNode::SubsurfaceScatteringNode()
 : BsdfNode(node_type)
 {
+       closure = falloff;
 }
 
 void SubsurfaceScatteringNode::compile(SVMCompiler& compiler)
@@ -2300,6 +2307,7 @@ void SubsurfaceScatteringNode::compile(SVMCompiler& compiler)
 
 void SubsurfaceScatteringNode::compile(OSLCompiler& compiler)
 {
+       closure = falloff;
        compiler.parameter(this, "falloff");
        compiler.add(this, "node_subsurface_scattering");
 }
@@ -2318,7 +2326,7 @@ NODE_DEFINE(EmissionNode)
        NodeType* type = NodeType::add("emission", create, NodeType::SHADER);
 
        SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f));
-       SOCKET_IN_FLOAT(strength, "Strength", 1.0f);
+       SOCKET_IN_FLOAT(strength, "Strength", 10.0f);
        SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL);
 
        SOCKET_OUT_CLOSURE(emission, "Emission");
@@ -2339,7 +2347,7 @@ void EmissionNode::compile(SVMCompiler& compiler)
        if(color_in->link || strength_in->link) {
                compiler.add_node(NODE_EMISSION_WEIGHT,
                                  compiler.stack_assign(color_in),
-                                                 compiler.stack_assign(strength_in));
+                                 compiler.stack_assign(strength_in));
        }
        else
                compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color * strength);
@@ -2352,13 +2360,15 @@ void EmissionNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_emission");
 }
 
-bool EmissionNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *)
+void EmissionNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *color_in = input("Color");
        ShaderInput *strength_in = input("Strength");
 
-       return ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) ||
-               (!strength_in->link && strength == 0.0f));
+       if ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) ||
+           (!strength_in->link && strength == 0.0f)) {
+               folder.discard();
+       }
 }
 
 /* Background Closure */
@@ -2402,13 +2412,15 @@ void BackgroundNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_background");
 }
 
-bool BackgroundNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *)
+void BackgroundNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *color_in = input("Color");
        ShaderInput *strength_in = input("Strength");
 
-       return ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) ||
-               (!strength_in->link && strength == 0.0f));
+       if ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) ||
+           (!strength_in->link && strength == 0.0f)) {
+               folder.discard();
+       }
 }
 
 /* Holdout Closure */
@@ -3370,10 +3382,9 @@ ValueNode::ValueNode()
 {
 }
 
-bool ValueNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void ValueNode::constant_fold(const ConstantFolder& folder)
 {
-       optimized->set(value);
-       return true;
+       folder.make_constant(value);
 }
 
 void ValueNode::compile(SVMCompiler& compiler)
@@ -3406,10 +3417,9 @@ ColorNode::ColorNode()
 {
 }
 
-bool ColorNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void ColorNode::constant_fold(const ConstantFolder& folder)
 {
-       optimized->set(value);
-       return true;
+       folder.make_constant(value);
 }
 
 void ColorNode::compile(SVMCompiler& compiler)
@@ -3458,6 +3468,20 @@ void AddClosureNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_add_closure");
 }
 
+void AddClosureNode::constant_fold(const ConstantFolder& folder)
+{
+       ShaderInput *closure1_in = input("Closure1");
+       ShaderInput *closure2_in = input("Closure2");
+
+       /* remove useless add closures nodes */
+       if(!closure1_in->link) {
+               folder.bypass_or_discard(closure2_in);
+       }
+       else if(!closure2_in->link) {
+               folder.bypass_or_discard(closure1_in);
+       }
+}
+
 /* Mix Closure */
 
 NODE_DEFINE(MixClosureNode)
@@ -3489,35 +3513,28 @@ void MixClosureNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_mix_closure");
 }
 
-bool MixClosureNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *)
+void MixClosureNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *fac_in = input("Fac");
        ShaderInput *closure1_in = input("Closure1");
        ShaderInput *closure2_in = input("Closure2");
-       ShaderOutput *closure_out = output("Closure");
 
        /* remove useless mix closures nodes */
        if(closure1_in->link == closure2_in->link) {
-               graph->relink(this, closure_out, closure1_in->link);
-               return true;
+               folder.bypass_or_discard(closure1_in);
        }
-
-       /* remove unused mix closure input when factor is 0.0 or 1.0 */
-       /* check for closure links and make sure factor link is disconnected */
-       if(closure1_in->link && closure2_in->link && !fac_in->link) {
+       /* remove unused mix closure input when factor is 0.0 or 1.0
+        * check for closure links and make sure factor link is disconnected */
+       else if(!fac_in->link) {
                /* factor 0.0 */
-               if(fac == 0.0f) {
-                       graph->relink(this, closure_out, closure1_in->link);
-                       return true;
+               if(fac <= 0.0f) {
+                       folder.bypass_or_discard(closure1_in);
                }
                /* factor 1.0 */
-               else if(fac == 1.0f) {
-                       graph->relink(this, closure_out, closure2_in->link);
-                       return true;
+               else if(fac >= 1.0f) {
+                       folder.bypass_or_discard(closure2_in);
                }
        }
-
-       return false;
 }
 
 /* Mix Closure */
@@ -3579,26 +3596,21 @@ InvertNode::InvertNode()
 {
 }
 
-bool InvertNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *optimized)
+void InvertNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *fac_in = input("Fac");
        ShaderInput *color_in = input("Color");
-       ShaderOutput *color_out = output("Color");
 
        if(!fac_in->link) {
                /* evaluate fully constant node */
                if(!color_in->link) {
-                       optimized->set(interp(color, make_float3(1.0f, 1.0f, 1.0f) - color, fac));
-                       return true;
+                       folder.make_constant(interp(color, make_float3(1.0f, 1.0f, 1.0f) - color, fac));
                }
                /* remove no-op node */
                else if(fac == 0.0f) {
-                       graph->relink(this, color_out, color_in->link);
-                       return true;
+                       folder.bypass(color_in->link);
                }
        }
-
-       return false;
 }
 
 void InvertNode::compile(SVMCompiler& compiler)
@@ -3687,61 +3699,47 @@ void MixNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_mix");
 }
 
-bool MixNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *optimized)
+void MixNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *fac_in = input("Fac");
        ShaderInput *color1_in = input("Color1");
        ShaderInput *color2_in = input("Color2");
-       ShaderOutput *color_out = output("Color");
 
        /* evaluate fully constant node */
-       if(all_inputs_constant()) {
-               float3 result = svm_mix(type, fac, color1, color2);
-               optimized->set(use_clamp ? svm_mix_clamp(result) : result);
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant_clamp(svm_mix(type, fac, color1, color2), use_clamp);
+               return;
        }
 
        /* remove no-op node when factor is 0.0 */
        if(!fac_in->link && fac <= 0.0f) {
                /* note that some of the modes will clamp out of bounds values even without use_clamp */
-               if(!color1_in->link) {
-                       float3 result = svm_mix(type, 0.0f, color1, color1);
-                       optimized->set(use_clamp ? svm_mix_clamp(result) : result);
-                       return true;
+               if(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN) {
+                       if(!color1_in->link) {
+                               folder.make_constant_clamp(svm_mix(type, 0.0f, color1, color1), use_clamp);
+                               return;
+                       }
                }
-               else if(!use_clamp && type != NODE_MIX_LIGHT && type != NODE_MIX_DODGE && type != NODE_MIX_BURN) {
-                       graph->relink(this, color_out, color1_in->link);
-                       return true;
+               else if(folder.try_bypass_or_make_constant(color1_in, color1, use_clamp)) {
+                       return;
                }
        }
 
-       if(type != NODE_MIX_BLEND) {
-               return false;
-       }
-
-       /* remove useless mix colors nodes */
-       if(color1_in->link && color1_in->link == color2_in->link && !use_clamp) {
-               graph->relink(this, color_out, color1_in->link);
-               return true;
-       }
-       if(!color1_in->link && !color2_in->link && color1 == color2) {
-               optimized->set(use_clamp ? svm_mix_clamp(color1) : color1);
-               return true;
-       }
-
-       /* remove no-op mix color node when factor is 1.0 */
-       if(!fac_in->link && fac >= 1.0f) {
-               if(!color2_in->link) {
-                       optimized->set(use_clamp ? svm_mix_clamp(color2) : color2);
-                       return true;
+       if(type == NODE_MIX_BLEND) {
+               /* remove useless mix colors nodes */
+               if(color1_in->link ? (color1_in->link == color2_in->link) : (!color2_in->link && color1 == color2)) {
+                       if(folder.try_bypass_or_make_constant(color1_in, color1, use_clamp)) {
+                               return;
+                       }
                }
-               else if(!use_clamp) {
-                       graph->relink(this, color_out, color2_in->link);
-                       return true;
+
+               /* remove no-op mix color node when factor is 1.0 */
+               if(!fac_in->link && fac >= 1.0f) {
+                       if(folder.try_bypass_or_make_constant(color2_in, color2, use_clamp)) {
+                               return;
+                       }
                }
        }
-
-       return false;
 }
 
 /* Combine RGB */
@@ -3764,14 +3762,11 @@ CombineRGBNode::CombineRGBNode()
 {
 }
 
-bool CombineRGBNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void CombineRGBNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
-               optimized->set(make_float3(r, g, b));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(make_float3(r, g, b));
        }
-
-       return false;
 }
 
 void CombineRGBNode::compile(SVMCompiler& compiler)
@@ -3819,14 +3814,11 @@ CombineXYZNode::CombineXYZNode()
 {
 }
 
-bool CombineXYZNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void CombineXYZNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
-               optimized->set(make_float3(x, y, z));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(make_float3(x, y, z));
        }
-
-       return false;
 }
 
 void CombineXYZNode::compile(SVMCompiler& compiler)
@@ -3874,14 +3866,11 @@ CombineHSVNode::CombineHSVNode()
 {
 }
 
-bool CombineHSVNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void CombineHSVNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
-               optimized->set(hsv_to_rgb(make_float3(h, s, v)));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(hsv_to_rgb(make_float3(h, s, v)));
        }
-
-       return false;
 }
 
 void CombineHSVNode::compile(SVMCompiler& compiler)
@@ -3922,14 +3911,11 @@ GammaNode::GammaNode()
 {
 }
 
-bool GammaNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void GammaNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
-               optimized->set(svm_math_gamma_color(color, gamma));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(svm_math_gamma_color(color, gamma));
        }
-
-       return false;
 }
 
 void GammaNode::compile(SVMCompiler& compiler)
@@ -3969,14 +3955,11 @@ BrightContrastNode::BrightContrastNode()
 {
 }
 
-bool BrightContrastNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void BrightContrastNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
-               optimized->set(svm_brightness_contrast(color, bright, contrast));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(svm_brightness_contrast(color, bright, contrast));
        }
-
-       return false;
 }
 
 void BrightContrastNode::compile(SVMCompiler& compiler)
@@ -4019,18 +4002,16 @@ SeparateRGBNode::SeparateRGBNode()
 {
 }
 
-bool SeparateRGBNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized)
+void SeparateRGBNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
+       if(folder.all_inputs_constant()) {
                for(int channel = 0; channel < 3; channel++) {
-                       if(outputs[channel] == socket) {
-                               optimized->set(color[channel]);
-                               return true;
+                       if(outputs[channel] == folder.output) {
+                               folder.make_constant(color[channel]);
+                               return;
                        }
                }
        }
-
-       return false;
 }
 
 void SeparateRGBNode::compile(SVMCompiler& compiler)
@@ -4078,18 +4059,16 @@ SeparateXYZNode::SeparateXYZNode()
 {
 }
 
-bool SeparateXYZNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized)
+void SeparateXYZNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
+       if(folder.all_inputs_constant()) {
                for(int channel = 0; channel < 3; channel++) {
-                       if(outputs[channel] == socket) {
-                               optimized->set(vector[channel]);
-                               return true;
+                       if(outputs[channel] == folder.output) {
+                               folder.make_constant(vector[channel]);
+                               return;
                        }
                }
        }
-
-       return false;
 }
 
 void SeparateXYZNode::compile(SVMCompiler& compiler)
@@ -4137,20 +4116,18 @@ SeparateHSVNode::SeparateHSVNode()
 {
 }
 
-bool SeparateHSVNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized)
+void SeparateHSVNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
+       if(folder.all_inputs_constant()) {
                float3 hsv = rgb_to_hsv(color);
 
                for(int channel = 0; channel < 3; channel++) {
-                       if(outputs[channel] == socket) {
-                               optimized->set(hsv[channel]);
-                               return true;
+                       if(outputs[channel] == folder.output) {
+                               folder.make_constant(hsv[channel]);
+                               return;
                        }
                }
        }
-
-       return false;
 }
 
 void SeparateHSVNode::compile(SVMCompiler& compiler)
@@ -4536,14 +4513,11 @@ BlackbodyNode::BlackbodyNode()
 {
 }
 
-bool BlackbodyNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void BlackbodyNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
-               optimized->set(svm_math_blackbody_color(temperature));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(svm_math_blackbody_color(temperature));
        }
-
-       return false;
 }
 
 void BlackbodyNode::compile(SVMCompiler& compiler)
@@ -4645,15 +4619,11 @@ MathNode::MathNode()
 {
 }
 
-bool MathNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void MathNode::constant_fold(const ConstantFolder& folder)
 {
-       if(all_inputs_constant()) {
-               float value = svm_math(type, value1, value2);
-               optimized->set(use_clamp ? saturate(value) : value);
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant_clamp(svm_math(type, value1, value2), use_clamp);
        }
-
-       return false;
 }
 
 void MathNode::compile(SVMCompiler& compiler)
@@ -4707,29 +4677,25 @@ VectorMathNode::VectorMathNode()
 {
 }
 
-bool VectorMathNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized)
+void VectorMathNode::constant_fold(const ConstantFolder& folder)
 {
        float value;
        float3 vector;
 
-       if(all_inputs_constant()) {
+       if(folder.all_inputs_constant()) {
                svm_vector_math(&value,
                                &vector,
                                type,
                                vector1,
                                vector2);
 
-               if(socket == output("Value")) {
-                       optimized->set(value);
-                       return true;
+               if(folder.output == output("Value")) {
+                       folder.make_constant(value);
                }
-               else if(socket == output("Vector")) {
-                       optimized->set(vector);
-                       return true;
+               else if(folder.output == output("Vector")) {
+                       folder.make_constant(vector);
                }
        }
-
-       return false;
 }
 
 void VectorMathNode::compile(SVMCompiler& compiler)
@@ -4863,7 +4829,7 @@ void BumpNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_bump");
 }
 
-bool BumpNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *)
+void BumpNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *height_in = input("Height");
        ShaderInput *normal_in = input("Normal");
@@ -4871,18 +4837,15 @@ bool BumpNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *)
        if(height_in->link == NULL) {
                if(normal_in->link == NULL) {
                        GeometryNode *geom = new GeometryNode();
-                       graph->add(geom);
-                       graph->relink(this, outputs[0], geom->output("Normal"));
+                       folder.graph->add(geom);
+                       folder.bypass(geom->output("Normal"));
                }
                else {
-                       graph->relink(this, outputs[0], normal_in->link);
+                       folder.bypass(normal_in->link);
                }
-               return true;
        }
 
        /* TODO(sergey): Ignore bump with zero strength. */
-
-       return false;
 }
 
 
@@ -4893,6 +4856,30 @@ CurvesNode::CurvesNode(const NodeType *node_type)
 {
 }
 
+void CurvesNode::constant_fold(const ConstantFolder& folder, ShaderInput *value_in)
+{
+       ShaderInput *fac_in = input("Fac");
+
+       /* remove no-op node */
+       if(!fac_in->link && fac == 0.0f) {
+               folder.bypass(value_in->link);
+       }
+       /* evaluate fully constant node */
+       else if(folder.all_inputs_constant()) {
+               if (curves.size() == 0)
+                       return;
+
+               float3 pos = (value - make_float3(min_x, min_x, min_x)) / (max_x - min_x);
+               float3 result;
+
+               result[0] = rgb_ramp_lookup(curves.data(), pos[0], true, true, curves.size()).x;
+               result[1] = rgb_ramp_lookup(curves.data(), pos[1], true, true, curves.size()).y;
+               result[2] = rgb_ramp_lookup(curves.data(), pos[2], true, true, curves.size()).z;
+
+               folder.make_constant(interp(value, result, fac));
+       }
+}
+
 void CurvesNode::compile(SVMCompiler& compiler, int type, ShaderInput *value_in, ShaderOutput *value_out)
 {
        if(curves.size() == 0)
@@ -4956,6 +4943,11 @@ RGBCurvesNode::RGBCurvesNode()
 {
 }
 
+void RGBCurvesNode::constant_fold(const ConstantFolder& folder)
+{
+       CurvesNode::constant_fold(folder, input("Color"));
+}
+
 void RGBCurvesNode::compile(SVMCompiler& compiler)
 {
        CurvesNode::compile(compiler, NODE_RGB_CURVES, input("Color"), output("Color"));
@@ -4989,6 +4981,11 @@ VectorCurvesNode::VectorCurvesNode()
 {
 }
 
+void VectorCurvesNode::constant_fold(const ConstantFolder& folder)
+{
+       CurvesNode::constant_fold(folder, input("Vector"));
+}
+
 void VectorCurvesNode::compile(SVMCompiler& compiler)
 {
        CurvesNode::compile(compiler, NODE_VECTOR_CURVES, input("Vector"), output("Vector"));
@@ -5022,6 +5019,31 @@ RGBRampNode::RGBRampNode()
 {
 }
 
+void RGBRampNode::constant_fold(const ConstantFolder& folder)
+{
+       if(ramp.size() == 0 || ramp.size() != ramp_alpha.size())
+               return;
+
+       if(folder.all_inputs_constant()) {
+               float f = clamp(fac, 0.0f, 1.0f) * (ramp.size() - 1);
+
+               /* clamp int as well in case of NaN */
+               int i = clamp((int)f, 0, ramp.size()-1);
+               float t = f - (float)i;
+
+               bool use_lerp = interpolate && t > 0.0f;
+
+               if(folder.output == output("Color")) {
+                       float3 color = rgb_ramp_lookup(ramp.data(), fac, use_lerp, false, ramp.size());
+                       folder.make_constant(color);
+               }
+               else if(folder.output == output("Alpha")) {
+                       float alpha = float_ramp_lookup(ramp_alpha.data(), fac, use_lerp, false, ramp_alpha.size());
+                       folder.make_constant(alpha);
+               }
+       }
+}
+
 void RGBRampNode::compile(SVMCompiler& compiler)
 {
        if(ramp.size() == 0 || ramp.size() != ramp_alpha.size())
@@ -5102,12 +5124,10 @@ OSLNode::~OSLNode()
 
 ShaderNode *OSLNode::clone() const
 {
-       OSLNode *node = new OSLNode(*this);
-       node->type = new NodeType(*type);
-       return node;
+       return OSLNode::create(this->inputs.size(), this);
 }
 
-OSLNode* OSLNode::create(size_t num_inputs)
+OSLNode* OSLNode::create(size_t num_inputs, const OSLNode *from)
 {
        /* allocate space for the node itself and parameters, aligned to 16 bytes
         * assuming that's the most parameter types need */
@@ -5117,7 +5137,17 @@ OSLNode* OSLNode::create(size_t num_inputs)
        char *node_memory = (char*) operator new(node_size + inputs_size);
        memset(node_memory, 0, node_size + inputs_size);
 
-       return new(node_memory) OSLNode();
+       if (!from) {
+               return new(node_memory) OSLNode();
+       }
+       else {
+               /* copy input default values and node type for cloning */
+               memcpy(node_memory + node_size, (char*)from + node_size, inputs_size);
+
+               OSLNode *node = new(node_memory) OSLNode(*from);
+               node->type = new NodeType(*(from->type));
+               return node;
+       }
 }
 
 char* OSLNode::input_default_value()