Quellcode durchsuchen

partial optimization of core pass loop : use linked lists instead of arrays for shader list, sort by shader in a given pass.

Nicolas Cannasse vor 11 Jahren
Ursprung
Commit
4a20ce57f0

+ 1 - 1
h2d/RenderContext.hx

@@ -27,7 +27,7 @@ class RenderContext {
 		buffer = new hxd.FloatBuffer();
 		bufPos = 0;
 		manager = new h3d.shader.Manager(["output.position", "output.color"]);
-		pass = new h3d.mat.Pass("",[]);
+		pass = new h3d.mat.Pass("",null);
 		pass.depth(true, Always);
 		pass.culling = None;
 		baseShader = new h3d.shader.Base2d();

+ 2 - 2
h3d/Engine.hx

@@ -82,8 +82,8 @@ class Engine {
 	}
 
 	public function selectShader( shader : hxsl.RuntimeShader ) {
-		driver.selectShader(shader);
-		shaderSwitches++;
+		if( driver.selectShader(shader) )
+			shaderSwitches++;
 	}
 	
 	public function selectMaterial( pass : h3d.mat.Pass ) {

+ 1 - 0
h3d/impl/Driver.hx

@@ -59,6 +59,7 @@ class Driver {
 	}
 	
 	public function selectShader( shader : hxsl.RuntimeShader ) {
+		return false;
 	}
 	
 	public function selectMaterial( pass : h3d.mat.Pass ) {

+ 2 - 1
h3d/impl/GlDriver.hx

@@ -151,7 +151,7 @@ class GlDriver extends Driver {
 				}
 			programs.set(shader.id, p);
 		}
-		if( curProgram == p ) return;
+		if( curProgram == p ) return false;
 		gl.useProgram(p.p);
 		for( i in curAttribs...p.attribs.length ) {
 			gl.enableVertexAttribArray(i);
@@ -160,6 +160,7 @@ class GlDriver extends Driver {
 		while( curAttribs > p.attribs.length )
 			gl.disableVertexAttribArray(--curAttribs);
 		curProgram = p;
+		return true;
 	}
 	
 	override function uploadShaderBuffers( buf : h3d.shader.Buffers, which : h3d.shader.Buffers.BufferKind ) {

+ 14 - 24
h3d/mat/Material.hx

@@ -83,34 +83,24 @@ class Material {
 	}
 	
 	function set_castShadows(v) {
-		if( v ) {
-			var p = getPass("shadow");
-			if( p == null ) {
-				p = new Pass("shadow", [], mainPass);
-				addPass(p);
-			}
-		} else {
-			var p = getPass("shadow");
-			if( p != null ) removePass(p);
-		}
-		return v;
+		if( castShadows == v )
+			return v;
+		if( v )
+			addPass(new Pass("shadow", null, mainPass));
+		else
+			removePass(getPass("shadow"));
+		return castShadows = v;
 	}
 
 	function set_receiveShadows(v) {
+		if( v == receiveShadows )
+			return v;
 		var shadows = h3d.pass.Params.shadowShader;
-		if( v ) {
-			for( p in mainPass.shaders )
-				if( p == shadows )
-					return v;
-			mainPass.shaders.push(shadows);
-		} else {
-			for( p in mainPass.shaders )
-				if( p == shadows ) {
-					mainPass.shaders.remove(p);
-					break;
-				}
-		}
-		return v;
+		if( v )
+			mainPass.addShader(shadows);
+		else
+			mainPass.removeShader(shadows);
+		return receiveShadows = v;
 	}
 
 }

+ 3 - 3
h3d/mat/MeshMaterial.hx

@@ -12,7 +12,7 @@ class MeshMaterial extends Material {
 	public function new(?texture) {
 		mshader = new h3d.shader.BaseMesh();
 		blendMode = Normal;
-		super(new h3d.mat.Pass("default",[mshader]));
+		super(new h3d.mat.Pass("default",new hxsl.ShaderList(mshader)));
 		this.texture = texture;
 	}
 	
@@ -51,13 +51,13 @@ class MeshMaterial extends Material {
 	function set_texture(t) {
 		if( t == null ) {
 			if( textureShader != null ) {
-				mainPass.shaders.remove(textureShader);
+				mainPass.removeShader(textureShader);
 				textureShader = null;
 			}
 		} else {
 			if( textureShader == null ) {
 				textureShader = new h3d.shader.Texture();
-				mainPass.shaders.push(textureShader);
+				mainPass.addShader(textureShader);
 			}
 			textureShader.texture = t;
 		}

+ 37 - 10
h3d/mat/Pass.hx

@@ -9,7 +9,8 @@ class Pass {
 	var passId : Int;
 	var bits : Int = 0;
 	var parentPass : Pass;
-	var shaders : Array<hxsl.Shader>;
+	var parentShaders : hxsl.ShaderList;
+	var shaders : hxsl.ShaderList;
 	var nextPass : Pass;
 	
 	public var enableLights : Bool;
@@ -58,29 +59,55 @@ class Pass {
 	}
 	
 	public function addShader<T:hxsl.Shader>(s:T) : T {
-		shaders.push(s);
+		shaders = new hxsl.ShaderList(s, shaders);
 		return s;
 	}
 	
 	public function removeShader(s) {
-		return shaders.remove(s);
+		var sl = shaders, prev = null;
+		while( sl != null ) {
+			if( sl.s == s ) {
+				if( prev == null )
+					shaders = sl.next;
+				else
+					prev.next = sl.next;
+				return true;
+			}
+			prev = sl;
+			sl = sl.next;
+		}
+		return false;
 	}
 	
-	public function getShader < T:hxsl.Shader > (t:Class<T>) : T {
-		for( s in shaders )
-			if( Std.is(s, t) )
-				return cast s;
+	public function getShader< T:hxsl.Shader >(t:Class<T>) : T {
+		var s = shaders;
+		while( s != null ) {
+			if( Std.is(s.s, t) )
+				return cast s.s;
+			s = s.next;
+		}
 		return null;
 	}
 
 	public inline function getShaders() {
-		return new hxd.impl.ArrayIterator<hxsl.Shader>(shaders);
+		return shaders.iterator();
 	}
 
 	function getShadersRec() {
-		if( parentPass == null )
+		if( parentPass == null || parentShaders == parentPass.shaders )
 			return shaders;
-		return parentPass.getShadersRec().concat(shaders);
+		// relink to our parent shader list
+		var s = shaders, prev = null;
+		while( s != null && s != parentShaders ) {
+			prev = s;
+			s = s.next;
+		}
+		parentShaders = parentPass.shaders;
+		if( prev == null )
+			shaders = parentShaders;
+		else
+			prev.next = parentShaders;
+		return shaders;
 	}
 
 	public function getDebugShaderCode( scene : h3d.scene.Scene, toHxsl = true ) {

+ 48 - 12
h3d/pass/Base.hx

@@ -37,7 +37,9 @@ class Base {
 	}
 	
 	public function compileShader( p : h3d.mat.Pass ) {
-		return manager.compileShaders(p.getShadersRec());
+		var out = [for( s in p.getShadersRec() ) s];
+		out.reverse();
+		return manager.compileShaders(out);
 	}
 	
 	function allocBuffer( s : hxsl.RuntimeShader, shaders : Array<hxsl.Shader> ) {
@@ -48,29 +50,62 @@ class Base {
 	}
 	
 	@:access(h3d.scene)
-	public function draw( ctx : h3d.scene.RenderContext, passes : Object ) {
-		this.ctx = ctx;
-		for( k in ctx.sharedGlobals.keys() )
-			globals.fastSet(k, ctx.sharedGlobals.get(k));
-		setGlobals();
+	function setupShaders( passes : Object ) {
 		var p = passes;
 		var lightInit = false;
+		var instances = [];
 		while( p != null ) {
-			// TODO : use linked list for shaders (no allocation)
 			var shaders = p.pass.getShadersRec();
 			if( p.pass.enableLights && lightSystem != null ) {
-				if( p.pass.parentPass == null ) shaders = shaders.copy();
 				if( !lightInit )
 					lightSystem.initLights(ctx.lights);
 				shaders = lightSystem.computeLight(p.obj, shaders);
 			}
-			var shader = manager.compileShaders(shaders);
-			// TODO : sort passes by shader/textures
+			var count = 0;
+			for( s in shaders )
+				p.shaders[count++] = s;
+			// TODO : allow reversed shader compilation !
+			// reverse
+			for( n in 0...count >> 1 ) {
+				var n2 = count - 1 - n;
+				var tmp = p.shaders[n];
+				p.shaders[n] = p.shaders[n2];
+				p.shaders[n2] = tmp;
+			}
+			for( i in 0...count ) {
+				var s = p.shaders[i];
+ 				s.updateConstants(globals);
+				instances[i] = @:privateAccess s.instance;
+			}
+			instances[count] = null; // mark end
+			p.shader = manager.compileInstances(instances);
+			p = p.next;
+		}
+	}
+	
+	static inline function sortByShader( o1 : Object, o2 : Object ) {
+		var d = o1.shader.id - o2.shader.id;
+		if( d != 0 ) return d;
+		// TODO : sort by textures
+		return 0;
+	}
+	
+	@:access(h3d.scene)
+	public function draw( ctx : h3d.scene.RenderContext, passes : Object ) {
+		this.ctx = ctx;
+		for( k in ctx.sharedGlobals.keys() )
+			globals.fastSet(k, ctx.sharedGlobals.get(k));
+		setGlobals();
+		setupShaders(passes);
+		passes = haxe.ds.ListSort.sortSingleLinked(passes, sortByShader);
+		var p = passes;
+		while( p != null ) {
 			globalModelView = p.obj.absPos;
+			//if( p.shader.hasGlobal(globalModelViewInverseId) )
 			globalModelViewInverse = p.obj.getInvPos();
-			ctx.engine.selectShader(shader);
+			ctx.engine.selectShader(p.shader);
 			// TODO : reuse buffers between calls
-			var buf = allocBuffer(shader, shaders);
+			var buf = allocBuffer(p.shader, p.shaders);
 			ctx.engine.selectMaterial(p.pass);
 			ctx.engine.uploadShaderBuffers(buf, Globals);
 			ctx.engine.uploadShaderBuffers(buf, Params);
@@ -81,6 +116,7 @@ class Base {
 		}
 		ctx.drawPass = null;
 		this.ctx = null;
+		return passes;
 	}
 	
 }

+ 2 - 1
h3d/pass/Distance.hx

@@ -20,8 +20,9 @@ class Distance extends Base {
 			texture = new h3d.mat.Texture(ctx.engine.width, ctx.engine.height, [Target, TargetDepth, TargetNoFlipY]);
 		}
 		ctx.engine.setTarget(texture);
-		super.draw(ctx, passes);
+		passes = super.draw(ctx, passes);
 		ctx.engine.setTarget(null);
+		return passes;
 	}
 	
 }

+ 16 - 3
h3d/pass/LightSystem.hx

@@ -9,12 +9,14 @@ class LightSystem {
 	var lights : h3d.scene.Light;
 	var ambientShader : h3d.shader.AmbientLight;
 	var lightCount : Int;
+	var cachedShaderList : Array<hxsl.ShaderList>;
 	@global("global.ambientLight") public var ambientLight : h3d.Vector;
 	@global("global.perPixelLighting") public var perPixelLighting : Bool;
 
 	public function new(globals) {
 		this.globals = globals;
 		initGlobals();
+		cachedShaderList = [];
 		ambientLight = new h3d.Vector(1, 1, 1);
 		ambientShader = new h3d.shader.AmbientLight();
 	}
@@ -40,7 +42,7 @@ class LightSystem {
 	}
 	
 	@:access(h3d.scene.Object.absPos)
-	public function computeLight( obj : h3d.scene.Object, shaders : Array<hxsl.Shader> ) : Array<hxsl.Shader> {
+	public function computeLight( obj : h3d.scene.Object, shaders : hxsl.ShaderList ) : hxsl.ShaderList {
 		if( lightCount > maxLightsPerObject ) {
 			var l = lights;
 			while( l != null ) {
@@ -49,12 +51,23 @@ class LightSystem {
 			}
 			lights = haxe.ds.ListSort.sortSingleLinked(lights, sortLight);
 		}
-		shaders.push(ambientShader);
+		var k = 0;
+		inline function add( s : hxsl.Shader ) {
+			var sl = cachedShaderList[k++];
+			if( sl == null ) {
+				sl = new hxsl.ShaderList(null);
+				cachedShaderList[k - 1] = sl;
+			}
+			sl.s = s;
+			sl.next = shaders;
+			shaders = sl;
+		}
+		add(ambientShader);
 		var l = lights;
 		var i = 0;
 		while( l != null ) {
 			if( i++ == maxLightsPerObject ) break;
-			shaders.push(l.shader);
+			add(l.shader);
 			l = l.next;
 		}
 		return shaders;

+ 4 - 0
h3d/pass/Object.hx

@@ -3,8 +3,12 @@ package h3d.pass;
 class Object {
 	public var pass : h3d.mat.Pass;
 	public var obj : h3d.scene.Object;
+	public var shaders : Array<hxsl.Shader>;
+	public var shader : hxsl.RuntimeShader;
 	public var index : Int;
 	public var next : Object;
 	public function new() {
+		shaders = [];
+		trace("alloc");
 	}
 }

+ 2 - 1
h3d/pass/ShadowMap.hx

@@ -61,13 +61,14 @@ class ShadowMap extends Base {
 		lightCamera.target.set(ct.x, ct.y, ct.z);
 		lightCamera.pos.set(ct.x - lightDirection.x, ct.y - lightDirection.y, ct.z - lightDirection.z);
 		ctx.engine.setTarget(texture, 0xFFFFFFFF);
-		super.draw(ctx, passes);
+		passes = super.draw(ctx, passes);
 		ctx.engine.setTarget(null);
 		ctx.sharedGlobals.set(shadowMapId, texture);
 		ctx.sharedGlobals.set(shadowProjId, lightCamera.m);
 		ctx.sharedGlobals.set(shadowColorId, color);
 		ctx.sharedGlobals.set(shadowPowerId, power);
 		ctx.sharedGlobals.set(shadowBiasId, bias);
+		return passes;
 	}
 
 	

+ 5 - 1
h3d/scene/RenderContext.hx

@@ -67,12 +67,16 @@ class RenderContext {
 		while( p != null ) {
 			p.obj = null;
 			p.pass = null;
+			p.shader = null;
+			p.index = 0;
+			for( i in 0...p.shaders.length )
+				p.shaders[i] = null;
 			prev = p;
 			p = p.next;
 		}
 		if( prev != null ) {
 			prev.next = pool;
-			pool = prev;
+			pool = passes;
 		}
 		passes = null;
 		lights = null;

+ 16 - 5
h3d/scene/Scene.hx

@@ -102,15 +102,26 @@ class Scene extends Object implements h3d.IDrawable {
 			}
 			prev.next = null;
 			var render = getPass(curPass.pass.name);
-			passes.push( { render : render, pass : curPass, prev : prev, next : p } );
+			passes.push( { render : render, pass : curPass } );
 			curPass = p;
 		}
 		@:privateAccess passes.sort(function(p1, p2) return p2.render.priority - p1.render.priority);
 		for( p in passes )
-			p.render.draw(ctx, p.pass);
-		// restore linked list to reuse
-		for( p in passes )
-			p.prev.next = p.next;
+			p.pass = p.render.draw(ctx, p.pass);
+		
+		// relink pass objects to reuse
+		var count = 0;
+		var prev : h3d.pass.Object = null;
+		for( p in passes ) {
+			var p = p.pass;
+			if( prev != null )
+				prev.next = p;
+			while( p != null ) {
+				prev = p;
+				p = p.next;
+			}
+		}
+		ctx.passes = passes[0].pass;
 		ctx.done();
 		for( p in postPasses )
 			p.render(engine);

+ 5 - 1
h3d/shader/Manager.hx

@@ -127,7 +127,11 @@ class Manager {
 	}
 	
 	public function compileShaders( shaders : Array<hxsl.Shader> ) {
-		var instances = [for( s in shaders ) { s.updateConstants(globals); @:privateAccess s.instance; }];
+		var instances = [for( s in shaders ) if( s != null ) { s.updateConstants(globals); @:privateAccess s.instance; }];
+		return shaderCache.link(instances, output);
+	}
+
+	public inline function compileInstances( instances : Array<hxsl.SharedShader.ShaderInstance> ) {
 		return shaderCache.link(instances, output);
 	}
 

+ 8 - 2
hxsl/Cache.hx

@@ -46,6 +46,7 @@ class Cache {
 			linkCache.set(outVars, c);
 		}
 		for( i in instances ) {
+			if( i == null ) break;
 			if( c.next == null ) c.next = new Map();
 			var cs = c.next.get(i.id);
 			if( cs == null ) {
@@ -56,9 +57,14 @@ class Cache {
 		}
 		if( c.linked != null )
 			return c.linked;
-			
+		
 		var linker = new hxsl.Linker();
-		var s = linker.link([for( s in instances ) s.shader], this.outVars[outVars]);
+		var shaders = [];
+		for( i in instances ) {
+			if( i == null ) break;
+			shaders.push(i.shader);
+		}
+		var s = linker.link(shaders, this.outVars[outVars]);
 		
 		// params tracking
 		var paramVars = new Map();

+ 28 - 0
hxsl/ShaderList.hx

@@ -0,0 +1,28 @@
+package hxsl;
+
+class ShaderList {
+	public var s : hxsl.Shader;
+	public var next : ShaderList;
+	public function new(s, ?n) {
+		this.s = s;
+		this.next = n;
+	}
+	public inline function iterator() {
+		return new ShaderIterator(this);
+	}
+}
+
+private class ShaderIterator {
+	var l : ShaderList;
+	public inline function new(l) {
+		this.l = l;
+	}
+	public inline function hasNext() {
+		return l != null;
+	}
+	public inline function next() {
+		var s = l.s;
+		l = l.next;
+		return s;
+	}
+}