diff options
author | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2014-04-18 15:40:30 +0400 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2014-04-18 16:40:19 +0400 |
commit | 2a9ef256b1e6cea833855d9517916b9656527672 (patch) | |
tree | dd91b03941b78eb8b4af33f8b72029441195c287 /intern/cycles/render | |
parent | 2d7b53331c80e878d48e3c2a6bdc81fef9a1a6e0 (diff) |
Cycles: SVM optimization for mix shaders, to skip more code when the mix weight
for one of the input shaders is zero.
This gives about 5% speedup for koro_final.blend. In general this is important
so you can design shaders that run faster for shadows, diffuse bounces, etc, for
example by skipping procedural textures or even using a single fixed color.
Diffstat (limited to 'intern/cycles/render')
-rw-r--r-- | intern/cycles/render/svm.cpp | 72 |
1 files changed, 63 insertions, 9 deletions
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index c004187a091..8035b0129ca 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -515,11 +515,6 @@ void SVMCompiler::generate_closure(ShaderNode *node, set<ShaderNode*>& done) void SVMCompiler::generate_multi_closure(ShaderNode *node, set<ShaderNode*>& done, set<ShaderNode*>& closure_done) { - /* todo: the weak point here is that unlike the single closure sampling - * we will evaluate all nodes even if they are used as input for closures - * that are unused. it's not clear what would be the best way to skip such - * nodes at runtime, especially if they are tangled up */ - /* only generate once */ if(closure_done.find(node) != closure_done.end()) return; @@ -530,11 +525,70 @@ void SVMCompiler::generate_multi_closure(ShaderNode *node, set<ShaderNode*>& don /* weighting is already taken care of in ShaderGraph::transform_multi_closure */ ShaderInput *cl1in = node->input("Closure1"); ShaderInput *cl2in = node->input("Closure2"); + ShaderInput *facin = node->input("Fac"); + + /* skip empty mix/add closure nodes */ + if(!cl1in->link && !cl2in->link) + return; + + if(facin && facin->link) { + /* mix closure: generate instructions to compute mix weight */ + set<ShaderNode*> dependencies; + find_dependencies(dependencies, done, facin); + generate_svm_nodes(dependencies, done); + + stack_assign(facin); /* XXX unassign? */ + + /* execute shared dependencies. this is needed to allow skipping + * of zero weight closures and their dependencies later, so we + * ensure that they only skip dependencies that are unique to them */ + set<ShaderNode*> cl1deps, cl2deps, shareddeps; + + find_dependencies(cl1deps, done, cl1in); + find_dependencies(cl2deps, done, cl2in); + + set_intersection(cl1deps.begin(), cl1deps.end(), + cl2deps.begin(), cl2deps.end(), + std::inserter(shareddeps, shareddeps.begin())); + + generate_svm_nodes(shareddeps, done); + + /* generate instructions for input closure 1 */ + if(cl1in->link) { + /* add instruction to skip closure and its dependencies if mix weight is zero */ + svm_nodes.push_back(make_int4(NODE_JUMP_IF_ONE, 0, facin->stack_offset, 0)); + int node_jump_skip_index = svm_nodes.size() - 1; - if(cl1in->link) - generate_multi_closure(cl1in->link->parent, done, closure_done); - if(cl2in->link) - generate_multi_closure(cl2in->link->parent, done, closure_done); + generate_multi_closure(cl1in->link->parent, done, closure_done); + + /* fill in jump instruction location to be after closure */ + svm_nodes[node_jump_skip_index].y = svm_nodes.size() - node_jump_skip_index - 1; + } + + /* generate instructions for input closure 2 */ + if(cl2in->link) { + /* add instruction to skip closure and its dependencies if mix weight is zero */ + svm_nodes.push_back(make_int4(NODE_JUMP_IF_ZERO, 0, facin->stack_offset, 0)); + int node_jump_skip_index = svm_nodes.size() - 1; + + generate_multi_closure(cl2in->link->parent, done, closure_done); + + /* fill in jump instruction location to be after closure */ + svm_nodes[node_jump_skip_index].y = svm_nodes.size() - node_jump_skip_index - 1; + } + + /* unassign */ + facin->stack_offset = SVM_STACK_INVALID; // XXX clear? + } + else { + /* execute closures and their dependencies, no runtime checks + * to skip closures here because was already optimized due to + * fixed weight or add closure that always needs both */ + if(cl1in->link) + generate_multi_closure(cl1in->link->parent, done, closure_done); + if(cl2in->link) + generate_multi_closure(cl2in->link->parent, done, closure_done); + } } else { /* execute dependencies for closure */ |