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
This commit is contained in:
Thomas Dinges 2015-11-25 13:52:39 +01:00
parent 415b5a4369
commit e796581655
4 changed files with 128 additions and 56 deletions

View File

@ -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<ShaderNode*>& 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<ShaderNode*> done;
constant_fold(done, output());
/* 3: Simplification. */
simplify_nodes(scene);
simplify_settings(scene);
/* 4: De-duplication. */
/* TODO(dingto): Implement */

View File

@ -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<bool>& visited, vector<bool>& on_stack);
void clean(Scene *scene);
void simplify_nodes(Scene *scene);
void simplify_settings(Scene *scene);
void constant_fold(set<ShaderNode*>& visited, ShaderNode *node);
void bump_from_displacement();
void refine_bump_nodes();
void default_inputs(bool do_osl);

View File

@ -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,6 +4062,29 @@ static ShaderEnum math_type_init()
ShaderEnum MathNode::type_enum = math_type_init();
bool MathNode::constant_fold(ShaderOutput *socket, float3 *optimized_value)
{
ShaderInput *value1_in = input("Value1");
ShaderInput *value2_in = input("Value2");
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);
}
return true;
}
}
return false;
}
void MathNode::compile(SVMCompiler& compiler)
{
ShaderInput *value1_in = input("Value1");
@ -4061,21 +4092,6 @@ void MathNode::compile(SVMCompiler& compiler)
ShaderOutput *value_out = output("Value");
compiler.stack_assign(value_out);
/* 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);
}
compiler.add_node(NODE_VALUE_F,
__float_as_int(optimized_value),
value_out->stack_offset);
return;
}
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);

View File

@ -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;