Просмотр исходного кода

review textures handling in shaders: follow more modern glsl (texture2D and textureCube become texture() etc)
added texture array support
optimized clear() for render targets

Nicolas Cannasse 7 лет назад
Родитель
Сommit
ce17d9bb08

+ 10 - 10
h3d/Engine.hx

@@ -4,12 +4,12 @@ import h3d.mat.Data;
 private class TargetTmp {
 	public var t : h3d.mat.Texture;
 	public var next : TargetTmp;
-	public var face : Int;
+	public var layer : Int;
 	public var mipLevel : Int;
-	public function new(t, n, f, m) {
+	public function new(t, n, l, m) {
 		this.t = t;
 		this.next = n;
-		this.face = f;
+		this.layer = l;
 		this.mipLevel = m;
 	}
 }
@@ -45,7 +45,7 @@ class Engine {
 	var targetTmp : TargetTmp;
 	var targetStack : TargetTmp;
 	var currentTargetTex : h3d.mat.Texture;
-	var currentTargetFace : Int;
+	var currentTargetLayer : Int;
 	var currentTargetMip : Int;
 	var needFlushTarget : Bool;
 	var nullTexture : h3d.mat.Texture;
@@ -292,16 +292,16 @@ class Engine {
 		return targetStack == null ? null : targetStack.t;
 	}
 
-	public function pushTarget( tex : h3d.mat.Texture, face = 0, mipLevel = 0 ) {
+	public function pushTarget( tex : h3d.mat.Texture, layer = 0, mipLevel = 0 ) {
 		var c = targetTmp;
 		if( c == null )
-			c = new TargetTmp(tex, targetStack, face, mipLevel);
+			c = new TargetTmp(tex, targetStack, layer, mipLevel);
 		else {
 			targetTmp = c.next;
 			c.t = tex;
 			c.next = targetStack;
 			c.mipLevel = mipLevel;
-			c.face = face;
+			c.layer = layer;
 		}
 		targetStack = c;
 		updateNeedFlush();
@@ -312,7 +312,7 @@ class Engine {
 		if( t == null )
 			needFlushTarget = currentTargetTex != null;
 		else
-			needFlushTarget = currentTargetTex != t.t || currentTargetFace != t.face || currentTargetMip != t.mipLevel;
+			needFlushTarget = currentTargetTex != t.t || currentTargetLayer != t.layer || currentTargetMip != t.mipLevel;
 	}
 
 	public function pushTargets( textures : Array<h3d.mat.Texture> ) {
@@ -345,9 +345,9 @@ class Engine {
 			driver.setRenderTarget(null);
 			currentTargetTex = null;
 		} else {
-			driver.setRenderTarget(t.t, t.face, t.mipLevel);
+			driver.setRenderTarget(t.t, t.layer, t.mipLevel);
 			currentTargetTex = t.t;
-			currentTargetFace = t.face;
+			currentTargetLayer = t.layer;
 			currentTargetMip = t.mipLevel;
 		}
 		needFlushTarget = false;

+ 16 - 14
h3d/impl/DirectXDriver.hx

@@ -356,6 +356,7 @@ class DirectXDriver extends h3d.impl.Driver {
 
 		var rt = t.flags.has(Target);
 		var isCube = t.flags.has(Cube);
+		var isArray = t.flags.has(IsArray);
 
 		var desc = new Texture2dDesc();
 		desc.width = t.width;
@@ -370,6 +371,8 @@ class DirectXDriver extends h3d.impl.Driver {
 			desc.arraySize = 6;
 			desc.misc |= TextureCube;
 		}
+		if( isArray )
+			desc.arraySize = t.layerCount;
 		if( t.flags.has(MipMapped) && !t.flags.has(ManualMipMapGen) ) {
 			desc.bind |= RenderTarget;
 			desc.misc |= GenerateMips;
@@ -383,7 +386,7 @@ class DirectXDriver extends h3d.impl.Driver {
 
 		var vdesc = new ShaderResourceViewDesc();
 		vdesc.format = desc.format;
-		vdesc.dimension = isCube ? TextureCube : Texture2D;
+		vdesc.dimension = isCube ? TextureCube : isArray ? Texture2DArray : Texture2D;
 		vdesc.arraySize = desc.arraySize;
 		vdesc.start = 0; // top mip level
 		vdesc.count = -1; // all mip levels
@@ -477,13 +480,13 @@ class DirectXDriver extends h3d.impl.Driver {
 		tmp.release();
 	}
 
-	override function capturePixels(tex:h3d.mat.Texture, face:Int, mipLevel:Int) : hxd.Pixels {
+	override function capturePixels(tex:h3d.mat.Texture, layer:Int, mipLevel:Int) : hxd.Pixels {
 		var pixels = hxd.Pixels.alloc(tex.width >> mipLevel, tex.height >> mipLevel, tex.format);
-		captureTexPixels(pixels, tex, face, mipLevel);
+		captureTexPixels(pixels, tex, layer, mipLevel);
 		return pixels;
 	}
 
-	function captureTexPixels( pixels: hxd.Pixels, tex:h3d.mat.Texture, face:Int, mipLevel:Int)  {
+	function captureTexPixels( pixels: hxd.Pixels, tex:h3d.mat.Texture, layer:Int, mipLevel:Int)  {
 		var desc = new Texture2dDesc();
 		desc.width = pixels.width;
 		desc.height = pixels.height;
@@ -494,7 +497,7 @@ class DirectXDriver extends h3d.impl.Driver {
 		if( tmp == null )
 			throw "Capture failed: can't create tmp texture";
 
-		tmp.copySubresourceRegion(0,0,0,0,tex.t.res,tex.t.mips * face + mipLevel, null);
+		tmp.copySubresourceRegion(0,0,0,0,tex.t.res,tex.t.mips * layer + mipLevel, null);
 
 		var pitch = 0;
 		var ptr = tmp.map(0, Read, true, pitch);
@@ -642,8 +645,7 @@ class DirectXDriver extends h3d.impl.Driver {
 		ctx.paramsSize = shader.paramsSize;
 		ctx.paramsContent = new hl.Bytes(shader.paramsSize * 16);
 		ctx.paramsContent.fill(0, shader.paramsSize * 16, 0xDD);
-		ctx.texturesCount = shader.textures2DCount + shader.texturesCubeCount;
-		ctx.textures2DCount = shader.textures2DCount;
+		ctx.texturesCount = shader.texturesCount;
 		ctx.bufferCount = shader.bufferCount;
 		ctx.globals = dx.Driver.createBuffer(shader.globalsSize * 16, Dynamic, ConstantBuffer, CpuWrite, None, 0, null);
 		ctx.params = dx.Driver.createBuffer(shader.paramsSize * 16, Dynamic, ConstantBuffer, CpuWrite, None, 0, null);
@@ -680,7 +682,7 @@ class DirectXDriver extends h3d.impl.Driver {
 	}
 
 	var tmpTextures = new Array<h3d.mat.Texture>();
-	override function setRenderTarget(tex:Null<h3d.mat.Texture>, face = 0, mipLevel = 0) {
+	override function setRenderTarget(tex:Null<h3d.mat.Texture>, layer = 0, mipLevel = 0) {
 		if( tex == null ) {
 			curTexture = null;
 			currentDepth = defaultDepth;
@@ -694,7 +696,7 @@ class DirectXDriver extends h3d.impl.Driver {
 			return;
 		}
 		tmpTextures[0] = tex;
-		_setRenderTargets(tmpTextures, face, mipLevel);
+		_setRenderTargets(tmpTextures, layer, mipLevel);
 	}
 
 	function unbind( res ) {
@@ -714,7 +716,7 @@ class DirectXDriver extends h3d.impl.Driver {
 		_setRenderTargets(textures, 0, 0);
 	}
 
-	function _setRenderTargets( textures:Array<h3d.mat.Texture>, face : Int, mipLevel : Int ) {
+	function _setRenderTargets( textures:Array<h3d.mat.Texture>, layer : Int, mipLevel : Int ) {
 		if( textures.length == 0 ) {
 			setRenderTarget(null);
 			return;
@@ -734,13 +736,13 @@ class DirectXDriver extends h3d.impl.Driver {
 			}
 			if( tex.t.rt == null )
 				throw "Can't render to texture which is not allocated with Target flag";
-			var index = mipLevel * 6 + face;
+			var index = mipLevel * tex.layerCount + layer;
 			var rt = tex.t.rt[index];
 			if( rt == null ) {
-				var cube = tex.flags.has(Cube);
-				var v = new dx.Driver.RenderTargetDesc(getTextureFormat(tex), cube ? Texture2DArray : Texture2D);
+				var arr = tex.flags.has(Cube) || tex.flags.has(IsArray);
+				var v = new dx.Driver.RenderTargetDesc(getTextureFormat(tex), arr ? Texture2DArray : Texture2D);
 				v.mipMap = mipLevel;
-				v.firstSlice = face;
+				v.firstSlice = layer;
 				v.sliceCount = 1;
 				rt = Driver.createRenderTargetView(tex.t.res, v);
 				tex.t.rt[index] = rt;

+ 2 - 2
h3d/impl/Driver.hx

@@ -149,7 +149,7 @@ class Driver {
 	public function captureRenderBuffer( pixels : hxd.Pixels ) {
 	}
 
-	public function capturePixels( tex : h3d.mat.Texture, face : Int, mipLevel : Int ) : hxd.Pixels {
+	public function capturePixels( tex : h3d.mat.Texture, layer : Int, mipLevel : Int ) : hxd.Pixels {
 		throw "Can't capture pixels on this platform";
 		return null;
 	}
@@ -190,7 +190,7 @@ class Driver {
 	public function setRenderZone( x : Int, y : Int, width : Int, height : Int ) {
 	}
 
-	public function setRenderTarget( tex : Null<h3d.mat.Texture>, face = 0, mipLevel = 0 ) {
+	public function setRenderTarget( tex : Null<h3d.mat.Texture>, layer = 0, mipLevel = 0 ) {
 	}
 
 	public function setRenderTargets( textures : Array<h3d.mat.Texture> ) {

+ 61 - 25
h3d/impl/GlDriver.hx

@@ -17,6 +17,8 @@ private extern class GL2 extends js.html.webgl.GL {
 	function getUniformBlockIndex( p : Program, name : String ) : Int;
 	function bindBufferBase( target : Int, index : Int, buffer : js.html.webgl.Buffer ) : Void;
 	function uniformBlockBinding( p : Program, blockIndex : Int, blockBinding : Int ) : Void;
+	function framebufferTextureLayer( target : Int, attach : Int, t : js.html.webgl.Texture, level : Int, layer : Int ) : Void;
+	function texImage3D(target : Int, level : Int, internalformat : Int, width : Int, height : Int, depth : Int, border : Int, format : Int, type : Int, source : Dynamic) : Void;
 	static inline var RGBA16F = 0x881A;
 	static inline var RGBA32F = 0x8814;
 	static inline var ALPHA16F = 0x881C;
@@ -30,6 +32,7 @@ private extern class GL2 extends js.html.webgl.GL {
 	static inline var SRGB8_ALPHA = 0x8C43;
 	static inline var DEPTH_COMPONENT24 = 0x81A6;
 	static inline var UNIFORM_BUFFER = 0x8A11;
+	static inline var TEXTURE_2D_ARRAY = 0x8C1A;
 }
 private typedef Uniform = js.html.webgl.UniformLocation;
 private typedef Program = js.html.webgl.Program;
@@ -87,8 +90,7 @@ private class CompiledShader {
 	public var vertex : Bool;
 	public var globals : Uniform;
 	public var params : Uniform;
-	public var textures : Array<Uniform>;
-	public var cubeTextures : Array<Uniform>;
+	public var textures : Array<{ u : Uniform, t : hxsl.Ast.Type }>;
 	public var buffers : Array<Int>;
 	public var shader : hxsl.RuntimeShader.RuntimeShaderData;
 	public function new(s,vertex,shader) {
@@ -154,7 +156,7 @@ class GlDriver extends Driver {
 	var bufferHeight : Int;
 	var curTarget : h3d.mat.Texture;
 	var numTargets : Int;
-	var curTargetFace : Int;
+	var curTargetLayer : Int;
 	var curTargetMip : Int;
 
 	var debug : Bool;
@@ -288,8 +290,26 @@ class GlDriver extends Driver {
 		var prefix = s.vertex ? "vertex" : "fragment";
 		s.globals = gl.getUniformLocation(p.p, prefix + "Globals");
 		s.params = gl.getUniformLocation(p.p, prefix + "Params");
-		s.textures = [for( i in 0...shader.textures2DCount ) gl.getUniformLocation(p.p, prefix + "Textures[" + i + "]")];
-		s.cubeTextures = [for( i in 0...shader.texturesCubeCount ) gl.getUniformLocation(p.p, prefix + "TexturesCube[" + i + "]")];
+		s.textures = [];
+		var index = 0;
+		var curT = null;
+		var name = "";
+		var t = shader.textures;
+		while( t != null ) {
+			if( t.type != curT ) {
+				curT = t.type;
+				name = switch( t.type ) {
+				case TSampler2D: "Textures";
+				case TSamplerCube: "TexturesCube";
+				case TSampler2DArray: "TexturesArray";
+				default: throw "Unsupported texture type "+t.type;
+				}
+				index = 0;
+			}
+			s.textures.push({ u : gl.getUniformLocation(p.p, prefix+name+"["+index+"]"), t : curT });
+			index++;
+			t = t.next;
+		}
 		if( shader.bufferCount > 0 ) {
 			s.buffers = [for( i in 0...shader.bufferCount ) gl.getUniformBlockIndex(p.p,"uniform_buffer"+i)];
 			for( i in 0...shader.bufferCount )
@@ -427,15 +447,18 @@ class GlDriver extends Driver {
 			}
 		case Textures:
 			var tcount = s.textures.length;
-			for( i in 0...s.textures.length + s.cubeTextures.length ) {
+			for( i in 0...s.textures.length ) {
 				var t = buf.tex[i];
-				var isCube = i >= tcount;
+				var pt = s.textures[i];
 				if( t == null || t.isDisposed() ) {
-					if( isCube ) {
-						t = h3d.mat.Texture.defaultCubeTexture();
-					} else {
+					switch( pt.t ) {
+					case TSampler2D:
 						var color = h3d.mat.Defaults.loadingTextureColor;
 						t = h3d.mat.Texture.fromColor(color, (color >>> 24) / 255);
+					case TSamplerCube:
+						t = h3d.mat.Texture.defaultCubeTexture();
+					default:
+						throw "Missing texture";
 					}
 				}
 				if( t != null && t.t == null && t.realloc != null ) {
@@ -444,8 +467,7 @@ class GlDriver extends Driver {
 				}
 				t.lastFrame = frame;
 
-				var pt = isCube ? s.cubeTextures[i - tcount] : s.textures[i];
-				if( pt == null ) continue;
+				if( pt.u == null ) continue;
 				if( boundTextures[i] == t.t ) continue;
 				boundTextures[i] = t.t;
 
@@ -454,9 +476,9 @@ class GlDriver extends Driver {
 					throw "Invalid texture context";
 				#end
 
-				var mode = isCube ? GL.TEXTURE_CUBE_MAP : GL.TEXTURE_2D;
+				var mode = getBindType(t);
 				gl.activeTexture(GL.TEXTURE0 + i);
-				gl.uniform1i(pt, i);
+				gl.uniform1i(pt.u, i);
 				gl.bindTexture(mode, t.t.t);
 				lastActiveIndex = i;
 
@@ -679,9 +701,15 @@ class GlDriver extends Driver {
 		}
 	}
 
+	function getBindType( t : h3d.mat.Texture ) {
+		var isCube = t.flags.has(Cube);
+		var isArray = t.flags.has(IsArray);
+		return isCube ? GL.TEXTURE_CUBE_MAP : isArray ? GL2.TEXTURE_2D_ARRAY : GL.TEXTURE_2D;
+	}
+
 	override function allocTexture( t : h3d.mat.Texture ) : Texture {
 		var tt = gl.createTexture();
-		var bind = t.flags.has(Cube) ? GL.TEXTURE_CUBE_MAP : GL.TEXTURE_2D;
+		var bind = getBindType(t);
 		var tt : Texture = { t : tt, width : t.width, height : t.height, internalFmt : GL.RGBA, pixelFmt : GL.UNSIGNED_BYTE, bits : -1, bind : bind #if multidriver, driver : this #end };
 		switch( t.format ) {
 		case RGBA:
@@ -721,6 +749,10 @@ class GlDriver extends Driver {
 					break;
 				}
 			}
+		} else if( t.flags.has(IsArray) ) {
+			gl.texImage3D(GL2.TEXTURE_2D_ARRAY, 0, tt.internalFmt, tt.width, tt.height, t.layerCount, 0, getChannels(tt), tt.pixelFmt, null);
+			if( gl.getError() == GL.OUT_OF_MEMORY )
+				outOfMem = true;
 		} else {
 			gl.texImage2D(bind, 0, tt.internalFmt, tt.width, tt.height, 0, getChannels(tt), tt.pixelFmt, null);
 			if( gl.getError() == GL.OUT_OF_MEMORY )
@@ -843,7 +875,7 @@ class GlDriver extends Driver {
 	}
 
 	override function generateMipMaps( t : h3d.mat.Texture ) {
-		var bind = t.flags.has(Cube) ? GL.TEXTURE_CUBE_MAP : GL.TEXTURE_2D;
+		var bind = getBindType(t);
 		gl.bindTexture(bind, t.t.t);
 		gl.generateMipmap(bind);
 		restoreBind();
@@ -940,7 +972,8 @@ class GlDriver extends Driver {
 
 	override function uploadTexturePixels( t : h3d.mat.Texture, pixels : hxd.Pixels, mipLevel : Int, side : Int ) {
 		var cubic = t.flags.has(Cube);
-		var bind = cubic ? GL.TEXTURE_CUBE_MAP : GL.TEXTURE_2D;
+		var bind = getBindType(t);
+		if( t.flags.has(IsArray) ) throw "TODO:texImage3D";
 		var face = cubic ? CUBE_FACES[side] : GL.TEXTURE_2D;
 		gl.bindTexture(bind, t.t.t);
 		pixels.convert(t.format);
@@ -1129,16 +1162,16 @@ class GlDriver extends Driver {
 		}
 	}
 
-	override function capturePixels(tex:h3d.mat.Texture, face:Int, mipLevel:Int) {
+	override function capturePixels(tex:h3d.mat.Texture, layer:Int, mipLevel:Int) {
 		var old = curTarget;
 		var oldCount = numTargets;
-		var oldFace = curTargetFace;
+		var oldLayer = curTargetLayer;
 		var oldMip = curTargetMip;
 		numTargets = 1;
-		setRenderTarget(tex, face, mipLevel);
+		setRenderTarget(tex, layer, mipLevel);
 		var pixels = hxd.Pixels.alloc(tex.width >> mipLevel, tex.height >> mipLevel, tex.format);
 		captureRenderBuffer(pixels);
-		setRenderTarget(old, oldFace, oldMip);
+		setRenderTarget(old, oldLayer, oldMip);
 		if( oldCount > 1 ) {
 			setDrawBuffers(oldCount);
 			numTargets = oldCount;
@@ -1146,7 +1179,7 @@ class GlDriver extends Driver {
 		return pixels;
 	}
 
-	override function setRenderTarget( tex : h3d.mat.Texture, face = 0, mipLevel = 0 ) {
+	override function setRenderTarget( tex : h3d.mat.Texture, layer = 0, mipLevel = 0 ) {
 		unbindTargets();
 		curTarget = tex;
 		if( tex == null ) {
@@ -1164,7 +1197,7 @@ class GlDriver extends Driver {
 			tex.alloc();
 
 		if( tex.flags.has(MipMapped) && !tex.flags.has(WasCleared) ) {
-			var bind = tex.flags.has(Cube) ? GL.TEXTURE_CUBE_MAP : GL.TEXTURE_2D;
+			var bind = getBindType(tex);
 			gl.bindTexture(bind, tex.t.t);
 			gl.generateMipmap(bind);
 			restoreBind();
@@ -1172,14 +1205,17 @@ class GlDriver extends Driver {
 
 		tex.flags.set(WasCleared); // once we draw to, do not clear again
 		tex.lastFrame = frame;
-		curTargetFace = face;
+		curTargetLayer = layer;
 		curTargetMip = mipLevel;
 		#if multidriver
 		if( tex.t.driver != this )
 			throw "Invalid texture context";
 		#end
 		gl.bindFramebuffer(GL.FRAMEBUFFER, commonFB);
-		gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, tex.flags.has(Cube) ? CUBE_FACES[face] : GL.TEXTURE_2D, tex.t.t, mipLevel);
+		if( tex.flags.has(IsArray) )
+			gl.framebufferTextureLayer(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, tex.t.t, mipLevel, layer);
+		else
+			gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, tex.flags.has(Cube) ? CUBE_FACES[layer] : GL.TEXTURE_2D, tex.t.t, mipLevel);
 		if( tex.depthBuffer != null ) {
 			gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.RENDERBUFFER, @:privateAccess tex.depthBuffer.b.r);
 			gl.framebufferRenderbuffer(GL.FRAMEBUFFER, GL.STENCIL_ATTACHMENT, GL.RENDERBUFFER, tex.depthBuffer.hasStencil() ? @:privateAccess tex.depthBuffer.b.r : null);

+ 2 - 2
h3d/impl/Stage3dDriver.hx

@@ -611,7 +611,7 @@ class Stage3dDriver extends Driver {
 			shaderChanged = true;
 			curShader = p;
 			// unbind extra textures
-			var tcount : Int = shader.fragment.textures2DCount + shader.fragment.texturesCubeCount + shader.vertex.textures2DCount + shader.vertex.texturesCubeCount;
+			var tcount : Int = shader.fragment.texturesCount + shader.vertex.texturesCount;
 			while( curTextures.length > tcount ) {
 				curTextures.pop();
 				ctx.setTextureAt(curTextures.length, null);
@@ -629,7 +629,7 @@ class Stage3dDriver extends Driver {
 	override function uploadShaderBuffers( buffers : h3d.shader.Buffers, which : h3d.shader.Buffers.BufferKind ) {
 		switch( which ) {
 		case Textures:
-			for( i in 0...curShader.s.fragment.textures2DCount + curShader.s.fragment.texturesCubeCount ) {
+			for( i in 0...curShader.s.fragment.texturesCount ) {
 				var t = buffers.fragment.tex[i];
 				if( t == null || t.isDisposed() ) {
 					var color = h3d.mat.Defaults.loadingTextureColor;

+ 4 - 0
h3d/mat/Data.hx

@@ -116,6 +116,10 @@ enum TextureFlags {
 		Allow texture data serialization when found in a scene (for user generated textures)
 	**/
 	Serialize;
+	/**
+		Tells if it's a texture array
+	**/
+	IsArray;
 }
 
 typedef TextureFormat = hxd.PixelFormat;

+ 56 - 32
h3d/mat/Texture.hx

@@ -44,6 +44,7 @@ class Texture {
 	public var mipMap(default,set) : MipMap;
 	public var filter(default,set) : Filter;
 	public var wrap(default, set) : Wrap;
+	public var layerCount(get, never) : Int;
 
 	/**
 		If this callback is set, the texture can be re-allocated when the 3D context has been lost or when
@@ -92,6 +93,10 @@ class Texture {
 		if( !this.flags.has(NoAlloc) ) alloc();
 	}
 
+	function get_layerCount() {
+		return flags.has(Cube) ? 6 : 1;
+	}
+
 	public function alloc() {
 		if( t == null )
 			mem.allocTexture(this);
@@ -184,32 +189,51 @@ class Texture {
 			alloc();
 	}
 
-	public function clear( color : Int, alpha = 1. ) {
+	public function clear( color : Int, alpha = 1., ?layer = -1 ) {
 		alloc();
-		var p = hxd.Pixels.alloc(width, height, nativeFormat);
-		var k = 0;
-		var b = color & 0xFF, g = (color >> 8) & 0xFF, r = (color >> 16) & 0xFF, a = Std.int(alpha * 255);
-		if( a < 0 ) a = 0 else if( a > 255 ) a = 255;
-		switch( nativeFormat ) {
-		case RGBA:
-		case BGRA:
-			// flip b/r
-			var tmp = r;
-			r = b;
-			b = tmp;
-		default:
-			throw "TODO";
-		}
-		for( i in 0...width * height ) {
-			p.bytes.set(k++,r);
-			p.bytes.set(k++,g);
-			p.bytes.set(k++,b);
-			p.bytes.set(k++,a);
+		if( flags.has(Target) #if (usegl || hlsdl || js) || true #end ) {
+			var engine = h3d.Engine.getCurrent();
+			color |= Std.int(hxd.Math.clamp(alpha)*255) << 24;
+			if( layer < 0 ) {
+				for( i in 0...layerCount ) {
+					engine.pushTarget(this, i);
+					engine.clear(color);
+					engine.popTarget();
+				}
+			} else {
+				engine.pushTarget(this, layer);
+				engine.clear(color);
+				engine.popTarget();
+			}
+		} else {
+			var p = hxd.Pixels.alloc(width, height, nativeFormat);
+			var k = 0;
+			var b = color & 0xFF, g = (color >> 8) & 0xFF, r = (color >> 16) & 0xFF, a = Std.int(alpha * 255);
+			if( a < 0 ) a = 0 else if( a > 255 ) a = 255;
+			switch( nativeFormat ) {
+			case RGBA:
+			case BGRA:
+				// flip b/r
+				var tmp = r;
+				r = b;
+				b = tmp;
+			default:
+				throw "TODO";
+			}
+			for( i in 0...width * height ) {
+				p.bytes.set(k++,r);
+				p.bytes.set(k++,g);
+				p.bytes.set(k++,b);
+				p.bytes.set(k++,a);
+			}
+			if( nativeFlip ) p.flags.set(FlipY);
+			if( layer < 0 ) {
+				for( i in 0...layerCount )
+					uploadPixels(p, 0, i);
+			} else
+				uploadPixels(p, 0, layer);
+			p.dispose();
 		}
-		if( nativeFlip ) p.flags.set(FlipY);
-		for( i in 0...(flags.has(Cube) ? 6 : 1) )
-			uploadPixels(p, 0, i);
-		p.dispose();
 	}
 
 	inline function checkSize(width, height, mip) {
@@ -217,25 +241,25 @@ class Texture {
 			throw "Invalid upload size : " + width + "x" + height + " should be " + (this.width >> mip) + "x" + (this.height >> mip);
 	}
 
-	function checkMipMapGen(mipLevel,side) {
-		if( mipLevel == 0 && flags.has(MipMapped) && !flags.has(ManualMipMapGen) && (!flags.has(Cube) || side == 5) )
+	function checkMipMapGen(mipLevel,layer) {
+		if( mipLevel == 0 && flags.has(MipMapped) && !flags.has(ManualMipMapGen) && (!flags.has(Cube) || layer == 5) )
 			mem.driver.generateMipMaps(this);
 	}
 
-	public function uploadBitmap( bmp : hxd.BitmapData, mipLevel = 0, side = 0 ) {
+	public function uploadBitmap( bmp : hxd.BitmapData, mipLevel = 0, layer = 0 ) {
 		alloc();
 		checkSize(bmp.width, bmp.height, mipLevel);
-		mem.driver.uploadTextureBitmap(this, bmp, mipLevel, side);
+		mem.driver.uploadTextureBitmap(this, bmp, mipLevel, layer);
 		flags.set(WasCleared);
-		checkMipMapGen(mipLevel, side);
+		checkMipMapGen(mipLevel, layer);
 	}
 
-	public function uploadPixels( pixels : hxd.Pixels, mipLevel = 0, side = 0 ) {
+	public function uploadPixels( pixels : hxd.Pixels, mipLevel = 0, layer = 0 ) {
 		alloc();
 		checkSize(pixels.width, pixels.height, mipLevel);
-		mem.driver.uploadTexturePixels(this, pixels, mipLevel, side);
+		mem.driver.uploadTexturePixels(this, pixels, mipLevel, layer);
 		flags.set(WasCleared);
-		checkMipMapGen(mipLevel, side);
+		checkMipMapGen(mipLevel, layer);
 	}
 
 	public function dispose() {

+ 32 - 0
h3d/mat/TextureArray.hx

@@ -0,0 +1,32 @@
+package h3d.mat;
+import h3d.mat.Data;
+
+class TextureArray extends Texture {
+
+	var layers : Int;
+
+	public function new(w, h, layers, ?flags : Array<TextureFlags>, ?format : TextureFormat, ?allocPos : h3d.impl.AllocPos ) {
+		this.layers = layers;
+		if( flags == null ) flags = [];
+		flags.push(IsArray);
+		super(w,h,flags,format,allocPos);
+	}
+
+	override function get_layerCount() {
+		return layers;
+	}
+
+	override function clone( ?allocPos : h3d.impl.AllocPos ) {
+		var old = lastFrame;
+		preventAutoDispose();
+		var t = new TextureArray(width, height, layers, null, format, allocPos);
+		h3d.pass.Copy.run(this, t);
+		lastFrame = old;
+		return t;
+	}
+
+	override function toString() {
+		return super.toString()+"["+layers+"]";
+	}
+
+}

+ 1 - 1
h3d/pass/Default.hx

@@ -81,7 +81,7 @@ class Default extends Base {
 			}
 			p.shader = manager.compileShaders(shaders);
 			p.shaders = shaders;
-			var t = p.shader.fragment.textures2D;
+			var t = p.shader.fragment.textures;
 			if( t == null )
 				p.texture = 0;
 			else {

+ 2 - 11
h3d/pass/ShaderManager.hx

@@ -217,18 +217,9 @@ class ShaderManager {
 				p = p.next;
 			}
 			var tid = 0;
-			var p = s.textures2D;
+			var p = s.textures;
 			while( p != null ) {
-				var t = getParamValue(p, shaders, !STRICT);
-				if( t == null ) t = h3d.mat.Texture.fromColor(0xFF00FF);
-				buf.tex[tid++] = t;
-				p = p.next;
-			}
-			var p = s.texturesCube;
-			while( p != null ) {
-				var t = getParamValue(p, shaders, !STRICT);
-				if( t == null ) t = h3d.mat.Texture.defaultCubeTexture();
-				buf.tex[tid++] = t;
+				buf.tex[tid++] = getParamValue(p, shaders, !STRICT);
 				p = p.next;
 			}
 			var p = s.buffers;

+ 2 - 2
h3d/shader/Buffers.hx

@@ -19,14 +19,14 @@ class ShaderBuffers {
 	public function new( s : hxsl.RuntimeShader.RuntimeShaderData ) {
 		globals = new ShaderBufferData(s.globalsSize<<2);
 		params = new ShaderBufferData(s.paramsSize<<2);
-		tex = new haxe.ds.Vector(s.textures2DCount + s.texturesCubeCount);
+		tex = new haxe.ds.Vector(s.texturesCount);
 		buffers = s.bufferCount > 0 ? new haxe.ds.Vector(s.bufferCount) : null;
 	}
 
 	public function grow( s : hxsl.RuntimeShader.RuntimeShaderData ) {
 		var ng = s.globalsSize << 2;
 		var np = s.paramsSize << 2;
-		var nt = s.textures2DCount + s.texturesCubeCount;
+		var nt = s.texturesCount;
 		var nb = s.bufferCount;
 		if( globals.length < ng ) globals = new ShaderBufferData(ng);
 		if( params.length < np ) params = new ShaderBufferData(np);

+ 4 - 4
hxsl/AgalOut.hx

@@ -626,13 +626,13 @@ class AgalOut {
 				}
 			}
 			return r;
-		case [Texture2D | TextureCube, [t,uv]]:
-			var t = expr(t);
+		case [Texture, [et,uv]]:
+			var t = expr(et);
 			var uv = expr(uv);
 			var r = allocReg();
 			if( t.t != RTexture ) throw "assert";
 			var flags = [TIgnoreSampler];
-			if( g == TextureCube )
+			if( et.t == TSamplerCube )
 				flags.push(TCube);
 			op(OTex(r, uv, { index : t.index, flags : flags }));
 			return r;
@@ -802,7 +802,7 @@ class AgalOut {
 		case TMat4: 4;
 		case TArray(t, SConst(size)), TBuffer(t, SConst(size)): (Tools.size(t) * size + 3) >> 2;
 		case TStruct(vl): throw "TODO";
-		case TVoid, TString, TSampler2D, TSamplerCube, TFun(_), TArray(_), TBuffer(_), TChannel(_): throw "assert "+t;
+		case TVoid, TString, TSampler2D, TSampler2DArray, TSamplerCube, TFun(_), TArray(_), TBuffer(_), TChannel(_): throw "assert "+t;
 		}
 	}
 

+ 5 - 6
hxsl/Ast.hx

@@ -12,6 +12,7 @@ enum Type {
 	TMat3x4;
 	TBytes( size : Int );
 	TSampler2D;
+	TSampler2DArray;
 	TSamplerCube;
 	TStruct( vl : Array<TVar> );
 	TFun( variants : Array<FunType> );
@@ -198,8 +199,8 @@ enum TGlobal {
 	//MatrixCompMult;
 	//Any;
 	//All;
-	Texture2D;
-	TextureCube;
+	Texture;
+	TextureLod;
 	// ...other texture* operations
 	// constructors
 	ToInt;
@@ -228,8 +229,6 @@ enum TGlobal {
 	DFdx;
 	DFdy;
 	Fwidth;
-	TextureCubeLod;
-	Texture2DLod;
 	ChannelRead;
 	// debug
 	Trace;
@@ -356,7 +355,7 @@ class Tools {
 
 	public static function isSampler( t : Type ) {
 		return switch( t ) {
-		case TSampler2D, TSamplerCube:
+		case TSampler2D, TSamplerCube, TSampler2DArray, TChannel(_):
 			true;
 		default:
 			false;
@@ -492,7 +491,7 @@ class Tools {
 		case TMat4: 16;
 		case TMat3x4: 12;
 		case TBytes(s): s;
-		case TBool, TString, TSampler2D, TSamplerCube, TFun(_): 0;
+		case TBool, TString, TSampler2D, TSampler2DArray, TSamplerCube, TFun(_): 0;
 		case TArray(t, SConst(v)), TBuffer(t, SConst(v)): size(t) * v;
 		case TArray(_, SVar(_)), TBuffer(_): 0;
 		}

+ 15 - 10
hxsl/Cache.hx

@@ -338,7 +338,9 @@ class Cache {
 		var flat = new Flatten();
 		var c = new RuntimeShaderData();
 		var data = flat.flatten(s, kind, constsToGlobal);
+		var textures = [];
 		c.consts = flat.consts;
+		c.texturesCount = 0;
 		for( g in flat.allocData.keys() ) {
 			var alloc = flat.allocData.get(g);
 			switch( g.kind ) {
@@ -358,12 +360,9 @@ class Cache {
 				for( i in 0...out.length - 1 )
 					out[i].next = out[i + 1];
 				switch( g.type ) {
-				case TArray(TSampler2D, _):
-					c.textures2D = out[0];
-					c.textures2DCount = out.length;
-				case TArray(TSamplerCube, _):
-					c.texturesCube = out[0];
-					c.texturesCubeCount = out.length;
+				case TArray(t, _) if( t.isSampler() ):
+					textures.push({ t : t, all : out });
+					c.texturesCount += out.length;
 				case TArray(TVec(4, VFloat), SConst(size)):
 					c.params = out[0];
 					c.paramsSize = size;
@@ -386,14 +385,20 @@ class Cache {
 			default: throw "assert";
 			}
 		}
+		if( textures.length > 0 ) {
+			// relink in order based on type
+			textures.sort(function(t1,t2) return t1.t.getIndex() - t2.t.getIndex());
+			c.textures = textures[0].all[0];
+			for( i in 1...textures.length ) {
+				var prevAll = textures[i-1].all;
+				var prev = prevAll[prevAll.length - 1];
+				prev.next = textures[i].all[0];
+			}
+		}
 		if( c.globals == null )
 			c.globalsSize = 0;
 		if( c.params == null )
 			c.paramsSize = 0;
-		if( c.textures2D == null )
-			c.textures2DCount = 0;
-		if( c.texturesCube == null )
-			c.texturesCubeCount = 0;
 		if( c.buffers == null )
 			c.bufferCount = 0;
 		c.data = data;

+ 12 - 10
hxsl/Checker.hx

@@ -54,14 +54,17 @@ class Checker {
 				genFloat;
 			case Cross:
 				[ { args : [ { name : "a", type : vec3 }, { name : "b", type : vec3 } ], ret : vec3 } ];
-			case Texture2D:
-				[ { args : [ { name : "tex", type : TSampler2D }, { name : "uv", type : vec2 } ], ret : vec4 } ];
-			case Texture2DLod:
-				[ { args : [ { name : "tex", type : TSampler2D }, { name : "uv", type : vec2 }, { name : "lod", type : TFloat } ], ret : vec4 } ];
-			case TextureCube:
-				[ { args : [ { name : "tex", type : TSamplerCube }, { name : "normal", type : vec3 } ], ret : vec4 } ];
-			case TextureCubeLod:
-				[ { args : [ { name : "tex", type : TSamplerCube }, { name : "normal", type : vec3 }, { name : "lod", type : TFloat } ], ret : vec4 } ];
+			case Texture:
+				[
+					{ args : [ { name : "tex", type : TSampler2D }, { name : "uv", type : vec2 } ], ret : vec4 },
+					{ args : [ { name : "tex", type : TSamplerCube }, { name : "normal", type : vec3 } ], ret : vec4 },
+					{ args : [ { name : "tex", type : TSampler2DArray }, { name : "uv", type : vec3 } ], ret : vec4 },
+				];
+			case TextureLod:
+				[
+					{ args : [ { name : "tex", type : TSampler2D }, { name : "uv", type : vec2 }, { name : "lod", type : TFloat } ], ret : vec4 },
+					{ args : [ { name : "tex", type : TSamplerCube }, { name : "normal", type : vec3 }, { name : "lod", type : TFloat } ], ret : vec4 },
+				];
 			case ToInt:
 				[for( t in baseType ) { args : [ { name : "value", type : t } ], ret : TInt } ];
 			case ToFloat:
@@ -808,8 +811,7 @@ class Checker {
 		var g = globals.get(f);
 		if( g == null ) {
 			var gl : TGlobal = switch( [f, e.t] ) {
-			case ["get", TSampler2D]: Texture2D;
-			case ["get", TSamplerCube]: TextureCube;
+			case ["get", TSampler2D|TSampler2DArray|TSamplerCube]: Texture;
 			case ["get", TChannel(_)]: ChannelRead;
 			default: null;
 			}

+ 1 - 1
hxsl/Dce.hx

@@ -218,7 +218,7 @@ class Dce {
 			return { e : TConst(CNull), t : e.t, p : e.p };
 		case TCall({ e : TGlobal(ChannelRead) }, [_, uv, { e : TConst(CInt(cid)) }]):
 			var c = channelVars[cid];
-			return { e : TCall({ e : TGlobal(Texture2D), p : e.p, t : TVoid }, [{ e : TVar(c), t : c.type, p : e.p }, uv]), t : TVoid, p : e.p };
+			return { e : TCall({ e : TGlobal(Texture), p : e.p, t : TVoid }, [{ e : TVar(c), t : c.type, p : e.p }, uv]), t : TVoid, p : e.p };
 		case TIf(e, econd, eelse):
 			var e = mapExpr(e, true);
 			var econd = mapExpr(econd, isVar);

+ 9 - 7
hxsl/Flatten.hx

@@ -79,7 +79,9 @@ class Flatten {
 		pack(prefix + "Globals", Global, globals, VFloat);
 		pack(prefix + "Params", Param, params, VFloat);
 		var allVars = globals.concat(params);
-		var textures = packTextures(prefix + "Textures", allVars, TSampler2D).concat(packTextures(prefix+"TexturesCube", allVars, TSamplerCube));
+		var textures = packTextures(prefix + "Textures", allVars, TSampler2D)
+			.concat(packTextures(prefix+"TexturesCube", allVars, TSamplerCube))
+			.concat(packTextures(prefix+"TexturesArray", allVars, TSampler2DArray));
 		packBuffers(allVars);
 		var funs = [for( f in s.funs ) mapFun(f, mapExpr)];
 		for( t in textures )
@@ -277,9 +279,12 @@ class Flatten {
 			var stride = Std.int(a.size / len);
 			var earr = [for( i in 0...len ) { var a = new Alloc(a.g, a.t, a.pos + stride * i, stride); access(a, t, pos, AIndex(a)); }];
 			return { e : TArrayDecl(earr), t : t, p : pos };
-		case TSampler2D, TSamplerCube, TChannel(_):
-			return read(0,pos);
 		default:
+			if( t.isSampler() ) {
+				var e = read(0, pos);
+				e.t = t;
+				return e;
+			}
 			var size = varSize(t, a.t);
 			if( size > 4 )
 				return Error.t("Access not supported for " + t.toString(), null);
@@ -388,11 +393,8 @@ class Flatten {
 			kind : kind,
 		};
 		for( v in vars ) {
-			switch( v.type ) {
-			case TSampler2D, TSamplerCube, TChannel(_), TBuffer(_):
+			if( v.type.isSampler() || v.type.match(TBuffer(_)) )
 				continue;
-			default:
-			}
 			var size = varSize(v.type, t);
 			var best : Alloc = null;
 			for( a in alloc )

+ 77 - 57
hxsl/GlslOut.hx

@@ -23,8 +23,6 @@ class GlslOut {
 		m.set(ToInt, "int");
 		m.set(ToFloat, "float");
 		m.set(ToBool, "bool");
-		m.set(Texture2D, "_texture2D");
-		m.set(Texture2DLod, "_texture2DLod");
 		m.set(LReflect, "reflect");
 		m.set(Mat3x4, "_mat3x4");
 		for( g in m )
@@ -108,6 +106,10 @@ class GlslOut {
 			add("_mat3x4");
 		case TSampler2D:
 			add("sampler2D");
+		case TSampler2DArray:
+			add("sampler2DArray");
+			if( isES )
+				decl("precision lowp sampler2DArray;");
 		case TSamplerCube:
 			add("samplerCube");
 		case TStruct(vl):
@@ -207,6 +209,72 @@ class GlslOut {
 		addExpr(e, tabs);
 	}
 
+	function getFunName( g : TGlobal, args : Array<TExpr>, rt : Type ) {
+		switch( g ) {
+		case Mat3x4:
+			decl(MAT34);
+		case DFdx, DFdy, Fwidth:
+			decl("#extension GL_OES_standard_derivatives:enable");
+		case Pack:
+			decl("vec4 pack( float v ) { vec4 color = fract(v * vec4(1, 255, 255.*255., 255.*255.*255.)); return color - color.yzww * vec4(1. / 255., 1. / 255., 1. / 255., 0.); }");
+		case Unpack:
+			decl("float unpack( vec4 color ) { return dot(color,vec4(1., 1. / 255., 1. / (255. * 255.), 1. / (255. * 255. * 255.))); }");
+		case PackNormal:
+			decl("vec4 packNormal( vec3 v ) { return vec4((v + vec3(1.)) * vec3(0.5),1.); }");
+		case UnpackNormal:
+			decl("vec3 unpackNormal( vec4 v ) { return normalize((v.xyz - vec3(0.5)) * vec3(2.)); }");
+		case Texture:
+			switch( args[0].t ) {
+			case TSampler2D, TSampler2DArray if( !flipY ):
+				if( isES2 )
+					return "texture2D";
+			case TSampler2D:
+				// convert S/T (bottom left) to U/V (top left)
+				// we don't use 1. because of pixel rounding (fixes artifacts in blur)
+				decl('vec4 _texture2D( sampler2D t, vec2 v ) { return ${isES2?"texture2D":"texture"}(t,vec2(v.x,0.999999-v.y)); }');
+				return "_texture2D";
+			case TSampler2DArray:
+				decl('vec4 _texture2DArr( sampler2DArray t, vec3 v ) { return texture(t,vec3(v.x,0.999999-v.y,v.z)); }');
+				return "_texture2DArr";
+			case TSamplerCube:
+				if( isES2 )
+					return "textureCube";
+			case var t:
+				throw "Unsupported "+t;
+			}
+		case TextureLod:
+			switch( args[0].t ) {
+			case TSampler2D, TSampler2DArray if( !flipY ):
+				if( isES2 ) {
+					decl("#extension GL_EXT_shader_texture_lod : enable");
+					return "texture2DLodEXT";
+				}
+			case TSampler2D:
+				var tlod = isES2 ? "texture2DLod" : "textureLod";
+				decl('vec4 _texture2DLod( sampler2D t, vec2 v, float lod ) { return $tlod(t,vec2(v.x,0.999999-v.y)); }');
+				return "_texture2DLod";
+			case TSampler2DArray:
+				decl('vec4 _texture2DArrLod( sampler2DArray t, vec3 v, float lod ) { return textureLod(t,vec3(v.x,0.999999-v.y,v.z)); }');
+				return "_texture2DArrLod";
+			case TSamplerCube:
+				if( isES2 ) {
+					decl("#extension GL_EXT_shader_texture_lod : enable");
+					return "textureCubeLodEXT";
+				}
+			default:
+			}
+		case Mod if( rt == TInt && isES ):
+			decl("int _imod( int x, int y ) { return int(mod(float(x),float(y))); }");
+			return "_imod";
+		case Mat3 if( args[0].t == TMat3x4 ):
+			decl(MAT34);
+			decl("mat3 _mat3( _mat3x4 v ) { return mat3(v.a.xyz,v.b.xyz,v.c.xyz); }");
+			return "_mat3";
+		default:
+		}
+		return GLOBALS.get(g);
+	}
+
 	function addExpr( e : TExpr, tabs : String ) {
 		switch( e.e ) {
 		case TConst(c):
@@ -224,46 +292,6 @@ class GlslOut {
 		case TVar(v):
 			ident(v);
 		case TGlobal(g):
-			switch( g ) {
-			case Mat3x4:
-				decl(MAT34);
-			case DFdx, DFdy, Fwidth:
-				decl("#extension GL_OES_standard_derivatives:enable");
-			case Pack:
-				decl("vec4 pack( float v ) { vec4 color = fract(v * vec4(1, 255, 255.*255., 255.*255.*255.)); return color - color.yzww * vec4(1. / 255., 1. / 255., 1. / 255., 0.); }");
-			case Unpack:
-				decl("float unpack( vec4 color ) { return dot(color,vec4(1., 1. / 255., 1. / (255. * 255.), 1. / (255. * 255. * 255.))); }");
-			case PackNormal:
-				decl("vec4 packNormal( vec3 v ) { return vec4((v + vec3(1.)) * vec3(0.5),1.); }");
-			case UnpackNormal:
-				decl("vec3 unpackNormal( vec4 v ) { return normalize((v.xyz - vec3(0.5)) * vec3(2.)); }");
-			case Texture2D:
-				// convert S/T (bottom left) to U/V (top left)
-				// we don't use 1. because of pixel rounding (fixes artifacts in blur)
-				decl('vec4 _texture2D( sampler2D t, vec2 v ) { return ${isES2?"texture2D":"texture"}(t,${flipY?'vec2(v.x,0.999999-v.y)':'v'}); }');
-			case TextureCube:
-				if( !isES2 ) {
-					add("texture");
-					return;
-				}
-			case Texture2DLod:
-				var tlod = "texture2DLod";
-				if( isES2 ) {
-					decl("#extension GL_EXT_shader_texture_lod : enable");
-					tlod = "texture2DLodEXT";
-				}
-				decl('vec4 _texture2DLod( sampler2D t, vec2 v, float lod ) { return $tlod(t,${flipY?'vec2(v.x,0.999999-v.y)':'v'}); }');
-			case TextureCubeLod:
-				if( !isES2 ) {
-					add("textureLod");
-					return;
-				}
-				if( version <= 100 ) {
-					decl("#extension GL_EXT_shader_texture_lod : enable");
-					decl("vec4 textureCubeLod( samplerCube t, vec3 coord, float lod ) { return textureCubeLodEXT(t,coord,lod); }");
-				}
-			default:
-			}
 			add(GLOBALS.get(g));
 		case TParenthesis(e):
 			add("(");
@@ -347,25 +375,17 @@ class GlslOut {
 			} else {
 				add("/*var*/");
 			}
-		case TCall( { e : TGlobal(Mod) }, [v1,v2]) if( e.t == TInt ):
-			decl("int mod( int x, int y ) { return int(mod(float(x),float(y))); }");
-			add("mod(");
-			addValue(v1, tabs);
-			add(",");
-			addValue(v2, tabs);
-			add(")");
-		case TCall( { e : TGlobal(Mat3) }, [e]) if( e.t == TMat3x4 ):
-			decl(MAT34);
-			decl("mat3 _mat3( _mat3x4 v ) { return mat3(v.a.xyz,v.b.xyz,v.c.xyz); }");
-			add("_mat3(");
-			addValue(e, tabs);
-			add(")");
 		case TCall( { e : TGlobal(Saturate) }, [e]):
 			add("clamp(");
 			addValue(e, tabs);
 			add(", 0., 1.)");
-		case TCall(e, args):
-			addValue(e, tabs);
+		case TCall(v, args):
+			switch( v.e ) {
+			case TGlobal(g):
+				add(getFunName(g,args, e.t));
+			default:
+				addValue(v, tabs);
+			}
 			add("(");
 			var first = true;
 			for( e in args ) {

+ 6 - 4
hxsl/HlslOut.hx

@@ -93,7 +93,7 @@ class HlslOut {
 			add("float4x4");
 		case TMat3x4:
 			add("float4x3");
-		case TSampler2D, TSamplerCube:
+		case TSampler2D, TSamplerCube, TSampler2DArray:
 			add("SamplerState");
 		case TStruct(vl):
 			add("struct { ");
@@ -220,12 +220,12 @@ class HlslOut {
 			var acc = varAccess.get(v.id);
 			if( acc != null ) add(acc);
 			ident(v);
-		case TCall({ e : TGlobal(g = (Texture2D | TextureCube | Texture2DLod | TextureCubeLod)) }, args):
+		case TCall({ e : TGlobal(g = (Texture | TextureLod)) }, args):
 			addValue(args[0], tabs);
 			switch( g ) {
-			case Texture2D, TextureCube:
+			case Texture:
 				add(".Sample(");
-			case Texture2DLod, TextureCubeLod:
+			case TextureLod:
 				add(".SampleLevel(");
 			default:
 				throw "assert";
@@ -584,6 +584,8 @@ class HlslOut {
 					add("Texture2D ");
 				case TSamplerCube:
 					add("TextureCube ");
+				case TSampler2DArray:
+					add("Texture2DArray ");
 				default:
 					throw "Unsupported sampler " + t;
 				}

+ 1 - 0
hxsl/MacroParser.hx

@@ -79,6 +79,7 @@ class MacroParser {
 			case "Mat3x4": return TMat3x4;
 			case "String": return TString;
 			case "Sampler2D": return TSampler2D;
+			case "Sampler2DArray": return TSampler2DArray;
 			case "SamplerCube": return TSamplerCube;
 			case "Bytes2": return TBytes(2);
 			case "Bytes3": return TBytes(3);

+ 2 - 0
hxsl/Macros.hx

@@ -28,6 +28,8 @@ class Macros {
 			TAnonymous(fields);
 		case TSampler2D:
 			macro : hxsl.Types.Sampler2D;
+		case TSampler2DArray:
+			macro : hxsl.Types.Sampler2DArray;
 		case TSamplerCube:
 			macro : hxsl.Types.SamplerCube;
 		case TMat3, TMat3x4, TMat4:

+ 2 - 4
hxsl/RuntimeShader.hx

@@ -51,10 +51,8 @@ class RuntimeShaderData {
 	public var paramsSize : Int;
 	public var globals : AllocGlobal;
 	public var globalsSize : Int;
-	public var textures2D : AllocParam;
-	public var textures2DCount : Int;
-	public var texturesCube : AllocParam;
-	public var texturesCubeCount : Int;
+	public var textures : AllocParam;
+	public var texturesCount : Int;
 	public var buffers : AllocParam;
 	public var bufferCount : Int;
 	public var consts : Array<Float>;

+ 1 - 0
hxsl/Types.hx

@@ -6,6 +6,7 @@ typedef BVec = Array<Bool>;
 typedef Matrix = h3d.Matrix;
 typedef Texture = h3d.mat.Texture;
 typedef Sampler2D = h3d.mat.Texture;
+typedef Sampler2DArray = h3d.mat.TextureArray;
 typedef SamplerCube = h3d.mat.Texture;
 typedef ChannelTexture = h3d.mat.Texture;
 typedef Buffer = h3d.Buffer;

+ 80 - 0
samples/ShaderAdvanced.hx

@@ -0,0 +1,80 @@
+class TestUniformBuffer extends hxsl.Shader {
+
+	static var SRC = {
+
+		@param var colorMatrix : Buffer<Vec4,4>;
+		var pixelColor : Vec4;
+
+		function fragment() {
+			pixelColor *= mat4(colorMatrix[0],colorMatrix[1],colorMatrix[2],colorMatrix[3]);
+		}
+
+	};
+
+}
+
+class TestTextureArray extends hxsl.Shader {
+
+	static var SRC = {
+
+		@const var COUNT : Int = 3;
+		@param var time : Float;
+		@param var textures : Sampler2DArray;
+		var calculatedUV : Vec2;
+		var pixelColor : Vec4;
+
+		function fragment() {
+			pixelColor *= textures.get(vec3(calculatedUV, int((calculatedUV.x + calculatedUV.y) * COUNT + time) % COUNT));
+		}
+
+	};
+
+}
+
+class ShaderAdvanced extends hxd.App {
+
+	var updates : Array<Float -> Void> = [];
+
+	override function init() {
+		engine.backgroundColor = 0xFF202020;
+
+		// uniform buffer
+		var bmp = new h2d.Bitmap(h2d.Tile.fromColor(0xFF0000,128,128),s2d);
+		var ubuffer = bmp.addShader(new TestUniformBuffer());
+		ubuffer.colorMatrix = new h3d.Buffer(4,4,[UniformBuffer,Dynamic]);
+		var hue : Float = 0.;
+		updates.push(function(dt) {
+			hue += dt * 0.1;
+			var m = new h3d.Matrix();
+			m.identity();
+			m.colorHue(hue);
+
+			var buf = new hxd.FloatBuffer();
+			for( v in m.getFloats() )
+				buf.push(v);
+			ubuffer.colorMatrix.uploadVector(buf, 0, 4);
+		});
+
+		// texture array
+		var bmp = new h2d.Bitmap(h2d.Tile.fromColor(0xFFFFFF,128,128), s2d);
+		var tarr = bmp.addShader(new TestTextureArray());
+		bmp.x = 128;
+		updates.push(function(dt) {
+			tarr.time += dt / 60;
+		});
+		tarr.textures = new h3d.mat.TextureArray(1,1,3,[Target]);
+		tarr.textures.clear(0xFF4040,1,0);
+		tarr.textures.clear(0x40FF40,1,1);
+		tarr.textures.clear(0x4040FF,1,2);
+	}
+
+	override function update(dt:Float) {
+		for( f in updates )
+			f(dt);
+	}
+
+	static function main() {
+		new ShaderAdvanced();
+	}
+
+}

+ 0 - 45
samples/UniformBuffer.hx

@@ -1,45 +0,0 @@
-class TestShader extends hxsl.Shader {
-
-	static var SRC = {
-
-		@param var colorMatrix : Buffer<Vec4,4>;
-		var pixelColor : Vec4;
-
-		function fragment() {
-			pixelColor *= mat4(colorMatrix[0],colorMatrix[1],colorMatrix[2],colorMatrix[3]);
-			pixelColor.r += 0.1;
-			pixelColor.a = 1;
-		}
-
-	};
-
-}
-
-class UniformBuffer extends hxd.App {
-
-	var hue : Float = 0.;
-	var shader : TestShader;
-
-	override function init() {
-		var bmp = new h2d.Bitmap(h2d.Tile.fromColor(0xFF0000,128,128),s2d);
-		shader = bmp.addShader(new TestShader());
-		shader.colorMatrix = new h3d.Buffer(4,4,[UniformBuffer,Dynamic]);
-	}
-
-	override function update(dt:Float) {
-		hue += dt * 0.1;
-		var m = new h3d.Matrix();
-		m.identity();
-		m.colorHue(hue);
-
-		var buf = new hxd.FloatBuffer();
-		for( v in m.getFloats() )
-			buf.push(v);
-		shader.colorMatrix.uploadVector(buf, 0, 4);
-	}
-
-	static function main() {
-		new UniformBuffer();
-	}
-
-}