Browse Source

textures ok

Nicolas Cannasse 11 months ago
parent
commit
63e953b9c9
3 changed files with 176 additions and 29 deletions
  1. 1 1
      h3d/impl/Driver.hx
  2. 113 14
      h3d/impl/WebGpuDriver.hx
  3. 62 14
      hxsl/WgslOut.hx

+ 1 - 1
h3d/impl/Driver.hx

@@ -8,7 +8,7 @@ typedef Query = {};
 #elseif (js && webgpu)
 typedef IndexBuffer = { buf : WebGpuApi.GPU_Buffer, stride : Int };
 typedef GPUBuffer = WebGpuApi.GPU_Buffer;
-typedef Texture = {};
+typedef Texture = { tex : WebGpuApi.GPUTexture, view : WebGpuApi.GPUTextureView };
 typedef DepthBuffer = {};
 typedef Query = {};
 #elseif js

+ 113 - 14
h3d/impl/WebGpuDriver.hx

@@ -3,6 +3,7 @@ package h3d.impl;
 import h3d.impl.Driver;
 import h3d.impl.WebGpuApi;
 import h3d.mat.Pass;
+using hxsl.Ast;
 
 class WebGpuSubShader {
 	public var kind : GPUShaderStage;
@@ -11,6 +12,8 @@ class WebGpuSubShader {
 	public var globalsBufferSize : Int = 0;
 	public var paramsGroup : Int = 0;
 	public var globalsGroup : Int = 0;
+	public var texturesGroup : Int = 0;
+	public var texturesCount : Int;
 	public function new() {
 	}
 }
@@ -237,6 +240,19 @@ class WebGpuDriver extends h3d.impl.Driver {
 		return _allocBuffer(VERTEX,b.vertices,b.format.strideBytes);
 	}
 
+	override function allocTexture(t:h3d.mat.Texture):Texture {
+		var tex = device.createTexture({
+			size : { width : t.width, height : t.height },
+			mipLevelCount : t.mipLevels,
+			format : switch( t.format ) {
+			case RGBA: Rgba8unorm;
+			default: throw "Unsupported texture format "+t.format;
+			},
+			usage : TEXTURE_BINDING | COPY_DST,
+		});
+		return { tex : tex, view : tex.createView() };
+	}
+
 	override function allocIndexes(count:Int, is32:Bool):IndexBuffer {
 		var stride = is32?4:2;
 		var buf = _allocBuffer(INDEX,count,stride);
@@ -299,40 +315,95 @@ class WebGpuDriver extends h3d.impl.Driver {
 		buf.unmap();
 	}
 
-	function compile( parent : WebGpuShader, shader : hxsl.RuntimeShader.RuntimeShaderData, kind ) {
+	override function uploadTextureBitmap(t:h3d.mat.Texture, bmp:hxd.BitmapData, mipLevel:Int, side:Int) {
+		throw "Not implemented";
+	}
+
+	override function uploadTexturePixels(t:h3d.mat.Texture, pixels:hxd.Pixels, mipLevel:Int, side:Int) {
+		var bpp = hxd.Pixels.calcStride(pixels.width,pixels.format);
+		device.queue.writeTexture({ texture : t.t.tex, mipLevel : mipLevel, origin : side == 0 ? js.Lib.undefined : { z : side } }, pixels.bytes.getData(), { bytesPerRow : bpp }, { width : pixels.width, height : pixels.height });
+	}
+
+	function isSampler(t) {
+		return switch( t ) {
+		case TArray(t,_) if( t.isSampler() ): true;
+		default: t.isSampler();
+		}
+	}
+
+	function compileShaderPart( shader : hxsl.RuntimeShader.RuntimeShaderData, groups : Array<Array<GPUBindGroupLayoutEntry>>, kind ) {
 		var comp = new hxsl.WgslOut();
 		var sh = new WebGpuSubShader();
+		var textures = [];
 		sh.kind = kind;
 		for( v in shader.data.vars ) {
 			switch( v.kind ) {
 			case Param, Global:
+				switch(v.type) {
+				case TArray(t, SConst(n)) if( t.isSampler() ):
+					for( i in 0...n )
+						textures.push(t);
+					continue;
+				case t if( t.isSampler() ):
+					textures.push(t);
+					continue;
+				default:
+				}
+				var index = groups.length;
 				var size = hxsl.Ast.Tools.size(v.type) * 4;
-				var index = parent.groups.length;
-				var g = device.createBindGroupLayout({ entries : [{
-					binding: index,
+				groups.push([{
+					binding: 0,
 					visibility : kind,
 					buffer : {
 						type : Uniform,
 						hasDynamicOffset : false,
 						minBindingSize: size,
 					}
-				}]});
+				}]);
 				switch( v.kind ) {
 				case Param:
 					sh.paramsBufferSize = size;
 					sh.paramsGroup = index;
-					comp.paramsBinding = comp.paramsGroup = index;
+					comp.paramsGroup = index;
 				case Global:
 					sh.globalsBufferSize = size;
 					sh.globalsGroup = index;
-					comp.globalsBinding = comp.globalsGroup = index;
+					comp.globalsGroup = index;
 				default:
 				}
-				parent.groups.push(g);
 			default:
 			}
 		}
+
+		if( textures.length > 0 ) {
+			var index = groups.length;
+			groups.push([
+				for( t in textures ) {
+					binding : 0,
+					visibility : kind,
+					texture: {
+						sampleType : Float,
+						viewDimension : D2,
+					}
+				}
+			]);
+			groups.push([
+				for( t in textures ) {
+					binding : 0,
+					visibility : kind,
+					sampler: {
+						type : Filtering,
+					}
+				}
+			]);
+			sh.texturesGroup = comp.texturesGroup = index;
+		}
+		sh.texturesCount = textures.length;
+
 		var source = comp.run(shader.data);
+		#if debug_shaders
+		trace(source);
+		#end
 		sh.module = device.createShaderModule({ code : source });
 		return sh;
 	}
@@ -348,10 +419,14 @@ class WebGpuDriver extends h3d.impl.Driver {
 			default:
 			}
 		}
-		sh.groups = [];
+		var groupsDecls = [];
 		sh.format = hxd.BufferFormat.make(format);
-		sh.vertex = compile(sh,shader.vertex,VERTEX);
-		sh.fragment = compile(sh,shader.fragment,FRAGMENT);
+		sh.vertex = compileShaderPart(shader.vertex,groupsDecls,VERTEX);
+		sh.fragment = compileShaderPart(shader.fragment,groupsDecls,FRAGMENT);
+		#if debug_shaders
+		trace(groupsDecls);
+		#end
+		sh.groups = [for( g in groupsDecls ) device.createBindGroupLayout({ entries: g })];
 		sh.layout = device.createPipelineLayout({ bindGroupLayouts: sh.groups });
 		sh.inputCount = format.length;
 		return sh;
@@ -393,6 +468,10 @@ class WebGpuDriver extends h3d.impl.Driver {
 		_uploadShaderBuffers(buffers.fragment, which, currentShader.fragment);
 	}
 
+	function createSampler( t : h3d.mat.Texture ) {
+		return device.createSampler();
+	}
+
 	function _uploadShaderBuffers(buffers:h3d.shader.Buffers.ShaderBuffers, which:h3d.shader.Buffers.BufferKind, sh:WebGpuSubShader) {
 		switch( which ) {
 		case Globals, Params:
@@ -411,7 +490,7 @@ class WebGpuDriver extends h3d.impl.Driver {
 			var group = device.createBindGroup({
 				layout : currentShader.groups[index],
 				entries: [{
-					binding: index,
+					binding: 0,
 					resource: {
 						buffer : buffer,
 					}
@@ -419,9 +498,29 @@ class WebGpuDriver extends h3d.impl.Driver {
 			});
 			renderPass.setBindGroup(index, group);
 		case Textures:
-			if( buffers.tex.length == 0 )
+			if( sh.texturesCount == 0 )
 				return;
-			throw "TODO";
+			var index = sh.texturesGroup;
+			var group = device.createBindGroup({
+				layout : currentShader.groups[index],
+				entries: [
+					for( i in 0...buffers.tex.length ) {
+						binding : i,
+						resource : buffers.tex[i].t.view,
+					}
+				]
+			});
+			var samplers = device.createBindGroup({
+				layout : currentShader.groups[index+1],
+				entries: [
+					for( i in 0...buffers.tex.length ) {
+						binding : i,
+						resource : createSampler(buffers.tex[i]),
+					}
+				]
+			});
+			renderPass.setBindGroup(index, group);
+			renderPass.setBindGroup(index+1, samplers);
 		case Buffers:
 			if( buffers.buffers == null || buffers.buffers.length == 0 )
 				return;

+ 62 - 14
hxsl/WgslOut.hx

@@ -26,10 +26,9 @@ class WgslOut {
 	var allNames : Map<String, Int>;
 	var hasVarying : Bool;
 	public var varNames : Map<Int,String>;
-	public var paramsGroup : Int = 0;
-	public var paramsBinding : Int = 0;
-	public var globalsGroup : Int = 0;
-	public var globalsBinding : Int = 0;
+	public var paramsGroup : Int = -1;
+	public var globalsGroup : Int = -1;
+	public var texturesGroup : Int = -1;
 
 	var varAccess : Map<Int,String>;
 
@@ -86,11 +85,11 @@ class WgslOut {
 		case TMat3x4:
 			add("mat3x4f");
 		case TSampler2D:
-			add("Texture2D");
+			add("texture_2d<f32>");
 		case TSamplerCube:
-			add("TextureCube");
+			add("texture_cube<f32>");
 		case TSampler2DArray:
-			add("Texture2DArray");
+			add("texture_2d_array<f32>");
 		case TStruct(vl):
 			add("struct { ");
 			for( v in vl ) {
@@ -275,6 +274,27 @@ class WgslOut {
 			} else {
 				add("/*var*/");
 			}
+		case TCall({ e : TGlobal(g) },args):
+			switch( [g,args] ) {
+			case [Texture,[t,uv]]:
+				add("textureSample(");
+				addExpr(t, tabs);
+				add(",");
+				addExpr(t, tabs);
+				add("__sampler");
+				add(",");
+				addExpr(uv, tabs);
+				add(")");
+			default:
+				add(GLOBALS.get(g));
+				add("(");
+				var first = true;
+				for( e in args ) {
+					if( first ) first = false else add(", ");
+					addValue(e, tabs);
+				}
+				add(")");
+			}
 		case TCall(e, args):
 			addValue(e, tabs);
 			add("(");
@@ -346,7 +366,10 @@ class WgslOut {
 		case TBreak:
 			add("break");
 		case TArray(e, index):
-			switch( e.t ) {
+			switch( [e.t, index.e] ) {
+			case [TArray(et,_),TConst(CInt(idx))] if( et.isSampler() ):
+				addValue(e, tabs);
+				add(idx);
 			default:
 				addValue(e, tabs);
 				add("[");
@@ -458,11 +481,13 @@ class WgslOut {
 		if( !found )
 			return;
 
+		var globals = 0;
 		for( v in s.vars )
 			if( v.kind == Global ) {
-				add('@group(${globalsGroup}) @binding(${globalsBinding}) var<uniform> ');
+				add('@group(${globalsGroup}) @binding($globals) var<uniform> ');
 				addVar(v);
 				add(";\n");
+				globals++;
 			}
 	}
 
@@ -478,6 +503,7 @@ class WgslOut {
 
 		var textures = [];
 		var buffers = [];
+		var params = 0;
 		for( v in s.vars )
 			if( v.kind == Param ) {
 				switch( v.type ) {
@@ -493,9 +519,10 @@ class WgslOut {
 						continue;
 					}
 				}
-				add('@group($paramsGroup) @binding($paramsBinding) var<uniform> ');
+				add('@group($paramsGroup) @binding($params) var<uniform> ');
 				addVar(v);
 				add(";\n");
+				params++;
 			}
 
 		var bufCount = 0;
@@ -509,11 +536,32 @@ class WgslOut {
 
 		var texCount = 0;
 		for( v in textures ) {
-			addVar(v);
-			add(' : register(t${texCount});\n');
 			switch( v.type ) {
-			case TArray(_,SConst(n)): texCount += n;
-			default: texCount++;
+			case TArray(t,SConst(n)):
+				// array of textures not supported in WSGL (so annoying...)
+				for( i in 0...n ) {
+					add('@group($texturesGroup) @binding($texCount) var ');
+					add(v.name+i);
+					add(" : ");
+					addType(t);
+					add(";\n");
+
+					add('@group(${texturesGroup+1}) @binding($texCount) var ');
+					add(v.name+i+"__sampler");
+					add(" : sampler");
+					add(";\n");
+					texCount++;
+				}
+			default:
+				add('@group($texturesGroup) @binding($texCount) var ');
+				addVar(v);
+				add(";\n");
+
+				add('@group(${texturesGroup+1}) @binding($texCount) var ');
+				add(v.name+"__sampler");
+				add(" : sampler");
+				add(";\n");
+				texCount++;
 			}
 		}
 	}