[Bf-blender-cvs] [2a9ef25] master: Cycles: SVM optimization for mix shaders, to skip more code when the mix weight for one of the input shaders is zero.

Brecht Van Lommel noreply at git.blender.org
Fri Apr 18 14:55:27 CEST 2014


Commit: 2a9ef256b1e6cea833855d9517916b9656527672
Author: Brecht Van Lommel
Date:   Fri Apr 18 13:40:30 2014 +0200
https://developer.blender.org/rB2a9ef256b1e6cea833855d9517916b9656527672

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.

===================================================================

M	intern/cycles/kernel/svm/svm.h
M	intern/cycles/kernel/svm/svm_types.h
M	intern/cycles/render/svm.cpp

===================================================================

diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index 96c7cef..49351d1 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 4381bfe..8662081 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 c004187..8035b01 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 */




More information about the Bf-blender-cvs mailing list