瀏覽代碼

simple triangle render ok

Nicolas Cannasse 1 年之前
父節點
當前提交
66eb794c29
共有 3 個文件被更改,包括 121 次插入73 次删除
  1. 32 32
      h3d/impl/WebGpuApi.hx
  2. 65 32
      h3d/impl/WebGpuDriver.hx
  3. 24 9
      hxsl/WgslOut.hx

+ 32 - 32
h3d/impl/WebGpuApi.hx

@@ -287,40 +287,40 @@ enum GPUMapMode {
 
 typedef GPUMapModeFlags = haxe.EnumFlags<GPUMapMode>;
 
-extern class GPUBuffer {
+extern class GPU_Buffer {
 	var size(default,null) : GPUSize64;
 	var usage(default,null) : GPUBufferUsageFlags;
 	var mapState(default,null) : GPUBufferMapState;
 	/**
-	 * Maps the given range of the {@link GPUBuffer} and resolves the returned {@link Promise} when the
-	 * {@link GPUBuffer}'s content is ready to be accessed with {@link GPUBuffer#getMappedRange}.
+	 * Maps the given range of the {@link GPU_Buffer} and resolves the returned {@link Promise} when the
+	 * {@link GPU_Buffer}'s content is ready to be accessed with {@link GPU_Buffer#getMappedRange}.
 	 * The resolution of the returned {@link Promise} **only** indicates that the buffer has been mapped.
 	 * It does not guarantee the completion of any other operations visible to the content timeline,
 	 * and in particular does not imply that any other {@link Promise} returned from
-	 * {@link GPUQueue#onSubmittedWorkDone()} or {@link GPUBuffer#mapAsync} on other {@link GPUBuffer}s
+	 * {@link GPUQueue#onSubmittedWorkDone()} or {@link GPU_Buffer#mapAsync} on other {@link GPU_Buffer}s
 	 * have resolved.
 	 * The resolution of the {@link Promise} returned from {@link GPUQueue#onSubmittedWorkDone}
 	 * **does** imply the completion of
-	 * {@link GPUBuffer#mapAsync} calls made prior to that call,
-	 * on {@link GPUBuffer}s last used exclusively on that queue.
+	 * {@link GPU_Buffer#mapAsync} calls made prior to that call,
+	 * on {@link GPU_Buffer}s last used exclusively on that queue.
 	 * @param mode - Whether the buffer should be mapped for reading or writing.
 	 * @param offset - Offset in bytes into the buffer to the start of the range to map.
 	 * @param size - Size in bytes of the range to map.
 	 */
 	function mapAsync( mode: GPUMapModeFlags, ?offset: GPUSize64, ?size: GPUSize64 ) : Promise<Void>;
 	/**
-	 * Returns an {@link ArrayBuffer} with the contents of the {@link GPUBuffer} in the given mapped range.
+	 * Returns an {@link ArrayBuffer} with the contents of the {@link GPU_Buffer} in the given mapped range.
 	 * @param offset - Offset in bytes into the buffer to return buffer contents from.
 	 * @param size - Size in bytes of the {@link ArrayBuffer} to return.
 	 */
 	function getMappedRange( ?offset: GPUSize64, ?size: GPUSize64 ) : js.lib.ArrayBuffer;
 	/**
-	 * Unmaps the mapped range of the {@link GPUBuffer} and makes it's contents available for use by the
+	 * Unmaps the mapped range of the {@link GPU_Buffer} and makes it's contents available for use by the
 	 * GPU again.
 	 */
 	function unmap() : Void;
 	/**
-	 * Destroys the {@link GPUBuffer}.
+	 * Destroys the {@link GPU_Buffer}.
 	 * Note: It is valid to destroy a buffer multiple times.
 	 * Note: Since no further operations can be enqueued using this buffer, implementations can
 	 * free resource allocations, including mapped memory that was just unmapped.
@@ -470,7 +470,7 @@ extern class GPUQueue {
 	function submit( commandBuffers: Array<GPUCommandBuffer> ) : Void;
 	function onSubmittedWorkDone() : Promise<Void>;
 	/**
-	 * Issues a write operation of the provided data into a {@link GPUBuffer}.
+	 * Issues a write operation of the provided data into a {@link GPU_Buffer}.
 	 * @param buffer - The buffer to write to.
 	 * @param bufferOffset - Offset in bytes into `buffer` to begin writing at.
 	 * @param data - Data to write into `buffer`.
@@ -479,7 +479,7 @@ extern class GPUQueue {
 	 * @param size - Size of content to write from `data` to `buffer`. Given in elements if
 	 * 	`data` is a `TypedArray` and bytes otherwise.
 	 */
-	function writeBuffer( buffer: GPUBuffer, bufferOffset: GPUSize64, data: BufferSource, ?dataOffset: GPUSize64, ?size: GPUSize64 ) : Void;
+	function writeBuffer( buffer: GPU_Buffer, bufferOffset: GPUSize64, data: BufferSource, ?dataOffset: GPUSize64, ?size: GPUSize64 ) : Void;
 	/**
 	 * Issues a write operation of the provided data into a {@link GPUTexture}.
 	 * @param destination - The texture subresource and origin to write to.
@@ -619,7 +619,7 @@ typedef GPUBufferDescriptor = {> GPUObjectDescriptorBase,
 	var usage: GPUBufferUsageFlags;
 	/**
 	 * If `true` creates the buffer in an already mapped state, allowing
-	 * {@link GPUBuffer#getMappedRange} to be called immediately. It is valid to set
+	 * {@link GPU_Buffer#getMappedRange} to be called immediately. It is valid to set
 	 * {@link GPUBufferDescriptor#mappedAtCreation} to `true` even if {@link GPUBufferDescriptor#usage}
 	 * does not contain {@link GPUBufferUsage#MAP_READ} or {@link GPUBufferUsage#MAP_WRITE}. This can be
 	 * used to set the buffer's initial data.
@@ -1153,7 +1153,7 @@ extern class GPURenderCommands extends GPUBindingCommands {
 	 * @param size - Size in bytes of the index data in `buffer`.
 	 * 	Defaults to the size of the buffer minus the offset.
 	 */
-	function setIndexBuffer( buffer: GPUBuffer, indexFormat: GPUIndexFormat, ?offset: GPUSize64, ?size: GPUSize64 ) : Void;
+	function setIndexBuffer( buffer: GPU_Buffer, indexFormat: GPUIndexFormat, ?offset: GPUSize64, ?size: GPUSize64 ) : Void;
 	/**
 	 * Sets the current vertex buffer for the given slot.
 	 * @param slot - The vertex buffer slot to set the vertex buffer for.
@@ -1162,7 +1162,7 @@ extern class GPURenderCommands extends GPUBindingCommands {
 	 * @param size - Size in bytes of the vertex data in `buffer`.
 	 * 	Defaults to the size of the buffer minus the offset.
 	 */
-	function setVertexBuffer( slot: GPUIndex32, buffer: GPUBuffer, ?offset: GPUSize64, ?size: GPUSize64 ) : Void;
+	function setVertexBuffer( slot: GPUIndex32, buffer: GPU_Buffer, ?offset: GPUSize64, ?size: GPUSize64 ) : Void;
 	/**
 	 * Draws primitives.
 	 * See [[#rendering-operations]] for the detailed specification.
@@ -1173,8 +1173,8 @@ extern class GPURenderCommands extends GPUBindingCommands {
 	 */
 	function draw( vertexCount: GPUSize32, ?instanceCount: GPUSize32, ?firstVertex: GPUSize32, ?firstInstance: GPUSize32 ) : Void;
 	function drawIndexed( indexCount: GPUSize32, ?instanceCount: GPUSize32, ?firstIndex: GPUSize32, ?baseVertex: GPUSignedOffset32, ?firstInstance: GPUSize32 ) : Void;
-	function drawIndirect( indirectBuffer: GPUBuffer, indirectOffset: GPUSize64 ) : Void;
-	function drawIndexedIndirect( indirectBuffer: GPUBuffer, indirectOffset: GPUSize64 ) : Void;
+	function drawIndirect( indirectBuffer: GPU_Buffer, indirectOffset: GPUSize64 ) : Void;
+	function drawIndexedIndirect( indirectBuffer: GPU_Buffer, indirectOffset: GPUSize64 ) : Void;
 }
 
 typedef GPUColor = {
@@ -1433,7 +1433,7 @@ extern class GPUComputePassEncoder extends GPUBindingCommands {
 	function dispatchWorkgroups( workgroupCountX: GPUSize32, ?workgroupCountY: GPUSize32, ?workgroupCountZ: GPUSize32 ) : Void;
 	/**
 	 * Dispatch work to be performed with the current {@link GPUComputePipeline} using parameters read
-	 * from a {@link GPUBuffer}.
+	 * from a {@link GPU_Buffer}.
 	 * See [[#computing-operations]] for the detailed specification.
 	 * packed block of **three 32-bit unsigned integer values (12 bytes total)**,
 	 * given in the same order as the arguments for {@link GPUComputePassEncoder#dispatchWorkgroups}.
@@ -1441,7 +1441,7 @@ extern class GPUComputePassEncoder extends GPUBindingCommands {
 	 * @param indirectBuffer - Buffer containing the indirect dispatch parameters.
 	 * @param indirectOffset - Offset in bytes into `indirectBuffer` where the dispatch data begins.
 	 */
-	function dispatchWorkgroupsIndirect( indirectBuffer: GPUBuffer, indirectOffset: GPUSize64 ) : Void;
+	function dispatchWorkgroupsIndirect( indirectBuffer: GPU_Buffer, indirectOffset: GPUSize64 ) : Void;
 	/**
 	 * Completes recording of the compute pass commands sequence.
 	 */
@@ -1461,17 +1461,17 @@ extern class GPUCommandEncoder extends GPUDebugCommands {
 	function beginComputePass( ?descriptor: GPUComputePassDescriptor ) : GPUComputePassEncoder;
 	/**
 	 * Encode a command into the {@link GPUCommandEncoder} that copies data from a sub-region of a
-	 * {@link GPUBuffer} to a sub-region of another {@link GPUBuffer}.
-	 * @param source - The {@link GPUBuffer} to copy from.
+	 * {@link GPU_Buffer} to a sub-region of another {@link GPU_Buffer}.
+	 * @param source - The {@link GPU_Buffer} to copy from.
 	 * @param sourceOffset - Offset in bytes into `source` to begin copying from.
-	 * @param destination - The {@link GPUBuffer} to copy to.
+	 * @param destination - The {@link GPU_Buffer} to copy to.
 	 * @param destinationOffset - Offset in bytes into `destination` to place the copied data.
 	 * @param size - Bytes to copy.
 	 */
-	function copyBufferToBuffer( source: GPUBuffer, sourceOffset: GPUSize64, destination: GPUBuffer, destinationOffset: GPUSize64, size: GPUSize64 ) : Void;
+	function copyBufferToBuffer( source: GPU_Buffer, sourceOffset: GPUSize64, destination: GPU_Buffer, destinationOffset: GPUSize64, size: GPUSize64 ) : Void;
 	/**
 	 * Encode a command into the {@link GPUCommandEncoder} that copies data from a sub-region of a
-	 * {@link GPUBuffer} to a sub-region of one or multiple continuous texture subresources.
+	 * {@link GPU_Buffer} to a sub-region of one or multiple continuous texture subresources.
 	 * @param source - Combined with `copySize`, defines the region of the source buffer.
 	 * @param destination - Combined with `copySize`, defines the region of the destination texture subresource.
 	 * 	`copySize`:
@@ -1479,7 +1479,7 @@ extern class GPUCommandEncoder extends GPUDebugCommands {
 	function copyBufferToTexture( source: GPUImageCopyBuffer, destination: GPUImageCopyTexture, copySize: GPUExtent3D ) : Void;
 	/**
 	 * Encode a command into the {@link GPUCommandEncoder} that copies data from a sub-region of one or
-	 * multiple continuous texture subresourcesto a sub-region of a {@link GPUBuffer}.
+	 * multiple continuous texture subresourcesto a sub-region of a {@link GPU_Buffer}.
 	 * @param source - Combined with `copySize`, defines the region of the source texture subresources.
 	 * @param destination - Combined with `copySize`, defines the region of the destination buffer.
 	 * 	`copySize`:
@@ -1496,12 +1496,12 @@ extern class GPUCommandEncoder extends GPUDebugCommands {
 	function copyTextureToTexture( source: GPUImageCopyTexture, destination: GPUImageCopyTexture, copySize: GPUExtent3D ) : Void;
 	/**
 	 * Encode a command into the {@link GPUCommandEncoder} that fills a sub-region of a
-	 * {@link GPUBuffer} with zeros.
-	 * @param buffer - The {@link GPUBuffer} to clear.
+	 * {@link GPU_Buffer} with zeros.
+	 * @param buffer - The {@link GPU_Buffer} to clear.
 	 * @param offset - Offset in bytes into `buffer` where the sub-region to clear begins.
 	 * @param size - Size in bytes of the sub-region to clear. Defaults to the size of the buffer minus `offset`.
 	 */
-	function clearBuffer( buffer: GPUBuffer, ?offset: GPUSize64, ?size: GPUSize64 ) : Void;
+	function clearBuffer( buffer: GPU_Buffer, ?offset: GPUSize64, ?size: GPUSize64 ) : Void;
 	/**
 	 * Writes a timestamp value into a querySet when all previous commands have completed executing.
 	 * @param querySet - The query set that will store the timestamp values.
@@ -1509,14 +1509,14 @@ extern class GPUCommandEncoder extends GPUDebugCommands {
 	 */
 	function writeTimestamp( querySet: GPUQuerySet, queryIndex: GPUSize32 ) : Void;
 	/**
-	 * Resolves query results from a {@link GPUQuerySet} out into a range of a {@link GPUBuffer}.
+	 * Resolves query results from a {@link GPUQuerySet} out into a range of a {@link GPU_Buffer}.
 	 * 	querySet:
 	 * 	firstQuery:
 	 * 	queryCount:
 	 * 	destination:
 	 * 	destinationOffset:
 	 */
-	function resolveQuerySet( querySet: GPUQuerySet, firstQuery: GPUSize32, queryCount: GPUSize32, destination: GPUBuffer, destinationOffset: GPUSize64 ) : Void;
+	function resolveQuerySet( querySet: GPUQuerySet, firstQuery: GPUSize32, queryCount: GPUSize32, destination: GPU_Buffer, destinationOffset: GPUSize64 ) : Void;
 	/**
 	 * Completes recording of the commands sequence and returns a corresponding {@link GPUCommandBuffer}.
 	 * 	descriptor:
@@ -1614,9 +1614,9 @@ extern class GPUSampler {}
 
 typedef GPUBufferBinding = {
 	/**
-	 * The {@link GPUBuffer} to bind.
+	 * The {@link GPU_Buffer} to bind.
 	 */
-	var buffer: GPUBuffer;
+	var buffer: GPU_Buffer;
 	/**
 	 * The offset, in bytes, from the beginning of {@link GPUBufferBinding#buffer} to the
 	 * beginning of the range exposed to the shader by the buffer binding.
@@ -1840,7 +1840,7 @@ extern class GPUDevice {
 	 */
 	function destroy() : Void;
 
-	function createBuffer( descriptor: GPUBufferDescriptor ) : GPUBuffer;
+	function createBuffer( descriptor: GPUBufferDescriptor ) : GPU_Buffer;
 	function createTexture( descriptor: GPUTextureDescriptor ) : GPUTexture;
 	function createSampler( ?descriptor: GPUSamplerDescriptor ) : GPUSampler;
 	//function importExternalTexture( descriptor: GPUExternalTextureDescriptor ) : GPUExternalTexture;

+ 65 - 32
h3d/impl/WebGpuDriver.hx

@@ -5,7 +5,7 @@ import h3d.impl.WebGpuApi;
 import h3d.mat.Pass;
 
 class WebGpuShader {
-	public var inputs : InputNames;
+	public var format : hxd.BufferFormat;
 	public var pipeline : GPURenderPipeline;
 	public function new() {
 	}
@@ -28,11 +28,12 @@ class WebGpuDriver extends h3d.impl.Driver {
 	var device : GPUDevice;
 
 	var commandList : GPUCommandEncoder;
+	var commandUpload : GPUCommandEncoder;
 	var renderPass : GPURenderPassEncoder;
 	var renderPassDesc : GPURenderPassDescriptor;
 	var needClear : Bool;
 	var currentShader : WebGpuShader;
-	var shaderCache : Map<Int, WebGpuShader> = new Map();
+	var shadersCache : Map<Int, WebGpuShader> = new Map();
 	var frames : Array<WebGpuFrame> = [];
 	var frame : WebGpuFrame;
 	var frameCount : Int = 0;
@@ -100,7 +101,7 @@ class WebGpuDriver extends h3d.impl.Driver {
 		beginFrame();
 	}
 
-	override function setRenderTarget(tex:Null<h3d.mat.Texture>, layer:Int = 0, mipLevel:Int = 0) {
+	override function setRenderTarget(tex:Null<h3d.mat.Texture>, layer:Int = 0, mipLevel:Int = 0, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
 		flushPass();
 		if( tex == null ) {
 			renderPassDesc = {
@@ -162,8 +163,12 @@ class WebGpuDriver extends h3d.impl.Driver {
 
 	function flushFrame() {
 		flushPass();
-		if( commandList != null ) {
-			device.queue.submit([commandList.finish()]);
+		if( commandList != null || commandUpload != null ) {
+			var arr = [];
+			if( commandUpload != null ) arr.push(commandUpload.finish());
+			if( commandList != null ) arr.push(commandList.finish());
+			device.queue.submit(arr);
+			commandUpload = null;
 			commandList = null;
 		}
 		frame = frame == frames[0] ? frames[1] : frames[0];
@@ -204,44 +209,70 @@ class WebGpuDriver extends h3d.impl.Driver {
 		return "WebGPU";
 	}
 
-	override function allocVertexes(m:ManagedBuffer):VertexBuffer {
-		return allocBuffer(VERTEX,m.size,m.stride << 2);
+	override function allocBuffer( b : h3d.Buffer ):GPUBuffer {
+		return _allocBuffer(VERTEX,b.vertices,b.format.strideBytes);
 	}
 
 	override function allocIndexes(count:Int, is32:Bool):IndexBuffer {
-		return allocBuffer(INDEX,count,is32?2:4);
+		var stride = is32?4:2;
+		var buf = _allocBuffer(INDEX,count,stride);
+		return { buf : buf, stride : stride };
 	}
 
-	function allocBuffer(type:GPUBufferUsage,count,stride) {
-		var buf = device.createBuffer({
-			size : count * stride,
-			usage : (type:GPUBufferUsageFlags) | COPY_DST,
-			mappedAtCreation: false,
+	function _allocBuffer(type:GPUBufferUsage,count,stride) {
+		var totSize = count * stride;
+		return device.createBuffer({
+			size : (totSize + 3) & ~3,
+			usage : (type:GPUBufferUsageFlags),
+			mappedAtCreation: true,
 		});
-		return { buf : buf, count : count, stride : stride };
 	}
 
-	override function uploadVertexBytes(v:VertexBuffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
-		uploadBuffer(v,startVertex,vertexCount,buf,bufPos);
+	override function uploadBufferData( b : Buffer, startVertex : Int, vertexCount : Int, buf : hxd.FloatBuffer, bufPos : Int ) {
+		var buf : js.lib.Float32Array = buf.getNative();
+		var buf = new js.lib.Float32Array(buf.buffer, bufPos * 4, vertexCount * b.format.strideBytes >> 2);
+		uploadBuffer(b.vbuf,b.format.stride,startVertex,vertexCount,buf,0);
+	}
+
+	override function uploadBufferBytes(v:h3d.Buffer, startVertex:Int, vertexCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
+		uploadBuffer(v.vbuf,v.format.stride,startVertex,vertexCount,cast buf.getData(),bufPos);
+	}
+
+	override function uploadIndexBuffer( i : IndexBuffer, startIndice : Int, indiceCount : Int, buf : hxd.IndexBuffer, bufPos : Int ) {
+		var buf = new js.lib.Uint16Array(buf.getNative());
+		var sub = new js.lib.Uint16Array(buf.buffer, bufPos * i.stride, indiceCount);
+		uploadBuffer(i.buf,i.stride,startIndice,indiceCount,sub,0);
 	}
 
 	override function uploadIndexBytes(i:IndexBuffer, startIndice:Int, indiceCount:Int, buf:haxe.io.Bytes, bufPos:Int) {
-		uploadBuffer(i,startIndice,indiceCount,buf,bufPos);
+		uploadBuffer(i.buf,i.stride,startIndice,indiceCount,cast buf.getData(),bufPos);
 	}
 
-	function uploadBuffer(b:VertexBuffer,start:Int,count:Int,buf:haxe.io.Bytes, bufPos:Int) {
-		var size = count * b.stride;
+	function uploadBuffer(buf:GPU_Buffer,stride:Int,start:Int,count:Int,data:Dynamic,bufPos:Int) {
+		/*
+		var size = ((count * stride) + 3) & ~3;
 		var tmpBuf = device.createBuffer({
 			size : size,
 			usage : (MAP_WRITE:GPUBufferUsageFlags) | COPY_SRC,
 			mappedAtCreation : true,
 		});
-		new js.lib.Uint8Array(tmpBuf.getMappedRange()).set(cast buf.getData(), bufPos);
+		new js.lib.Uint8Array(tmpBuf.getMappedRange()).set(data, bufPos);
 		tmpBuf.unmap();
 		// copy
-		commandList.copyBufferToBuffer(tmpBuf,0,b.buf,start*b.stride,size);
+		if( commandUpload == null )
+			commandUpload = device.createCommandEncoder();
+		commandUpload.copyBufferToBuffer(tmpBuf,0,buf,start*stride,size);
 		// delete later
 		frame.toDelete.push(tmpBuf);
+		*/
+		var map = buf.getMappedRange();
+		if( data is js.lib.Uint16Array )
+			new js.lib.Uint16Array(map).set(data,bufPos);
+		else if( data is js.lib.Float32Array )
+			new js.lib.Float32Array(map).set(data,bufPos);
+		else
+			new js.lib.Uint8Array(map).set(data,bufPos);
+		buf.unmap();
 	}
 
 
@@ -254,12 +285,13 @@ class WebGpuDriver extends h3d.impl.Driver {
 
 	function makeShader( shader : hxsl.RuntimeShader ) {
 		var sh = new WebGpuShader();
-		var attribNames = [];
+		var format : Array<hxd.BufferFormat.BufferInput> = [];
 		for( v in shader.vertex.data.vars ) {
 			if( v.kind != Input ) continue;
-			attribNames.push(v.name);
+			var t = hxd.BufferFormat.InputFormat.fromHXSL(v.type);
+			format.push({ name : v.name, type : t });
 		}
-		sh.inputs = InputNames.get(attribNames);
+		sh.format = hxd.BufferFormat.make(format);
 
 		var vertex = compile(shader.vertex);
 		var fragment = compile(shader.fragment);
@@ -270,12 +302,12 @@ class WebGpuDriver extends h3d.impl.Driver {
 			vertex : { module : vertex, entryPoint : "main", buffers : [
 				{
 					attributes: [{ shaderLocation : 0, offset : 0, format : Float32x3 }],
-					arrayStride: 4 * 3,
+					arrayStride: 4 * 6,
 					stepMode: GPUVertexStepMode.Vertex
 				},
 				{
-					attributes: [{ shaderLocation : 1, offset : 0, format : Float32x3 }],
-					arrayStride: 4 * 3, // sizeof(float) * 3
+					attributes: [{ shaderLocation : 1, offset : 3*4, format : Float32x3 }],
+					arrayStride: 4 * 6, // sizeof(float) * 3
 					stepMode: GPUVertexStepMode.Vertex
 				}
 			]},
@@ -294,10 +326,10 @@ class WebGpuDriver extends h3d.impl.Driver {
 	}
 
 	override function selectShader( shader : hxsl.RuntimeShader ) {
-		var sh = shaderCache.get(shader.id);
+		var sh = shadersCache.get(shader.id);
 		if( sh == null ) {
 			sh = makeShader(shader);
-			shaderCache.set(shader.id, sh);
+			shadersCache.set(shader.id, sh);
 		}
 		if( sh == currentShader )
 			return false;
@@ -307,13 +339,14 @@ class WebGpuDriver extends h3d.impl.Driver {
 		return true;
 	}
 
-	override function getShaderInputNames():InputNames {
-		return currentShader.inputs;
+	override function selectBuffer(b:Buffer) {
+		for( i in 0...@:privateAccess currentShader.format.inputs.length )
+			renderPass.setVertexBuffer(i, b.vbuf);
 	}
 
 	override function draw(ibuf:IndexBuffer, startIndex:Int, ntriangles:Int) {
 		renderPass.setIndexBuffer(ibuf.buf, ibuf.stride==2?Uint16:Uint32, startIndex*ibuf.stride);
-		renderPass.draw(ntriangles*3);
+		renderPass.drawIndexed(ntriangles*3);
 	}
 
 	static var inst : WebGpuDriver;

+ 24 - 9
hxsl/WgslOut.hx

@@ -24,6 +24,7 @@ class WgslOut {
 	var decls : Array<String>;
 	var isVertex : Bool;
 	var allNames : Map<String, Int>;
+	var hasVarying : Bool;
 	public var varNames : Map<Int,String>;
 
 	var varAccess : Map<Int,String>;
@@ -422,14 +423,23 @@ class WgslOut {
 		for( f in s.funs )
 			collectGlobals(foundGlobals, f.expr);
 
-		add("struct s_input {\n");
-		var index = 0;
+		hasVarying = false;
 		for( v in s.vars )
-			if( v.kind == Input || (v.kind == Var && !isVertex) ) {
-				add('@location(${index++}) ');
-				declVar("_in.", v);
+			if( v.kind == Var ) {
+				hasVarying = true;
+				break;
 			}
-		add("};\n\n");
+
+		if( isVertex || hasVarying ) {
+			add("struct s_input {\n");
+			var index = 0;
+			for( v in s.vars )
+				if( v.kind == Input || (v.kind == Var && !isVertex) ) {
+					add('@location(${index++}) ');
+					declVar("_in.", v);
+				}
+			add("};\n\n");
+		}
 
 		add("struct s_output {\n");
 		var index = 0;
@@ -519,7 +529,8 @@ class WgslOut {
 	}
 
 	function initStatics( s : ShaderData ) {
-		add("var<private> _in : s_input;\n");
+		if( isVertex || hasVarying )
+			add("var<private> _in : s_input;\n");
 		add("var<private> _out : s_output;\n");
 
 		add("\n");
@@ -534,8 +545,12 @@ class WgslOut {
 
 	function emitMain( s : ShaderData, expr : TExpr ) {
 		add(isVertex ? "@vertex " : "@fragment ");
-		add("fn main( in__ : s_input ) -> s_output {\n");
-		add("\t_in = in__;\n");
+		if( isVertex || hasVarying ) {
+			add("fn main( in__ : s_input ) -> s_output {\n");
+			add("\t_in = in__;\n");
+		} else {
+			add("fn main() -> s_output {\n");
+		}
 		switch( expr.e ) {
 		case TBlock(el):
 			for( e in el ) {