From e796581655ed9a36c263a20e7ed97856fc0f1070 Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Wed, 25 Nov 2015 13:52:39 +0100 Subject: Cycles: Refactor of constant fold. * Move constant folding from nodes to the shader graph. This way it's part of our (later) 4-step optimization process. * Instead of only doing a one level constant fold, we can now do a recursive constant fold, allowing us to simplify shaders much further. Constant folding is implemented for Blackbody, Math and VectorMath nodes. Example (the highlighted nodes are removed before rendering): Before: http://archive.dingto.org/2015/blender/code/one_level_constant_fold.jpg Now: http://archive.dingto.org/2015/blender/code/multi_level_constant_fold.jpg Thanks to Sergey and Brecht for Review! Differential Revision: https://developer.blender.org/D1626 --- intern/cycles/render/graph.cpp | 44 ++++++++++++++-- intern/cycles/render/graph.h | 13 ++++- intern/cycles/render/nodes.cpp | 116 +++++++++++++++++++++++++---------------- intern/cycles/render/nodes.h | 9 ++-- 4 files changed, 127 insertions(+), 55 deletions(-) (limited to 'intern/cycles/render') diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index e888cb37137..8468690841d 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -297,7 +297,7 @@ void ShaderGraph::finalize(Scene *scene, finalized = true; } else if(do_simplify) { - simplify_nodes(scene); + simplify_settings(scene); } } @@ -562,11 +562,44 @@ void ShaderGraph::remove_unneeded_nodes() } } +/* Step 2: Constant folding. + * Try to constant fold some nodes, and pipe result directly to + * the input socket of connected nodes. + */ +void ShaderGraph::constant_fold(set& done, ShaderNode *node) +{ + /* Only fold each node once. */ + if(done.find(node) != done.end()) + return; + + done.insert(node); + + /* Fold nodes connected to inputs first. */ + foreach(ShaderInput *in, node->inputs) { + if(in->link) { + constant_fold(done, in->link->parent); + } + } + + /* Then fold self. */ + foreach(ShaderOutput *sock, node->outputs) { + float3 optimized_value = make_float3(0.0f, 0.0f, 0.0f); + + if(node->constant_fold(sock, &optimized_value)) { + /* Apply optimized value to connected sockets */ + foreach(ShaderInput *in, sock->links) { + in->value = optimized_value; + disconnect(in); + } + } + } +} + /* Step 3: Simplification.*/ -void ShaderGraph::simplify_nodes(Scene *scene) +void ShaderGraph::simplify_settings(Scene *scene) { foreach(ShaderNode *node, nodes) { - node->optimize(scene); + node->simplify_settings(scene); } } @@ -607,10 +640,11 @@ void ShaderGraph::clean(Scene *scene) remove_unneeded_nodes(); /* 2: Constant folding. */ - /* TODO(dingto): Implement */ + set done; + constant_fold(done, output()); /* 3: Simplification. */ - simplify_nodes(scene); + simplify_settings(scene); /* 4: De-duplication. */ /* TODO(dingto): Implement */ diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h index 6ad40720d4c..17fb75d459f 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/render/graph.h @@ -197,7 +197,15 @@ public: virtual void attributes(Shader *shader, AttributeRequestSet *attributes); virtual void compile(SVMCompiler& compiler) = 0; virtual void compile(OSLCompiler& compiler) = 0; - virtual void optimize(Scene * /*scene*/) {}; + + /* ** Node optimization ** */ + /* Check whether the node can be replaced with single constant. */ + virtual bool constant_fold(ShaderOutput * /*socket*/, float3 * /*optimized_value*/) { return false; } + + /* Simplify settings used by artists to the ones which are simpler to + * evaluate in the kernel but keep the final result unchanged. + */ + virtual void simplify_settings(Scene * /*scene*/) {}; virtual bool has_surface_emission() { return false; } virtual bool has_surface_transparent() { return false; } @@ -307,7 +315,8 @@ protected: void break_cycles(ShaderNode *node, vector& visited, vector& on_stack); void clean(Scene *scene); - void simplify_nodes(Scene *scene); + void simplify_settings(Scene *scene); + void constant_fold(set& visited, ShaderNode *node); void bump_from_displacement(); void refine_bump_nodes(); void default_inputs(bool do_osl); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index d89e74837c7..2bca8605a90 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -1881,7 +1881,7 @@ GlossyBsdfNode::GlossyBsdfNode() add_input("Roughness", SHADER_SOCKET_FLOAT, 0.2f); } -void GlossyBsdfNode::optimize(Scene *scene) +void GlossyBsdfNode::simplify_settings(Scene *scene) { if(distribution_orig == "") { distribution_orig = distribution; @@ -1950,7 +1950,7 @@ GlassBsdfNode::GlassBsdfNode() add_input("IOR", SHADER_SOCKET_FLOAT, 0.3f); } -void GlassBsdfNode::optimize(Scene *scene) +void GlassBsdfNode::simplify_settings(Scene *scene) { if(distribution_orig == "") { distribution_orig = distribution; @@ -2019,7 +2019,7 @@ RefractionBsdfNode::RefractionBsdfNode() add_input("IOR", SHADER_SOCKET_FLOAT, 0.3f); } -void RefractionBsdfNode::optimize(Scene *scene) +void RefractionBsdfNode::simplify_settings(Scene *scene) { if(distribution_orig == "") { distribution_orig = distribution; @@ -3955,6 +3955,21 @@ BlackbodyNode::BlackbodyNode() add_output("Color", SHADER_SOCKET_COLOR); } +bool BlackbodyNode::constant_fold(ShaderOutput *socket, float3 *optimized_value) +{ + ShaderInput *temperature_in = input("Temperature"); + + if(socket == output("Color")) { + if(temperature_in->link == NULL) { + *optimized_value = svm_math_blackbody_color(temperature_in->value.x); + + return true; + } + } + + return false; +} + void BlackbodyNode::compile(SVMCompiler& compiler) { ShaderInput *temperature_in = input("Temperature"); @@ -3962,15 +3977,8 @@ void BlackbodyNode::compile(SVMCompiler& compiler) compiler.stack_assign(color_out); - if(temperature_in->link == NULL) { - float3 color = svm_math_blackbody_color(temperature_in->value.x); - compiler.add_node(NODE_VALUE_V, color_out->stack_offset); - compiler.add_node(NODE_VALUE_V, color); - } - else { - compiler.stack_assign(temperature_in); - compiler.add_node(NODE_BLACKBODY, temperature_in->stack_offset, color_out->stack_offset); - } + compiler.stack_assign(temperature_in); + compiler.add_node(NODE_BLACKBODY, temperature_in->stack_offset, color_out->stack_offset); } void BlackbodyNode::compile(OSLCompiler& compiler) @@ -4054,28 +4062,36 @@ static ShaderEnum math_type_init() ShaderEnum MathNode::type_enum = math_type_init(); -void MathNode::compile(SVMCompiler& compiler) +bool MathNode::constant_fold(ShaderOutput *socket, float3 *optimized_value) { ShaderInput *value1_in = input("Value1"); ShaderInput *value2_in = input("Value2"); - ShaderOutput *value_out = output("Value"); - compiler.stack_assign(value_out); + if(socket == output("Value")) { + /* Optimize math node without links to a single value. */ + if(value1_in->link == NULL && value2_in->link == NULL) { + optimized_value->x = svm_math((NodeMath)type_enum[type], + value1_in->value.x, + value2_in->value.x); + + if(use_clamp) { + optimized_value->x = saturate(optimized_value->x); + } - /* Optimize math node without links to a single value node. */ - if(value1_in->link == NULL && value2_in->link == NULL) { - float optimized_value = svm_math((NodeMath)type_enum[type], - value1_in->value.x, - value2_in->value.x); - if(use_clamp) { - optimized_value = saturate(optimized_value); + return true; } - compiler.add_node(NODE_VALUE_F, - __float_as_int(optimized_value), - value_out->stack_offset); - return; } + return false; +} + +void MathNode::compile(SVMCompiler& compiler) +{ + ShaderInput *value1_in = input("Value1"); + ShaderInput *value2_in = input("Value2"); + ShaderOutput *value_out = output("Value"); + + compiler.stack_assign(value_out); compiler.stack_assign(value1_in); compiler.stack_assign(value2_in); @@ -4124,6 +4140,35 @@ static ShaderEnum vector_math_type_init() ShaderEnum VectorMathNode::type_enum = vector_math_type_init(); +bool VectorMathNode::constant_fold(ShaderOutput *socket, float3 *optimized_value) +{ + ShaderInput *vector1_in = input("Vector1"); + ShaderInput *vector2_in = input("Vector2"); + + float value; + float3 vector; + + /* Optimize vector math node without links to a single value node. */ + if(vector1_in->link == NULL && vector2_in->link == NULL) { + svm_vector_math(&value, + &vector, + (NodeVectorMath)type_enum[type], + vector1_in->value, + vector2_in->value); + + if(socket == output("Value")) { + optimized_value->x = value; + return true; + } + else if (socket == output("Vector")) { + *optimized_value = vector; + return true; + } + } + + return false; +} + void VectorMathNode::compile(SVMCompiler& compiler) { ShaderInput *vector1_in = input("Vector1"); @@ -4134,25 +4179,6 @@ void VectorMathNode::compile(SVMCompiler& compiler) compiler.stack_assign(value_out); compiler.stack_assign(vector_out); - /* Optimize vector math node without links to a single value node. */ - if(vector1_in->link == NULL && vector2_in->link == NULL) { - float optimized_value; - float3 optimized_vector; - svm_vector_math(&optimized_value, - &optimized_vector, - (NodeVectorMath)type_enum[type], - vector1_in->value, - vector2_in->value); - - compiler.add_node(NODE_VALUE_F, - __float_as_int(optimized_value), - value_out->stack_offset); - - compiler.add_node(NODE_VALUE_V, vector_out->stack_offset); - compiler.add_node(NODE_VALUE_V, optimized_vector); - return; - } - compiler.stack_assign(vector1_in); compiler.stack_assign(vector2_in); diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 4f4061286cb..259936c0b7c 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -311,7 +311,7 @@ class GlossyBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(GlossyBsdfNode) - void optimize(Scene *scene); + void simplify_settings(Scene *scene); bool has_integrator_dependency(); ustring distribution, distribution_orig; @@ -322,7 +322,7 @@ class GlassBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(GlassBsdfNode) - void optimize(Scene *scene); + void simplify_settings(Scene *scene); bool has_integrator_dependency(); ustring distribution, distribution_orig; @@ -333,7 +333,7 @@ class RefractionBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(RefractionBsdfNode) - void optimize(Scene *scene); + void simplify_settings(Scene *scene); bool has_integrator_dependency(); ustring distribution, distribution_orig; @@ -636,6 +636,7 @@ public: class BlackbodyNode : public ShaderNode { public: SHADER_NODE_CLASS(BlackbodyNode) + bool constant_fold(ShaderOutput *socket, float3 *optimized_value); virtual int get_group() { return NODE_GROUP_LEVEL_3; } }; @@ -644,6 +645,7 @@ class MathNode : public ShaderNode { public: SHADER_NODE_CLASS(MathNode) virtual int get_group() { return NODE_GROUP_LEVEL_1; } + bool constant_fold(ShaderOutput *socket, float3 *optimized_value); bool use_clamp; @@ -663,6 +665,7 @@ class VectorMathNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorMathNode) virtual int get_group() { return NODE_GROUP_LEVEL_1; } + bool constant_fold(ShaderOutput *socket, float3 *optimized_value); ustring type; static ShaderEnum type_enum; -- cgit v1.2.3