From d893334497171e47ccc24c873f34055484bdf18b Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Sun, 13 Jul 2025 08:06:21 +0200 Subject: [PATCH 01/13] Allows the specification of workgroups for compute shaders and the specification of the dispatchSize --- src/nodes/gpgpu/ComputeNode.js | 23 +++++++--- src/renderers/common/Renderer.js | 26 ++--------- src/renderers/webgpu/WebGPUBackend.js | 44 ++++++++++++------- src/renderers/webgpu/nodes/WGSLNodeBuilder.js | 16 +++++-- 4 files changed, 62 insertions(+), 47 deletions(-) diff --git a/src/nodes/gpgpu/ComputeNode.js b/src/nodes/gpgpu/ComputeNode.js index ec64913b2e43e0..fbe325c459088e 100644 --- a/src/nodes/gpgpu/ComputeNode.js +++ b/src/nodes/gpgpu/ComputeNode.js @@ -20,9 +20,9 @@ class ComputeNode extends Node { * * @param {Node} computeNode - TODO * @param {number} count - TODO. - * @param {Array} [workgroupSize=[64]] - TODO. + * @param {Array} [workgroupSize = [ 64, 1, 1 ]] */ - constructor( computeNode, count, workgroupSize = [ 64 ] ) { + constructor( computeNode, count, workgroupSize = [ 64, 1, 1 ] ) { super( 'void' ); @@ -53,7 +53,7 @@ class ComputeNode extends Node { * TODO * * @type {Array} - * @default [64] + * @default [ 64, 1, 1 ] */ this.workgroupSize = workgroupSize; @@ -220,9 +220,22 @@ export default ComputeNode; * @function * @param {Node} node - TODO * @param {number} count - TODO. - * @param {Array} [workgroupSize=[64]] - TODO. + * @param {Array} [workgroupSize=[ 64, 1, 1 ]] * @returns {AtomicFunctionNode} */ -export const compute = ( node, count, workgroupSize ) => nodeObject( new ComputeNode( nodeObject( node ), count, workgroupSize ) ); +export const compute = ( node, countOrWorkgroupSize, workgroupSize ) => { + + let count = countOrWorkgroupSize; + + if ( Array.isArray( countOrWorkgroupSize ) ) { + + workgroupSize = countOrWorkgroupSize; + count = null; + + } + + return nodeObject( new ComputeNode( nodeObject( node ), count, workgroupSize ) ); + +}; addMethodChaining( 'compute', compute ); diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index c06fe9b02d4c94..82e9ce0d01425c 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -1243,24 +1243,13 @@ class Renderer { frameBufferTarget.depthBuffer = depth; frameBufferTarget.stencilBuffer = stencil; - if ( outputRenderTarget !== null ) { - - frameBufferTarget.setSize( outputRenderTarget.width, outputRenderTarget.height, outputRenderTarget.depth ); - - } else { - - frameBufferTarget.setSize( width, height, 1 ); - - } - + frameBufferTarget.setSize( width, height, outputRenderTarget !== null ? outputRenderTarget.depth : 1 ); frameBufferTarget.viewport.copy( this._viewport ); frameBufferTarget.scissor.copy( this._scissor ); frameBufferTarget.viewport.multiplyScalar( this._pixelRatio ); frameBufferTarget.scissor.multiplyScalar( this._pixelRatio ); frameBufferTarget.scissorTest = this._scissorTest; frameBufferTarget.multiview = outputRenderTarget !== null ? outputRenderTarget.multiview : false; - frameBufferTarget.resolveDepthBuffer = outputRenderTarget !== null ? outputRenderTarget.resolveDepthBuffer : true; - frameBufferTarget._autoAllocateDepthBuffer = outputRenderTarget !== null ? outputRenderTarget._autoAllocateDepthBuffer : false; return frameBufferTarget; @@ -1516,15 +1505,6 @@ class Renderer { } - _setXRLayerSize( width, height ) { - - this._width = width; - this._height = height; - - this.setViewport( 0, 0, width, height ); - - } - /** * The output pass performs tone mapping and color space conversion. * @@ -2310,7 +2290,7 @@ class Renderer { * @param {Node|Array} computeNodes - The compute node(s). * @return {Promise|undefined} A Promise that resolve when the compute has finished. Only returned when the renderer has not been initialized. */ - compute( computeNodes ) { + compute( computeNodes, dispatchSize = [ 0, 0, 0 ] ) { if ( this._isDeviceLost === true ) return; @@ -2389,7 +2369,7 @@ class Renderer { const computeBindings = bindings.getForCompute( computeNode ); const computePipeline = pipelines.getForCompute( computeNode, computeBindings ); - backend.compute( computeNodes, computeNode, computeBindings, computePipeline ); + backend.compute( computeNodes, computeNode, computeBindings, computePipeline, dispatchSize ); } diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js index 84ce1d8a07c65f..772dfbb4cfa635 100644 --- a/src/renderers/webgpu/WebGPUBackend.js +++ b/src/renderers/webgpu/WebGPUBackend.js @@ -1319,9 +1319,13 @@ class WebGPUBackend extends Backend { * @param {Array} bindings - The bindings. * @param {ComputePipeline} pipeline - The compute pipeline. */ - compute( computeGroup, computeNode, bindings, pipeline ) { + compute( computeGroup, computeNode, bindings, pipeline, dispatchSize ) { + const computeNodeData = this.get( computeNode ); const { passEncoderGPU } = this.get( computeGroup ); + const isValid = dispatchSize[ 0 ] > 0 && dispatchSize[ 1 ] > 0 && dispatchSize[ 2 ] > 0; + + dispatchSize = isValid ? dispatchSize : computeNodeData; // pipeline @@ -1340,30 +1344,38 @@ class WebGPUBackend extends Backend { } - const maxComputeWorkgroupsPerDimension = this.device.limits.maxComputeWorkgroupsPerDimension; + if ( isValid ) { - const computeNodeData = this.get( computeNode ); + passEncoderGPU.dispatchWorkgroups( + dispatchSize[ 0 ], + dispatchSize[ 1 ], + dispatchSize[ 2 ] + ); - if ( computeNodeData.dispatchSize === undefined ) computeNodeData.dispatchSize = { x: 0, y: 1, z: 1 }; + } else { - const { dispatchSize } = computeNodeData; + const maxComputeWorkgroupsPerDimension = this.device.limits.maxComputeWorkgroupsPerDimension; - if ( computeNode.dispatchCount > maxComputeWorkgroupsPerDimension ) { + if ( computeNodeData.dispatchSize === undefined ) computeNodeData.dispatchSize = { x: 0, y: 1, z: 1 }; - dispatchSize.x = Math.min( computeNode.dispatchCount, maxComputeWorkgroupsPerDimension ); - dispatchSize.y = Math.ceil( computeNode.dispatchCount / maxComputeWorkgroupsPerDimension ); + if ( computeNode.dispatchCount > maxComputeWorkgroupsPerDimension ) { - } else { + dispatchSize.x = Math.min( computeNode.dispatchCount, maxComputeWorkgroupsPerDimension ); + dispatchSize.y = Math.ceil( computeNode.dispatchCount / maxComputeWorkgroupsPerDimension ); + + } else { - dispatchSize.x = computeNode.dispatchCount; + dispatchSize.x = computeNode.dispatchCount; - } + } - passEncoderGPU.dispatchWorkgroups( - dispatchSize.x, - dispatchSize.y, - dispatchSize.z - ); + passEncoderGPU.dispatchWorkgroups( + dispatchSize.x, + dispatchSize.y, + dispatchSize.z + ); + + } } diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js index f67f828f487d95..f0c3cd2ce8888a 100644 --- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -1898,7 +1898,13 @@ ${ flowData.code } } else { - this.computeShader = this._getWGSLComputeCode( shadersData.compute, ( this.object.workgroupSize || [ 64 ] ).join( ', ' ) ); + //this.computeShader = this._getWGSLComputeCode( shadersData.compute, ( this.object.workgroupSize || [ 64 ] ).join( ', ' ) ); + + const workgroupSize = this.object.workgroupSize || [ 8, 8, 1 ]; + + if ( workgroupSize.length !== 3 ) throw new Error( "workgroupSize must have 3 elements" ); + + this.computeShader = this._getWGSLComputeCode( shadersData.compute, workgroupSize ); } @@ -2103,6 +2109,8 @@ fn main( ${shaderData.varyings} ) -> ${shaderData.returnType} { */ _getWGSLComputeCode( shaderData, workgroupSize ) { + const [ workgroupSizeX, workgroupSizeY, workgroupSizeZ ] = workgroupSize; + return `${ this.getSignature() } // directives ${shaderData.directives} @@ -2122,11 +2130,13 @@ ${shaderData.uniforms} // codes ${shaderData.codes} -@compute @workgroup_size( ${workgroupSize} ) +@compute @workgroup_size( ${workgroupSizeX}, ${workgroupSizeY}, ${workgroupSizeZ} ) fn main( ${shaderData.attributes} ) { // system - instanceIndex = globalId.x + globalId.y * numWorkgroups.x * u32(${workgroupSize}) + globalId.z * numWorkgroups.x * numWorkgroups.y * u32(${workgroupSize}); + instanceIndex = globalId.x + + globalId.y * (${workgroupSizeX} * numWorkgroups.x) + + globalId.z * (${workgroupSizeX} * numWorkgroups.x) * (${workgroupSizeY} * numWorkgroups.y); // vars ${shaderData.vars} From 334b61a0d429d8a10cdfccf8448521af9ffb5b76 Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Sun, 13 Jul 2025 09:02:00 +0200 Subject: [PATCH 02/13] Allows the specification of workgroups for compute shaders and the specification of the dispatchSize --- src/nodes/gpgpu/ComputeNode.js | 2 +- src/renderers/common/Renderer.js | 1 + src/renderers/webgpu/WebGPUBackend.js | 1 + src/renderers/webgpu/nodes/WGSLNodeBuilder.js | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/nodes/gpgpu/ComputeNode.js b/src/nodes/gpgpu/ComputeNode.js index fbe325c459088e..0c51c0ee17da37 100644 --- a/src/nodes/gpgpu/ComputeNode.js +++ b/src/nodes/gpgpu/ComputeNode.js @@ -219,7 +219,7 @@ export default ComputeNode; * @tsl * @function * @param {Node} node - TODO - * @param {number} count - TODO. + * @param {number} countOrWorkgroupSize - TODO. * @param {Array} [workgroupSize=[ 64, 1, 1 ]] * @returns {AtomicFunctionNode} */ diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 82e9ce0d01425c..3a1aec50e526ce 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -2288,6 +2288,7 @@ class Renderer { * if the renderer has been initialized. * * @param {Node|Array} computeNodes - The compute node(s). + * @param {Array} dispatchSize - Array with [x,y,z] values for dispatch. * @return {Promise|undefined} A Promise that resolve when the compute has finished. Only returned when the renderer has not been initialized. */ compute( computeNodes, dispatchSize = [ 0, 0, 0 ] ) { diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js index 772dfbb4cfa635..700ea9536dca79 100644 --- a/src/renderers/webgpu/WebGPUBackend.js +++ b/src/renderers/webgpu/WebGPUBackend.js @@ -1318,6 +1318,7 @@ class WebGPUBackend extends Backend { * @param {Node} computeNode - The compute node. * @param {Array} bindings - The bindings. * @param {ComputePipeline} pipeline - The compute pipeline. + * @param {Array} dispatchSize - Array with [x,y,z] values for dispatch. */ compute( computeGroup, computeNode, bindings, pipeline, dispatchSize ) { diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js index f0c3cd2ce8888a..4f5c1f6ed9e3bd 100644 --- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -1902,7 +1902,7 @@ ${ flowData.code } const workgroupSize = this.object.workgroupSize || [ 8, 8, 1 ]; - if ( workgroupSize.length !== 3 ) throw new Error( "workgroupSize must have 3 elements" ); + if ( workgroupSize.length !== 3 ) throw new Error( 'workgroupSize must have 3 elements' ); this.computeShader = this._getWGSLComputeCode( shadersData.compute, workgroupSize ); From 910ce623ac76c634abe9a80b17fe39214628aea6 Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Sun, 13 Jul 2025 09:16:03 +0200 Subject: [PATCH 03/13] Allows the specification of workgroups for compute shaders and the specification of the dispatchSize --- src/renderers/webgpu/nodes/WGSLNodeBuilder.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js index 4f5c1f6ed9e3bd..b2a3dd58421e8c 100644 --- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -1898,9 +1898,7 @@ ${ flowData.code } } else { - //this.computeShader = this._getWGSLComputeCode( shadersData.compute, ( this.object.workgroupSize || [ 64 ] ).join( ', ' ) ); - - const workgroupSize = this.object.workgroupSize || [ 8, 8, 1 ]; + const workgroupSize = this.object.workgroupSize || [ 64, 1, 1 ]; if ( workgroupSize.length !== 3 ) throw new Error( 'workgroupSize must have 3 elements' ); From 58af151cba75a3494ee37205c6678c3f869d9a4c Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Sun, 13 Jul 2025 11:44:51 +0200 Subject: [PATCH 04/13] Trigger GitHub to update PR From 227f25a8592251b9e0dd2de2902f4734d586ec19 Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Sun, 13 Jul 2025 11:47:12 +0200 Subject: [PATCH 05/13] rebase branch --- src/renderers/common/Renderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 3a1aec50e526ce..91440e34b59d2a 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -1241,6 +1241,7 @@ class Renderer { const outputRenderTarget = this.getOutputRenderTarget(); + frameBufferTarget.depthBuffer = depth; frameBufferTarget.stencilBuffer = stencil; frameBufferTarget.setSize( width, height, outputRenderTarget !== null ? outputRenderTarget.depth : 1 ); From 0d2d7fd8700e2950f8f45a1a17049ebd5c0a8315 Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Sun, 13 Jul 2025 11:47:29 +0200 Subject: [PATCH 06/13] rebase branch --- src/renderers/common/Renderer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 91440e34b59d2a..3a1aec50e526ce 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -1241,7 +1241,6 @@ class Renderer { const outputRenderTarget = this.getOutputRenderTarget(); - frameBufferTarget.depthBuffer = depth; frameBufferTarget.stencilBuffer = stencil; frameBufferTarget.setSize( width, height, outputRenderTarget !== null ? outputRenderTarget.depth : 1 ); From d44829ac58621e73fe12ac58916ba54f15b890e1 Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Sun, 13 Jul 2025 11:49:24 +0200 Subject: [PATCH 07/13] rebase branch --- src/renderers/common/Renderer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 3a1aec50e526ce..91440e34b59d2a 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -1241,6 +1241,7 @@ class Renderer { const outputRenderTarget = this.getOutputRenderTarget(); + frameBufferTarget.depthBuffer = depth; frameBufferTarget.stencilBuffer = stencil; frameBufferTarget.setSize( width, height, outputRenderTarget !== null ? outputRenderTarget.depth : 1 ); From 2332ea5fe33b727417354074910650261f266474 Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Sun, 13 Jul 2025 11:51:59 +0200 Subject: [PATCH 08/13] rebase branch --- src/renderers/common/Renderer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 91440e34b59d2a..3a1aec50e526ce 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -1241,7 +1241,6 @@ class Renderer { const outputRenderTarget = this.getOutputRenderTarget(); - frameBufferTarget.depthBuffer = depth; frameBufferTarget.stencilBuffer = stencil; frameBufferTarget.setSize( width, height, outputRenderTarget !== null ? outputRenderTarget.depth : 1 ); From 14bd77ff9d46e284318377bc595f6f98c9e62af7 Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Mon, 14 Jul 2025 03:27:41 +0200 Subject: [PATCH 09/13] update --- src/renderers/common/Renderer.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 3a1aec50e526ce..999abf9fd15780 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -1243,13 +1243,24 @@ class Renderer { frameBufferTarget.depthBuffer = depth; frameBufferTarget.stencilBuffer = stencil; - frameBufferTarget.setSize( width, height, outputRenderTarget !== null ? outputRenderTarget.depth : 1 ); + if ( outputRenderTarget !== null ) { + + frameBufferTarget.setSize( outputRenderTarget.width, outputRenderTarget.height, outputRenderTarget.depth ); + + } else { + + frameBufferTarget.setSize( width, height, 1 ); + + } + frameBufferTarget.viewport.copy( this._viewport ); frameBufferTarget.scissor.copy( this._scissor ); frameBufferTarget.viewport.multiplyScalar( this._pixelRatio ); frameBufferTarget.scissor.multiplyScalar( this._pixelRatio ); frameBufferTarget.scissorTest = this._scissorTest; frameBufferTarget.multiview = outputRenderTarget !== null ? outputRenderTarget.multiview : false; + frameBufferTarget.resolveDepthBuffer = outputRenderTarget !== null ? outputRenderTarget.resolveDepthBuffer : true; + frameBufferTarget._autoAllocateDepthBuffer = outputRenderTarget !== null ? outputRenderTarget._autoAllocateDepthBuffer : false; return frameBufferTarget; @@ -1505,6 +1516,15 @@ class Renderer { } + _setXRLayerSize( width, height ) { + + this._width = width; + this._height = height; + + this.setViewport( 0, 0, width, height ); + + } + /** * The output pass performs tone mapping and color space conversion. * From b55b9e97f6d0f5829e4d8fc364949e72b0d9e98c Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Mon, 14 Jul 2025 03:49:08 +0200 Subject: [PATCH 10/13] update --- src/renderers/common/Renderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 999abf9fd15780..e96d4165fe9a66 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -2308,7 +2308,7 @@ class Renderer { * if the renderer has been initialized. * * @param {Node|Array} computeNodes - The compute node(s). - * @param {Array} dispatchSize - Array with [x,y,z] values for dispatch. + * @param {Array} dispatchSize - Array with [ x,y,z ] values for dispatch. * @return {Promise|undefined} A Promise that resolve when the compute has finished. Only returned when the renderer has not been initialized. */ compute( computeNodes, dispatchSize = [ 0, 0, 0 ] ) { From 93850e9b130db4c87a6e0b979fb146518b602397 Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Tue, 15 Jul 2025 07:13:36 +0200 Subject: [PATCH 11/13] Extend compute with flexible workgroupSize and dispatchSize validation --- src/nodes/gpgpu/ComputeNode.js | 34 ++++++++++++++++--- src/renderers/common/Renderer.js | 34 ++++++++++++++++++- src/renderers/webgpu/WebGPUBackend.js | 4 +-- src/renderers/webgpu/nodes/WGSLNodeBuilder.js | 4 +-- 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/src/nodes/gpgpu/ComputeNode.js b/src/nodes/gpgpu/ComputeNode.js index 0c51c0ee17da37..4bb89f8f37e76a 100644 --- a/src/nodes/gpgpu/ComputeNode.js +++ b/src/nodes/gpgpu/ComputeNode.js @@ -219,18 +219,42 @@ export default ComputeNode; * @tsl * @function * @param {Node} node - TODO - * @param {number} countOrWorkgroupSize - TODO. - * @param {Array} [workgroupSize=[ 64, 1, 1 ]] + * @param {number} countOrWorkgroupSize - TODO, depends on the future of count * @returns {AtomicFunctionNode} */ -export const compute = ( node, countOrWorkgroupSize, workgroupSize ) => { +export const compute = ( node, countOrWorkgroupSize ) => { - let count = countOrWorkgroupSize; + let count = null; + let workgroupSize = [ 64, 1, 1 ]; //default if ( Array.isArray( countOrWorkgroupSize ) ) { workgroupSize = countOrWorkgroupSize; - count = null; + + if ( workgroupSize.length === 0 || workgroupSize.length > 3 ) { + + throw new Error( 'workgroupSize must have 1, 2, or 3 elements' ); + + } + + for ( let i = 0; i < workgroupSize.length; i ++ ) { + + const val = workgroupSize[ i ]; + + if ( typeof val !== 'number' || val <= 0 || ! Number.isInteger( val ) ) { + + throw new Error( `workgroupSize element at index ${i} must be a positive integer` ); + + } + + } + + // Implicit fill-up to [ x, y, z ] with 1s, just like WGSL treats @workgroup_size when fewer dimensions are specified + while ( workgroupSize.length < 3 ) workgroupSize.push( 1 ); + + } else { + + count = countOrWorkgroupSize; } diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index e96d4165fe9a66..007f2be138c1e9 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -2311,7 +2311,7 @@ class Renderer { * @param {Array} dispatchSize - Array with [ x,y,z ] values for dispatch. * @return {Promise|undefined} A Promise that resolve when the compute has finished. Only returned when the renderer has not been initialized. */ - compute( computeNodes, dispatchSize = [ 0, 0, 0 ] ) { + compute( computeNodes, dispatchSize = null ) { if ( this._isDeviceLost === true ) return; @@ -2352,6 +2352,38 @@ class Renderer { } + if ( dispatchSize !== null ) { + + if ( ! Array.isArray( dispatchSize ) ) { + + throw new Error( 'dispatchSize must be an array' ); + + } + + if ( dispatchSize.length === 0 || dispatchSize.length > 3 ) { + + throw new Error( 'dispatchSize must have 1, 2, or 3 elements' ); + + } + + // Check each element for positive integer + for ( let i = 0; i < dispatchSize.length; i ++ ) { + + const val = dispatchSize[ i ]; + + if ( typeof val !== 'number' || val <= 0 || ! Number.isInteger( val ) ) { + + throw new Error( `dispatchSize element at index ${i} must be a positive integer` ); + + } + + } + + // Implicit fill-up + while ( dispatchSize.length < 3 ) dispatchSize.push( 1 ); + + } + backend.beginCompute( computeNodes ); for ( const computeNode of computeList ) { diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js index 700ea9536dca79..c3a01d76e6f3c1 100644 --- a/src/renderers/webgpu/WebGPUBackend.js +++ b/src/renderers/webgpu/WebGPUBackend.js @@ -1324,9 +1324,9 @@ class WebGPUBackend extends Backend { const computeNodeData = this.get( computeNode ); const { passEncoderGPU } = this.get( computeGroup ); - const isValid = dispatchSize[ 0 ] > 0 && dispatchSize[ 1 ] > 0 && dispatchSize[ 2 ] > 0; + const isValid = Array.isArray( dispatchSize ); - dispatchSize = isValid ? dispatchSize : computeNodeData; + dispatchSize = isValid ? dispatchSize : computeNodeData; //Or at some point an initial value if condition is not met and count should be depracticed // pipeline diff --git a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js index b2a3dd58421e8c..ec4bceeb321b1a 100644 --- a/src/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/src/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -1898,9 +1898,7 @@ ${ flowData.code } } else { - const workgroupSize = this.object.workgroupSize || [ 64, 1, 1 ]; - - if ( workgroupSize.length !== 3 ) throw new Error( 'workgroupSize must have 3 elements' ); + const workgroupSize = this.object.workgroupSize; //early strictly validated in computeNode this.computeShader = this._getWGSLComputeCode( shadersData.compute, workgroupSize ); From 9359fa8d7c4419f927096b3b60ebca6a928ef62d Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Tue, 15 Jul 2025 07:29:09 +0200 Subject: [PATCH 12/13] Extend compute with flexible workgroupSize and dispatchSize validation --- src/renderers/common/Renderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index 007f2be138c1e9..cb3648c9c46ba5 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -2308,7 +2308,7 @@ class Renderer { * if the renderer has been initialized. * * @param {Node|Array} computeNodes - The compute node(s). - * @param {Array} dispatchSize - Array with [ x,y,z ] values for dispatch. + * @param {Array} dispatchSize - Array with [ x,y,z ] values for dispatch. Default = null * @return {Promise|undefined} A Promise that resolve when the compute has finished. Only returned when the renderer has not been initialized. */ compute( computeNodes, dispatchSize = null ) { From b034372527a760a3ad7cf07834fe1c6dc3b35e3f Mon Sep 17 00:00:00 2001 From: Attila Schroeder Date: Tue, 15 Jul 2025 08:42:23 +0200 Subject: [PATCH 13/13] Extend compute with flexible workgroupSize and dispatchSize validation --- src/renderers/common/Renderer.js | 32 -------------------------- src/renderers/webgpu/WebGPUBackend.js | 33 +++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/renderers/common/Renderer.js b/src/renderers/common/Renderer.js index cb3648c9c46ba5..98ac6a2bce2fea 100644 --- a/src/renderers/common/Renderer.js +++ b/src/renderers/common/Renderer.js @@ -2352,38 +2352,6 @@ class Renderer { } - if ( dispatchSize !== null ) { - - if ( ! Array.isArray( dispatchSize ) ) { - - throw new Error( 'dispatchSize must be an array' ); - - } - - if ( dispatchSize.length === 0 || dispatchSize.length > 3 ) { - - throw new Error( 'dispatchSize must have 1, 2, or 3 elements' ); - - } - - // Check each element for positive integer - for ( let i = 0; i < dispatchSize.length; i ++ ) { - - const val = dispatchSize[ i ]; - - if ( typeof val !== 'number' || val <= 0 || ! Number.isInteger( val ) ) { - - throw new Error( `dispatchSize element at index ${i} must be a positive integer` ); - - } - - } - - // Implicit fill-up - while ( dispatchSize.length < 3 ) dispatchSize.push( 1 ); - - } - backend.beginCompute( computeNodes ); for ( const computeNode of computeList ) { diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js index c3a01d76e6f3c1..7d15d935500af6 100644 --- a/src/renderers/webgpu/WebGPUBackend.js +++ b/src/renderers/webgpu/WebGPUBackend.js @@ -1324,9 +1324,6 @@ class WebGPUBackend extends Backend { const computeNodeData = this.get( computeNode ); const { passEncoderGPU } = this.get( computeGroup ); - const isValid = Array.isArray( dispatchSize ); - - dispatchSize = isValid ? dispatchSize : computeNodeData; //Or at some point an initial value if condition is not met and count should be depracticed // pipeline @@ -1345,7 +1342,33 @@ class WebGPUBackend extends Backend { } - if ( isValid ) { + if ( dispatchSize !== null ) { + + if ( ! Array.isArray( dispatchSize ) ) { + + throw new Error( 'dispatchSize must be an array' ); + + } + + if ( dispatchSize.length === 0 || dispatchSize.length > 3 ) { + + throw new Error( 'dispatchSize must have 1, 2, or 3 elements' ); + + } + + for ( let i = 0; i < dispatchSize.length; i ++ ) { + + const value = dispatchSize[ i ]; + + if ( typeof value !== 'number' || value <= 0 || ! Number.isInteger( value ) ) { + + throw new Error( `dispatchSize element at index ${i} must be a positive integer` ); + + } + + } + + while ( dispatchSize.length < 3 ) dispatchSize.push( 1 ); passEncoderGPU.dispatchWorkgroups( dispatchSize[ 0 ], @@ -1355,6 +1378,8 @@ class WebGPUBackend extends Backend { } else { + dispatchSize = computeNodeData; + const maxComputeWorkgroupsPerDimension = this.device.limits.maxComputeWorkgroupsPerDimension; if ( computeNodeData.dispatchSize === undefined ) computeNodeData.dispatchSize = { x: 0, y: 1, z: 1 };