Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2014-04-18 15:40:30 +0400
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2014-04-18 16:40:19 +0400
commit2a9ef256b1e6cea833855d9517916b9656527672 (patch)
treedd91b03941b78eb8b4af33f8b72029441195c287 /intern/cycles
parent2d7b53331c80e878d48e3c2a6bdc81fef9a1a6e0 (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')
-rw-r--r--intern/cycles/kernel/svm/svm.h9
-rw-r--r--intern/cycles/kernel/svm/svm_types.h3
-rw-r--r--intern/cycles/render/svm.cpp72
3 files changed, 72 insertions, 12 deletions
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index 96c7cefbcb2..49351d14443 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -232,8 +232,13 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
case NODE_ADD_CLOSURE:
svm_node_add_closure(sd, stack, node.y, node.z, &offset, &randb, &closure_weight);
break;
- case NODE_JUMP:
- offset = node.y;
+ case NODE_JUMP_IF_ZERO:
+ if(stack_load_float(stack, node.z) == 0.0f)
+ offset += node.y;
+ break;
+ case NODE_JUMP_IF_ONE:
+ if(stack_load_float(stack, node.z) == 1.0f)
+ offset += node.y;
break;
#ifdef __IMAGE_TEXTURES__
case NODE_TEX_IMAGE:
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index 4381bfe0996..866208106dc 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -36,7 +36,8 @@ typedef enum NodeType {
NODE_CLOSURE_SET_WEIGHT,
NODE_CLOSURE_WEIGHT,
NODE_MIX_CLOSURE,
- NODE_JUMP,
+ NODE_JUMP_IF_ZERO,
+ NODE_JUMP_IF_ONE,
NODE_TEX_IMAGE,
NODE_TEX_IMAGE_BOX,
NODE_TEX_SKY,
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 */