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:
parent
415b5a4369
commit
e796581655
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue