Ver Fonte

use output declaration to generate linker

Nicolas Cannnasse há 8 anos atrás
pai
commit
bf68cefabc

+ 2 - 2
h2d/RenderContext.hx

@@ -20,7 +20,7 @@ class RenderContext extends h3d.impl.RenderContext {
 	public var tmpBounds = new h2d.col.Bounds();
 	var texture : h3d.mat.Texture;
 	var baseShader : h3d.shader.Base2d;
-	var manager : h3d.shader.Manager;
+	var manager : h3d.pass.ShaderManager;
 	var compiledShader : hxsl.RuntimeShader;
 	var buffers : h3d.shader.Buffers;
 	var fixedBuffer : h3d.Buffer;
@@ -51,7 +51,7 @@ class RenderContext extends h3d.impl.RenderContext {
 		if( BUFFERING )
 			buffer = new hxd.FloatBuffer();
 		bufPos = 0;
-		manager = new h3d.shader.Manager(["output.position", "output.color"]);
+		manager = new h3d.pass.ShaderManager();
 		pass = new h3d.mat.Pass("",null);
 		pass.depth(true, Always);
 		pass.culling = None;

+ 4 - 4
h3d/pass/Default.hx

@@ -4,7 +4,7 @@ package h3d.pass;
 @:access(h3d.mat.Pass)
 class Default extends Base {
 
-	var manager : h3d.shader.Manager;
+	var manager : ShaderManager;
 	var globals(get, never) : hxsl.Globals;
 	var cachedBuffer : h3d.shader.Buffers;
 	var tcache : h3d.impl.TextureCache;
@@ -32,7 +32,7 @@ class Default extends Base {
 
 	public function new() {
 		super();
-		manager = new h3d.shader.Manager(getOutputs());
+		manager = new ShaderManager(getOutputs());
 		tcache = new h3d.impl.TextureCache();
 		shaderIdMap = [];
 		textureIdMap = [];
@@ -60,8 +60,8 @@ class Default extends Base {
 		tcache.dispose();
 	}
 
-	function getOutputs() {
-		return ["output.position", "output.color"];
+	function getOutputs() : Array<hxsl.Output> {
+		return [Value("output.color")];
 	}
 
 	override function compileShader( p : h3d.mat.Pass ) {

+ 3 - 3
h3d/pass/Depth.hx

@@ -12,8 +12,8 @@ class Depth extends Default {
 		depthMapId = hxsl.Globals.allocID("depthMap");
 	}
 
-	override function getOutputs() {
-		return ["output.position", "output.depth"];
+	override function getOutputs() : Array<hxsl.Output> {
+		return [PackFloat(Value("output.depth"))];
 	}
 
 	override function draw( passes ) {
@@ -22,7 +22,7 @@ class Depth extends Default {
 		ctx.engine.clear(enableSky ? 0 : 0xFF0000, 1);
 		passes = super.draw(passes);
 		ctx.engine.popTarget();
-		ctx.setGlobalID(depthMapId, texture);
+		ctx.setGlobalID(depthMapId, { texture : texture });
 		return passes;
 	}
 

+ 3 - 4
h3d/pass/HardwarePick.hx

@@ -7,11 +7,10 @@ private class FixedColor extends hxsl.Shader {
 		@param var viewport : Vec4;
 		var output : {
 			position : Vec4,
-			pickPosition : Vec4,
 			colorID : Vec4
 		};
 		function vertex() {
-			output.pickPosition = (output.position + vec4(viewport.xy, 0., 0.) * output.position.w) * vec4(viewport.zw, 1., 1.);
+			output.position = (output.position + vec4(viewport.xy, 0., 0.) * output.position.w) * vec4(viewport.zw, 1., 1.);
 		}
 		function fragment() {
 			output.colorID = colorID;
@@ -51,8 +50,8 @@ class HardwarePick extends Default {
 		#end
 	}
 
-	override function getOutputs() {
-		return ["output.pickPosition", "output.colorID"];
+	override function getOutputs() : Array<hxsl.Output> {
+		return [Value("output.colorID")];
 	}
 
 	override function drawObject(p) {

+ 19 - 8
h3d/pass/MRT.hx

@@ -11,11 +11,11 @@ class MRTSubPass extends Default {
 		this.mrt = m;
 		this.output = output;
 		super();
-		this.varId = hxsl.Globals.allocID(mrt.fragmentOutputs[output] + "Map");
+		this.varId = hxsl.Globals.allocID(mrt.outputNames[output].split(".").pop() + "Map");
 	}
 
 	override function getOutputs() {
-		return ["output.position", "output."+mrt.fragmentOutputs[output]];
+		return [mrt.fragmentOutputs[output]];
 	}
 
 	override function getTexture( index = 0 ) {
@@ -39,28 +39,39 @@ class MRTSubPass extends Default {
 
 class MRT extends Default {
 
-	var fragmentOutputs : Array<String>;
+	var fragmentOutputs : Array<hxsl.Output>;
+	var outputNames : Array<String>;
 	public var clearColors : Array<Null<Int>>;
 	public var clearSameColor : Null<Int>;
 	public var clearDepth : Null<Float>;
 
 	public function new( fragmentOutputs, ?clearSameColor, ?clearDepth, ?clearColors ) {
 		this.fragmentOutputs = fragmentOutputs;
+		this.outputNames = [for( i in 0...fragmentOutputs.length ) getOutputName(i)];
 		this.clearSameColor = clearSameColor;
 		if( clearDepth ) this.clearDepth = 1.;
 		this.clearColors = clearColors;
 		super();
 	}
 
+	function getOutputName(i:Int) {
+		function getRec(v:hxsl.Output) {
+			return switch( v ) {
+			case Value(v): v;
+			case PackFloat(v), PackNormal(v): getRec(v);
+			case Const(v): "Const" + Std.int(v);
+			case Vec2(_), Vec3(_), Vec4(_): "Output" + i;
+			}
+		}
+		return getRec(fragmentOutputs[i]);
+	}
+
 	override function getOutputs() {
-		var out = ["output.position"];
-		for( o in fragmentOutputs )
-			out.push("output." + o);
-		return out;
+		return fragmentOutputs;
 	}
 
 	override function draw(passes:Object) {
-		var tex = [for( i in 0...fragmentOutputs.length ) tcache.allocTarget(fragmentOutputs[i], ctx, ctx.engine.width, ctx.engine.height, true)];
+		var tex = [for( i in 0...fragmentOutputs.length ) tcache.allocTarget(outputNames[i], ctx, ctx.engine.width, ctx.engine.height, true)];
 		if( clearColors != null )
 			for( i in 0...fragmentOutputs.length ) {
 				var color = clearColors[i];

+ 2 - 2
h3d/pass/Normal.hx

@@ -10,8 +10,8 @@ class Normal extends Default {
 		normalMapId = hxsl.Globals.allocID("normalMap");
 	}
 
-	override function getOutputs() {
-		return ["output.position", "output.normal"];
+	override function getOutputs() : Array<hxsl.Output> {
+		return [PackNormal(Value("output.normal"))];
 	}
 
 	override function draw( passes ) {

+ 2 - 2
h3d/pass/ScreenFx.hx

@@ -4,7 +4,7 @@ class ScreenFx<T:hxsl.Shader> {
 
 	public var shader : T;
 	var pass : h3d.mat.Pass;
-	var manager : h3d.shader.Manager;
+	var manager : ShaderManager;
 	var plan : h3d.prim.Primitive;
 	var engine : h3d.Engine;
 	var shaders : hxsl.ShaderList;
@@ -13,7 +13,7 @@ class ScreenFx<T:hxsl.Shader> {
 	public function new(shader) {
 		this.shader = shader;
 		shaders = new hxsl.ShaderList(shader);
-		manager = new h3d.shader.Manager(["output.position", "output.color"]);
+		manager = new ShaderManager();
 		pass = new h3d.mat.Pass(Std.string(this), new hxsl.ShaderList(shader));
 		pass.culling = None;
 		pass.depth(false, Always);

+ 21 - 12
h3d/shader/Manager.hx → h3d/pass/ShaderManager.hx

@@ -1,18 +1,24 @@
-package h3d.shader;
+package h3d.pass;
 
-class Manager {
+class ShaderManager {
 
 	public var globals : hxsl.Globals;
 	var shaderCache : hxsl.Cache;
-	var output : Int;
+	var currentOutput : hxsl.ShaderList;
 
-	public function new(output) {
+	public function new(?output:Array<hxsl.Output>) {
 		shaderCache = hxsl.Cache.get();
 		#if flash
 		shaderCache.constsToGlobal = true;
 		#end
 		globals = new hxsl.Globals();
-		this.output = shaderCache.allocOutputVars(output);
+		currentOutput = new hxsl.ShaderList(null);
+		setOutput(output);
+	}
+
+	public function setOutput( ?output : Array<hxsl.Output> ) {
+		if( output == null ) output = [Value("output.color")];
+		currentOutput.s = shaderCache.getLinkShader(output);
 	}
 
 	@:noDebug
@@ -150,14 +156,14 @@ class Manager {
 		}
 		var si = shaders;
 		var n = p.instance;
-		while( n-- > 0 ) si = si.next;
+		while( --n > 0 ) si = si.next;
 		var v = si.s.getParamValue(p.index);
 		if( v == null && !opt ) throw "Missing param value " + si.s + "." + p.name;
 		return v;
 	}
 
-	public function fillGlobals( buf : Buffers, s : hxsl.RuntimeShader ) {
-		inline function fill(buf:Buffers.ShaderBuffers, s:hxsl.RuntimeShader.RuntimeShaderData) {
+	public function fillGlobals( buf : h3d.shader.Buffers, s : hxsl.RuntimeShader ) {
+		inline function fill(buf:h3d.shader.Buffers.ShaderBuffers, s:hxsl.RuntimeShader.RuntimeShaderData) {
 			var g = s.globals;
 			while( g != null ) {
 				var v = globals.fastGet(g.gid);
@@ -177,14 +183,14 @@ class Manager {
 		fill(buf.fragment, s.fragment);
 	}
 
-	public function fillParams( buf : Buffers, s : hxsl.RuntimeShader, shaders : hxsl.ShaderList ) {
-		inline function fill(buf:Buffers.ShaderBuffers, s:hxsl.RuntimeShader.RuntimeShaderData) {
+	public function fillParams( buf : h3d.shader.Buffers, s : hxsl.RuntimeShader, shaders : hxsl.ShaderList ) {
+		inline function fill(buf:h3d.shader.Buffers.ShaderBuffers, s:hxsl.RuntimeShader.RuntimeShaderData) {
 			var p = s.params;
 			while( p != null ) {
 				if( p.type == TFloat && p.perObjectGlobal == null ) {
 					var si = shaders;
 					var n = p.instance;
-					while( n-- > 0 ) si = si.next;
+					while( --n > 0 ) si = si.next;
 					buf.params[p.pos] = si.s.getParamFloatValue(p.index);
 					p = p.next;
 					continue;
@@ -216,7 +222,10 @@ class Manager {
 	public function compileShaders( shaders : hxsl.ShaderList ) {
 		globals.resetChannels();
 		for( s in shaders ) s.updateConstants(globals);
-		return shaderCache.link(shaders, output);
+		currentOutput.next = shaders;
+		var s = shaderCache.link(currentOutput);
+		currentOutput.next = null;
+		return s;
 	}
 
 }

+ 3 - 3
h3d/pass/ShadowMap.hx

@@ -105,8 +105,8 @@ class ShadowMap extends Default {
 		bounds.scaleCenter(1.01);
 	}
 
-	override function getOutputs() {
-		return ["output.position", "output.depth"];
+	override function getOutputs() : Array<hxsl.Output> {
+		return [PackFloat(Value("output.depth"))];
 	}
 
 	override function setGlobals() {
@@ -147,7 +147,7 @@ class ShadowMap extends Default {
 		if( blur.quality > 0 && blur.passes > 0 )
 			blur.apply(texture, tcache.allocTarget("tmpBlur", ctx, size, size, false), true);
 
-		ctx.setGlobalID(shadowMapId, texture);
+		ctx.setGlobalID(shadowMapId, { texture : texture });
 		ctx.setGlobalID(shadowProjId, lightCamera.m);
 		ctx.setGlobalID(shadowColorId, color);
 		ctx.setGlobalID(shadowPowerId, power);

+ 4 - 4
h3d/shader/BaseMesh.hx

@@ -31,8 +31,8 @@ class BaseMesh extends hxsl.Shader {
 		var output : {
 			var position : Vec4;
 			var color : Vec4;
-			var depth : Vec4;
-			var normal : Vec4;
+			var depth : Float;
+			var normal : Vec3;
 		};
 
 		var relativePosition : Vec3;
@@ -81,8 +81,8 @@ class BaseMesh extends hxsl.Shader {
 
 		function fragment() {
 			output.color = pixelColor;
-			output.depth = pack(depth);
-			output.normal = packNormal(transformedNormal);
+			output.depth = depth;
+			output.normal = transformedNormal;
 		}
 
 	};

+ 0 - 30
h3d/shader/ChannelSelect.hx

@@ -1,30 +0,0 @@
-package h3d.shader;
-
-class ChannelSelect extends hxsl.Shader {
-
-	static var SRC = {
-
-		var pixelColor : Vec4;
-		@const var bits : Int;
-
-		function __init__() {
-			if( bits == 1 )
-				pixelColor = pixelColor.rrrr;
-			else if( bits == 2 )
-				pixelColor = pixelColor.gggg;
-			else if( bits == 4 )
-				pixelColor = pixelColor.bbbb;
-			else if( bits == 8 )
-				pixelColor = pixelColor.aaaa;
-			else
-				pixelColor = vec4(0.);
-		}
-
-	}
-
-	public function new( bits : Int ) {
-		super();
-		this.bits = bits;
-	}
-
-}

+ 4 - 4
h3d/shader/SAO.hx

@@ -7,8 +7,8 @@ class SAO extends ScreenShader {
 		@range(4,30) @const var numSamples : Int;
 		@range(1,10) @const var numSpiralTurns : Int;
 
-		@ignore @param var depthTexture : Sampler2D;
-		@ignore @param var normalTexture : Sampler2D;
+		@ignore @param var depthTexture : Channel;
+		@ignore @param var normalTexture : Channel3;
 		@param var noiseTexture : Sampler2D;
 		@param var noiseScale : Vec2;
 		@range(0,10) @param var sampleRadius : Float;
@@ -43,7 +43,7 @@ class SAO extends ScreenShader {
 		}
 
 		function getPosition( uv : Vec2 ) : Vec3 {
-			var depth = unpack(depthTexture.get(uv));
+			var depth = depthTexture.get(uv);
 			var uv2 = (uv - 0.5) * vec2(2, -2);
 			var temp = vec4(uv2, depth, 1) * cameraInverseViewProj;
 			var originWS = temp.xyz / temp.w;
@@ -55,7 +55,7 @@ class SAO extends ScreenShader {
 			var vUV = input.uv;
 			var occlusion = 0.0;
 			var origin = getPosition(vUV);
-			var normal = unpackNormal(normalTexture.get(vUV));
+			var normal = normalTexture.get(vUV);
 
 			var sampleNoise = noiseTexture.get(vUV * noiseScale / screenRatio).x;
 			var randomPatternRotationAngle = 2.0 * PI * sampleNoise;

+ 2 - 2
h3d/shader/Shadow.hx

@@ -4,7 +4,7 @@ class Shadow extends hxsl.Shader {
 
 	static var SRC = {
 		@global var shadow : {
-			map : Sampler2D,
+			map : Channel,
 			proj : Mat3x4,
 			color : Vec3,
 			power : Float,
@@ -24,7 +24,7 @@ class Shadow extends hxsl.Shader {
 
 			var shadowPos = if( perPixel ) pixelTransformedPosition * shadow.proj * vec3(0.5, -0.5, 1) + vec3(0.5, 0.5, 0) else shadowPos;
 
-			var depth = unpack(shadow.map.get(shadowPos.xy));
+			var depth = shadow.map.get(shadowPos.xy);
 
 			#if false
 			// TODO : integrate surface-based bias

+ 121 - 28
hxsl/Cache.hx

@@ -11,44 +11,137 @@ class SearchMap {
 
 class Cache {
 
-	var linkCache : Map<Int,SearchMap>;
-	var outVarsMap : Map<String, Int>;
-	var outVars : Array<Array<String>>;
+	var linkCache : SearchMap;
+	var linkShaders : Map<String, Shader>;
 	var byID : Map<String, RuntimeShader>;
 	public var constsToGlobal : Bool;
 
 	function new() {
 		constsToGlobal = false;
-		linkCache = new Map();
-		outVarsMap = new Map();
-		outVars = [];
+		linkCache = new SearchMap();
+		linkShaders = new Map();
 		byID = new Map();
 	}
 
-	public function allocOutputVars( vars : Array<String> ) {
+	/**
+		Creates a shader that generate the output requested.
+	**/
+	public function getLinkShader( vars : Array<Output> ) {
 		var key = vars.join(",");
-		var id = outVarsMap.get(key);
-		if( id != null )
-			return id;
-		vars = vars.copy();
-		id = outVarsMap.get(vars.join(","));
-		if( id != null ) {
-			outVarsMap.set(key, id);
-			return id;
+		var shader = linkShaders.get(key);
+		if( shader != null )
+			return shader;
+		var s = new hxsl.SharedShader("");
+		var v = vars.copy();
+		s.data = {
+			name : "shaderLinker",
+			vars : [],
+			funs : [],
+		};
+		var pos = null;
+		var outVars = new Map<String,TVar>();
+		var outputCount = 0;
+		var tvec4 = TVec(4, VFloat);
+		function makeVec( g, size, args : Array<Output>, makeOutExpr : Output -> Int -> TExpr ) {
+			var out = [];
+			var rem = size;
+			for( i in 0...args.length ) {
+				var e = makeOutExpr(args[args.length - 1 - i], rem - (args.length - 1 - i));
+				rem -= Tools.size(e.t);
+				out.unshift(e);
+			}
+			return { e : TCall({ e : TGlobal(g), t : TVoid, p : pos }, out), t : TVec(size,VFloat), p : pos };
 		}
-		id = outVars.length;
-		outVars.push(vars);
-		outVarsMap.set(key, id);
-		return id;
+		function makeVar( name : String, t, parent : TVar ) {
+			var path = parent == null ? name : parent.getName() + "." + name;
+			var v = outVars.get(path);
+			if( v != null )
+				return v;
+			v = {
+				id : Tools.allocVarId(),
+				name : name,
+				type : t,
+				kind : Var,
+				parent : parent,
+			};
+			if( parent == null )
+				s.data.vars.push(v);
+			else {
+				switch( parent.type ) {
+				case TStruct(vl): vl.push(v);
+				default: throw "assert";
+				}
+			}
+			outVars.set(path, v);
+			return v;
+		}
+
+		function makeOutExpr( v : Output, rem : Int ) : TExpr {
+			switch( v ) {
+			case Const(v):
+				return { e : TConst(CFloat(v)), t : TFloat, p : pos };
+			case Vec2(args):
+				return makeVec(Vec2, 2, args, makeOutExpr);
+			case Vec3(args):
+				return makeVec(Vec3, 3, args, makeOutExpr);
+			case Vec4(args):
+				return makeVec(Vec4, 4, args, makeOutExpr);
+			case Value(vname):
+				var v = outVars.get(vname);
+				if( v != null )
+					return { e : TVar(v), t : v.type, p : pos };
+				var path = vname.split(".");
+				var parent : TVar = null;
+				while( path.length > 1 )
+					parent = makeVar(path.shift(), TStruct([]), parent);
+				v = makeVar(path.shift(), rem == 1 ? TFloat : TVec(rem, VFloat), parent);
+				return { e : TVar(v), t : v.type, p : pos };
+			case PackNormal(v):
+				return { e : TCall({ e : TGlobal(PackNormal), t : TVoid, p : pos }, [makeOutExpr(v,3)]), t : tvec4, p : pos };
+			case PackFloat(v):
+				return { e : TCall({ e : TGlobal(Pack), t : TVoid, p : pos }, [makeOutExpr(v,1)]), t : tvec4, p : pos };
+			}
+		}
+		function makeOutput( v : Output ) : TExpr {
+			var ov : TVar = {
+				id : Tools.allocVarId(),
+				type : tvec4,
+				name : "OUTPUT" + (outputCount++),
+				kind : Output,
+			};
+			s.data.vars.push(ov);
+			return { e : TBinop(OpAssign,{ e : TVar(ov), t : tvec4, p : pos }, makeOutExpr(v,4)), t : TVoid, p : pos };
+		}
+		function defineFun( kind : FunctionKind, vars : Array<Output> ) {
+			var fv : TVar = {
+				id : Tools.allocVarId(),
+				type : TFun([]),
+				name : ("" + kind).toLowerCase(),
+				kind : Function,
+			};
+			var f : TFunction = {
+				kind : kind,
+				ref : fv,
+				args : [],
+				ret : TVoid,
+				expr : { e : TBlock([for( v in vars ) makeOutput(v)]), p : pos, t : TVoid },
+			};
+			s.data.funs.push(f);
+		}
+		defineFun(Vertex, [Value("output.position")]);
+		defineFun(Fragment, vars);
+
+		shader = std.Type.createEmptyInstance(Shader);
+		@:privateAccess shader.shader = s;
+		linkShaders.set(key, shader);
+		@:privateAccess shader.updateConstantsFinal(null);
+
+		return shader;
 	}
 
 	@:noDebug
-	public function link( shaders : hxsl.ShaderList, outVars : Int ) {
-		var c = linkCache.get(outVars);
-		if( c == null ) {
-			c = new SearchMap();
-			linkCache.set(outVars, c);
-		}
+	public function link( shaders : hxsl.ShaderList ) {
+		var c = linkCache;
 		for( s in shaders ) {
 			var i = @:privateAccess s.instance;
 			if( c.next == null ) c.next = new Map();
@@ -60,11 +153,11 @@ class Cache {
 			c = cs;
 		}
 		if( c.linked == null )
-			c.linked = compileRuntimeShader(shaders, outVars);
+			c.linked = compileRuntimeShader(shaders);
 		return c.linked;
 	}
 
-	function compileRuntimeShader( shaders : hxsl.ShaderList, outVars : Int ) {
+	function compileRuntimeShader( shaders : hxsl.ShaderList ) {
 		var shaderDatas = [];
 		var index = 0;
 		for( s in shaders ) {
@@ -79,7 +172,7 @@ class Cache {
 		#end
 
 		var linker = new hxsl.Linker();
-		var s = linker.link([for( s in shaderDatas ) s.inst.shader], this.outVars[outVars]);
+		var s = linker.link([for( s in shaderDatas ) s.inst.shader]);
 
 		#if debug
 		Printer.check(s,[for( s in shaderDatas ) s.inst.shader]);

+ 6 - 6
hxsl/Checker.hx

@@ -110,11 +110,11 @@ class Checker {
 			case PackNormal:
 				[ { args : [ { name : "value", type : TVec(3, VFloat) } ], ret : TVec(4, VFloat) } ];
 			case ChannelRead:
-				[ 
-					{ args : [ { name : "channel", type : TChannel(1) }, { name : "uv", type : vec2 } ], ret : TFloat }, 
-					{ args : [ { name : "channel", type : TChannel(2) }, { name : "uv", type : vec2 } ], ret : TVec(2,VFloat) }, 
-					{ args : [ { name : "channel", type : TChannel(3) }, { name : "uv", type : vec2 } ], ret : TVec(3,VFloat) }, 
-					{ args : [ { name : "channel", type : TChannel(4) }, { name : "uv", type : vec2 } ], ret : TVec(4,VFloat) }, 
+				[
+					{ args : [ { name : "channel", type : TChannel(1) }, { name : "uv", type : vec2 } ], ret : TFloat },
+					{ args : [ { name : "channel", type : TChannel(2) }, { name : "uv", type : vec2 } ], ret : TVec(2,VFloat) },
+					{ args : [ { name : "channel", type : TChannel(3) }, { name : "uv", type : vec2 } ], ret : TVec(3,VFloat) },
+					{ args : [ { name : "channel", type : TChannel(4) }, { name : "uv", type : vec2 } ], ret : TVec(4,VFloat) },
 				];
 			case Trace:
 				[];
@@ -578,7 +578,7 @@ class Checker {
 		case EVars(vl):
 			for( v in vl ) {
 				if( v.kind == null ) {
-					if( v.name == "output" ) v.kind = Output else v.kind = Local;
+					v.kind = Local;
 					for( q in v.qualifiers )
 						switch( q ) {
 						case Const(_): v.kind = Param;

+ 1 - 1
hxsl/Eval.hx

@@ -183,7 +183,7 @@ class Eval {
 				var zero = { e : TConst(CFloat(0.)), t : TFloat, p : pos };
 				if( count == 1 )
 					return zero.e;
-				return TCall({ e : TGlobal([Vec2, Vec3, Vec4][count - 1]), t : TVoid, p : pos }, [zero]);
+				return TCall({ e : TGlobal([Vec2, Vec3, Vec4][count - 2]), t : TVoid, p : pos }, [zero]);
 			case PackedFloat:
 				return TCall({ e : TGlobal(Unpack), t:TVoid, p:pos}, [tget]);
 			case PackedNormal:

+ 8 - 10
hxsl/Linker.hx

@@ -140,7 +140,7 @@ class Linker {
 			id : vid,
 			name : vname,
 			type : v.type,
-			kind : v.kind == Output ? Local : v.kind,
+			kind : v.kind,
 			qualifiers : v.qualifiers,
 			parent : parent == null ? null : parent.v,
 		};
@@ -316,7 +316,7 @@ class Linker {
 		}
 	}
 
-	public function link( shadersData : Array<ShaderData>, outVars : Array<String> ) : ShaderData {
+	public function link( shadersData : Array<ShaderData> ) : ShaderData {
 		debug("---------------------- LINKING -----------------------");
 		varMap = new Map();
 		varIdMap = new Map();
@@ -335,9 +335,12 @@ class Linker {
 
 		// globalize vars
 		curInstance = 0;
+		var outVars = [];
 		for( s in shadersData ) {
-			for( v in s.vars )
+			for( v in s.vars ) {
 				allocVar(v, null);
+				if( v.kind == Output ) outVars.push(v);
+			}
 			for( f in s.funs ) {
 				var v = allocVar(f.ref, f.expr.p);
 				v.kind = f.kind;
@@ -381,13 +384,8 @@ class Linker {
 		// build dependency tree
 		var entry = new ShaderInfos("<entry>", false);
 		entry.deps = new Map();
-		for( outVar in outVars ) {
-			var v = varMap.get(outVar);
-			if( v == null )
-				throw "Variable not found " + outVar;
-			v.v.kind = Output;
-			buildDependency(entry, v, false);
-		}
+		for( v in outVars )
+			buildDependency(entry, allocVar(v,null), false);
 
 		// force shaders containing discard to be included
 		for( s in shaders )

+ 11 - 0
hxsl/Output.hx

@@ -0,0 +1,11 @@
+package hxsl;
+
+enum Output {
+	Const( v : Float);
+	Value( v : String );
+	PackNormal( v : Output );
+	PackFloat( v : Output );
+	Vec2( a : Array<Output> );
+	Vec3( a : Array<Output> );
+	Vec4( a : Array<Output> );
+}

+ 3 - 1
hxsl/SharedShader.hx

@@ -44,9 +44,11 @@ class SharedShader {
 
 	public function new(src:String) {
 		instanceCache = new Map();
-		data = haxe.Unserializer.run(src);
 		consts = null;
 		globals = [];
+		if( src == "" )
+			return;
+		data = haxe.Unserializer.run(src);
 		for( v in data.vars )
 			browseVar(v);
 		// don't try to optimize if consts is null, we need to do a few things in Eval

+ 1 - 1
samples/Sao.hx

@@ -20,7 +20,7 @@ class CustomRenderer extends h3d.scene.Renderer {
 		sao.shader.sampleRadius	= 0.2;
 		hasMRT = h3d.Engine.getCurrent().driver.hasFeature(MultipleRenderTargets);
 		if( hasMRT )
-			def = new h3d.pass.MRT(["color", "depth", "normal"], 0, true);
+			def = new h3d.pass.MRT([Value("output.color"), PackFloat(Value("output.depth")), PackNormal(Value("output.normal"))], 0, true);
 	}
 
 	override function renderPass(name, p:h3d.pass.Base, passes) {