|
|
@@ -63,6 +63,16 @@ const refreshUniforms = [
|
|
|
'transmissionMap'
|
|
|
];
|
|
|
|
|
|
+
|
|
|
+/**
|
|
|
+ * A WeakMap to cache lights data for node materials.
|
|
|
+ * Cache lights data by render ID to avoid unnecessary recalculations.
|
|
|
+ *
|
|
|
+ * @private
|
|
|
+ * @type {WeakMap<LightsNode,Object>}
|
|
|
+ */
|
|
|
+const _lightsCache = new WeakMap();
|
|
|
+
|
|
|
/**
|
|
|
* This class is used by {@link WebGPURenderer} as management component.
|
|
|
* It's primary purpose is to determine whether render objects require a
|
|
|
@@ -204,6 +214,8 @@ class NodeMaterialObserver {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ data.lights = this.getLightsData( renderObject.lightsNode.getLights() );
|
|
|
+
|
|
|
this.renderObjects.set( renderObject, data );
|
|
|
|
|
|
}
|
|
|
@@ -307,9 +319,10 @@ class NodeMaterialObserver {
|
|
|
* Returns `true` if the given render object has not changed its state.
|
|
|
*
|
|
|
* @param {RenderObject} renderObject - The render object.
|
|
|
+ * @param {Array<Light>} lightsData - The current material lights.
|
|
|
* @return {boolean} Whether the given render object has changed its state or not.
|
|
|
*/
|
|
|
- equals( renderObject ) {
|
|
|
+ equals( renderObject, lightsData ) {
|
|
|
|
|
|
const { object, material, geometry } = renderObject;
|
|
|
|
|
|
@@ -470,6 +483,22 @@ class NodeMaterialObserver {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ // lights
|
|
|
+
|
|
|
+ if ( renderObjectData.lights ) {
|
|
|
+
|
|
|
+ for ( let i = 0; i < lightsData.length; i ++ ) {
|
|
|
+
|
|
|
+ if ( renderObjectData.lights[ i ].map !== lightsData[ i ].map ) {
|
|
|
+
|
|
|
+ return false;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
// center
|
|
|
|
|
|
if ( renderObjectData.center ) {
|
|
|
@@ -496,6 +525,61 @@ class NodeMaterialObserver {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the lights data for the given material lights.
|
|
|
+ *
|
|
|
+ * @param {Array<Light>} materialLights - The material lights.
|
|
|
+ * @return {Array<Object>} The lights data for the given material lights.
|
|
|
+ */
|
|
|
+ getLightsData( materialLights ) {
|
|
|
+
|
|
|
+ const lights = [];
|
|
|
+
|
|
|
+ for ( const light of materialLights ) {
|
|
|
+
|
|
|
+ if ( light.isSpotLight === true && light.map !== null ) {
|
|
|
+
|
|
|
+ // only add lights that have a map
|
|
|
+
|
|
|
+ lights.push( { map: light.map.version } );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return lights;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the lights for the given lights node and render ID.
|
|
|
+ *
|
|
|
+ * @param {LightsNode} lightsNode - The lights node.
|
|
|
+ * @param {number} renderId - The render ID.
|
|
|
+ * @return {Array} The lights for the given lights node and render ID.
|
|
|
+ */
|
|
|
+ getLights( lightsNode, renderId ) {
|
|
|
+
|
|
|
+ if ( _lightsCache.has( lightsNode ) ) {
|
|
|
+
|
|
|
+ const cached = _lightsCache.get( lightsNode );
|
|
|
+
|
|
|
+ if ( cached.renderId === renderId ) {
|
|
|
+
|
|
|
+ return cached.lightsData;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const lightsData = this.getLightsData( lightsNode.getLights() );
|
|
|
+
|
|
|
+ _lightsCache.set( lightsNode, { renderId, lightsData } );
|
|
|
+
|
|
|
+ return lightsData;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Checks if the given render object requires a refresh.
|
|
|
*
|
|
|
@@ -524,7 +608,8 @@ class NodeMaterialObserver {
|
|
|
if ( isStatic || isBundle )
|
|
|
return false;
|
|
|
|
|
|
- const notEqual = this.equals( renderObject ) !== true;
|
|
|
+ const lightsData = this.getLights( renderObject.lightsNode, renderId );
|
|
|
+ const notEqual = this.equals( renderObject, lightsData ) !== true;
|
|
|
|
|
|
return notEqual;
|
|
|
|
|
|
@@ -8855,10 +8940,9 @@ class ComputeNode extends Node {
|
|
|
* Constructs a new compute node.
|
|
|
*
|
|
|
* @param {Node} computeNode - TODO
|
|
|
- * @param {number} count - TODO.
|
|
|
- * @param {Array<number>} [workgroupSize=[64]] - TODO.
|
|
|
+ * @param {Array<number>} workgroupSize - TODO.
|
|
|
*/
|
|
|
- constructor( computeNode, count, workgroupSize = [ 64 ] ) {
|
|
|
+ constructor( computeNode, workgroupSize ) {
|
|
|
|
|
|
super( 'void' );
|
|
|
|
|
|
@@ -8878,18 +8962,12 @@ class ComputeNode extends Node {
|
|
|
*/
|
|
|
this.computeNode = computeNode;
|
|
|
|
|
|
- /**
|
|
|
- * TODO
|
|
|
- *
|
|
|
- * @type {number}
|
|
|
- */
|
|
|
- this.count = count;
|
|
|
|
|
|
/**
|
|
|
* TODO
|
|
|
*
|
|
|
* @type {Array<number>}
|
|
|
- * @default [64]
|
|
|
+ * @default [ 64 ]
|
|
|
*/
|
|
|
this.workgroupSize = workgroupSize;
|
|
|
|
|
|
@@ -8898,7 +8976,7 @@ class ComputeNode extends Node {
|
|
|
*
|
|
|
* @type {number}
|
|
|
*/
|
|
|
- this.dispatchCount = 0;
|
|
|
+ this.count = null;
|
|
|
|
|
|
/**
|
|
|
* TODO
|
|
|
@@ -8931,7 +9009,19 @@ class ComputeNode extends Node {
|
|
|
*/
|
|
|
this.onInitFunction = null;
|
|
|
|
|
|
- this.updateDispatchCount();
|
|
|
+ }
|
|
|
+
|
|
|
+ setCount( count ) {
|
|
|
+
|
|
|
+ this.count = count;
|
|
|
+
|
|
|
+ return this;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ getCount() {
|
|
|
+
|
|
|
+ return this.count;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -8958,22 +9048,6 @@ class ComputeNode extends Node {
|
|
|
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * TODO
|
|
|
- */
|
|
|
- updateDispatchCount() {
|
|
|
-
|
|
|
- const { count, workgroupSize } = this;
|
|
|
-
|
|
|
- let size = workgroupSize[ 0 ];
|
|
|
-
|
|
|
- for ( let i = 1; i < workgroupSize.length; i ++ )
|
|
|
- size *= workgroupSize[ i ];
|
|
|
-
|
|
|
- this.dispatchCount = Math.ceil( count / size );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* TODO
|
|
|
*
|
|
|
@@ -9047,6 +9121,45 @@ class ComputeNode extends Node {
|
|
|
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * TSL function for creating a compute kernel node.
|
|
|
+ *
|
|
|
+ * @tsl
|
|
|
+ * @function
|
|
|
+ * @param {Node} node - TODO
|
|
|
+ * @param {Array<number>} [workgroupSize=[64]] - TODO.
|
|
|
+ * @returns {AtomicFunctionNode}
|
|
|
+ */
|
|
|
+const computeKernel = ( node, workgroupSize = [ 64 ] ) => {
|
|
|
+
|
|
|
+ if ( workgroupSize.length === 0 || workgroupSize.length > 3 ) {
|
|
|
+
|
|
|
+ console.error( 'THREE.TSL: compute() 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 ) ) {
|
|
|
+
|
|
|
+ console.error( `THREE.TSL: compute() 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 );
|
|
|
+
|
|
|
+ //
|
|
|
+
|
|
|
+ return nodeObject( new ComputeNode( nodeObject( node ), workgroupSize ) );
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* TSL function for creating a compute node.
|
|
|
*
|
|
|
@@ -9057,9 +9170,10 @@ class ComputeNode extends Node {
|
|
|
* @param {Array<number>} [workgroupSize=[64]] - TODO.
|
|
|
* @returns {AtomicFunctionNode}
|
|
|
*/
|
|
|
-const compute = ( node, count, workgroupSize ) => nodeObject( new ComputeNode( nodeObject( node ), count, workgroupSize ) );
|
|
|
+const compute = ( node, count, workgroupSize ) => computeKernel( node, workgroupSize ).setCount( count );
|
|
|
|
|
|
addMethodChaining( 'compute', compute );
|
|
|
+addMethodChaining( 'computeKernel', computeKernel );
|
|
|
|
|
|
/**
|
|
|
* This node can be used as a cache management component for another node.
|
|
|
@@ -16898,7 +17012,7 @@ class ViewportTextureNode extends TextureNode {
|
|
|
/**
|
|
|
* The framebuffer texture for the current renderer context.
|
|
|
*
|
|
|
- * @type {WeakMap}
|
|
|
+ * @type {WeakMap<RenderTarget, FramebufferTexture>}
|
|
|
* @private
|
|
|
*/
|
|
|
this._textures = new WeakMap();
|
|
|
@@ -19026,7 +19140,7 @@ class NodeMaterial extends Material {
|
|
|
|
|
|
output.assign( outputNode );
|
|
|
|
|
|
- outputNode = vec4( fogNode );
|
|
|
+ outputNode = vec4( fogNode.toStack() );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -22157,7 +22271,7 @@ class PhysicalLightingModel extends LightingModel {
|
|
|
* @param {Object} lightData - The light data.
|
|
|
* @param {NodeBuilder} builder - The current node builder.
|
|
|
*/
|
|
|
- direct( { lightDirection, lightColor, reflectedLight } ) {
|
|
|
+ direct( { lightDirection, lightColor, reflectedLight }, /* builder */ ) {
|
|
|
|
|
|
const dotNL = normalView.dot( lightDirection ).clamp();
|
|
|
const irradiance = dotNL.mul( lightColor );
|
|
|
@@ -22190,7 +22304,7 @@ class PhysicalLightingModel extends LightingModel {
|
|
|
* @param {Object} input - The input data.
|
|
|
* @param {NodeBuilder} builder - The current node builder.
|
|
|
*/
|
|
|
- directRectArea( { lightColor, lightPosition, halfWidth, halfHeight, reflectedLight, ltc_1, ltc_2 } ) {
|
|
|
+ directRectArea( { lightColor, lightPosition, halfWidth, halfHeight, reflectedLight, ltc_1, ltc_2 }, /* builder */ ) {
|
|
|
|
|
|
const p0 = lightPosition.add( halfWidth ).sub( halfHeight ); // counterclockwise; light shines in local neg z direction
|
|
|
const p1 = lightPosition.sub( halfWidth ).sub( halfHeight );
|
|
|
@@ -30292,8 +30406,8 @@ class Color4 extends Color {
|
|
|
* string argument to this method.
|
|
|
*
|
|
|
* @param {number|string|Color} r - The red value.
|
|
|
- * @param {number} g - The green value.
|
|
|
- * @param {number} b - The blue value.
|
|
|
+ * @param {number} [g] - The green value.
|
|
|
+ * @param {number} [b] - The blue value.
|
|
|
* @param {number} [a=1] - The alpha value.
|
|
|
* @return {Color4} A reference to this object.
|
|
|
*/
|
|
|
@@ -34235,7 +34349,7 @@ class PassTextureNode extends TextureNode {
|
|
|
|
|
|
setup( builder ) {
|
|
|
|
|
|
- if ( builder.object.isQuadMesh ) this.passNode.build( builder );
|
|
|
+ this.passNode.build( builder );
|
|
|
|
|
|
return super.setup( builder );
|
|
|
|
|
|
@@ -38066,10 +38180,9 @@ class LightsNode extends Node {
|
|
|
if ( light.isSpotLight === true ) {
|
|
|
|
|
|
const hashMap = ( light.map !== null ) ? light.map.id : -1;
|
|
|
- const hashMapVersion = ( light.map !== null ) ? light.map.version : -1;
|
|
|
const hashColorNode = ( light.colorNode ) ? light.colorNode.getCacheKey() : -1;
|
|
|
|
|
|
- _hashData.push( hashMap, hashMapVersion, hashColorNode );
|
|
|
+ _hashData.push( hashMap, hashColorNode );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -42072,6 +42185,7 @@ var TSL = /*#__PURE__*/Object.freeze({
|
|
|
colorSpaceToWorking: colorSpaceToWorking,
|
|
|
colorToDirection: colorToDirection,
|
|
|
compute: compute,
|
|
|
+ computeKernel: computeKernel,
|
|
|
computeSkinning: computeSkinning,
|
|
|
context: context,
|
|
|
convert: convert,
|
|
|
@@ -44234,7 +44348,7 @@ class NodeBuilder {
|
|
|
/**
|
|
|
* A reference to the current fog node.
|
|
|
*
|
|
|
- * @type {?FogNode}
|
|
|
+ * @type {?Node}
|
|
|
* @default null
|
|
|
*/
|
|
|
this.fogNode = null;
|
|
|
@@ -49856,6 +49970,15 @@ class XRManager extends EventDispatcher {
|
|
|
*/
|
|
|
this._supportsLayers = false;
|
|
|
|
|
|
+ /**
|
|
|
+ * Whether the device supports binding gl objects.
|
|
|
+ *
|
|
|
+ * @private
|
|
|
+ * @type {boolean}
|
|
|
+ * @readonly
|
|
|
+ */
|
|
|
+ this._supportsGlBinding = typeof XRWebGLBinding !== 'undefined';
|
|
|
+
|
|
|
this._frameBufferTargets = null;
|
|
|
|
|
|
/**
|
|
|
@@ -50042,7 +50165,7 @@ class XRManager extends EventDispatcher {
|
|
|
* @type {boolean}
|
|
|
* @readonly
|
|
|
*/
|
|
|
- this._useLayers = ( typeof XRWebGLBinding !== 'undefined' && 'createProjectionLayer' in XRWebGLBinding.prototype ); // eslint-disable-line compat/compat
|
|
|
+ this._useLayers = ( this._supportsGlBinding && 'createProjectionLayer' in XRWebGLBinding.prototype ); // eslint-disable-line compat/compat
|
|
|
|
|
|
/**
|
|
|
* Whether the usage of multiview has been requested by the application or not.
|
|
|
@@ -50600,9 +50723,18 @@ class XRManager extends EventDispatcher {
|
|
|
|
|
|
//
|
|
|
|
|
|
+ if ( this._supportsGlBinding ) {
|
|
|
+
|
|
|
+ const glBinding = new XRWebGLBinding( session, gl );
|
|
|
+ this._glBinding = glBinding;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+
|
|
|
if ( this._useLayers === true ) {
|
|
|
|
|
|
- // default path using XRWebGLBinding/XRProjectionLayer
|
|
|
+ // default path using XRProjectionLayer
|
|
|
|
|
|
let depthFormat = null;
|
|
|
let depthType = null;
|
|
|
@@ -50630,11 +50762,9 @@ class XRManager extends EventDispatcher {
|
|
|
|
|
|
}
|
|
|
|
|
|
- const glBinding = new XRWebGLBinding( session, gl );
|
|
|
- const glProjLayer = glBinding.createProjectionLayer( projectionlayerInit );
|
|
|
+ const glProjLayer = this._glBinding.createProjectionLayer( projectionlayerInit );
|
|
|
const layersArray = [ glProjLayer ];
|
|
|
|
|
|
- this._glBinding = glBinding;
|
|
|
this._glProjLayer = glProjLayer;
|
|
|
|
|
|
renderer.setPixelRatio( 1 );
|
|
|
@@ -51321,7 +51451,7 @@ function onAnimationFrame( time, frame ) {
|
|
|
}
|
|
|
|
|
|
const _scene = /*@__PURE__*/ new Scene();
|
|
|
-const _drawingBufferSize$1 = /*@__PURE__*/ new Vector2();
|
|
|
+const _drawingBufferSize = /*@__PURE__*/ new Vector2();
|
|
|
const _screen = /*@__PURE__*/ new Vector4();
|
|
|
const _frustum = /*@__PURE__*/ new Frustum();
|
|
|
const _frustumArray = /*@__PURE__*/ new FrustumArray();
|
|
|
@@ -52503,7 +52633,7 @@ class Renderer {
|
|
|
|
|
|
if ( useToneMapping === false && useColorSpace === false ) return null;
|
|
|
|
|
|
- const { width, height } = this.getDrawingBufferSize( _drawingBufferSize$1 );
|
|
|
+ const { width, height } = this.getDrawingBufferSize( _drawingBufferSize );
|
|
|
const { depth, stencil } = this;
|
|
|
|
|
|
let frameBufferTarget = this._frameBufferTarget;
|
|
|
@@ -52668,9 +52798,9 @@ class Renderer {
|
|
|
|
|
|
}
|
|
|
|
|
|
- this.getDrawingBufferSize( _drawingBufferSize$1 );
|
|
|
+ this.getDrawingBufferSize( _drawingBufferSize );
|
|
|
|
|
|
- _screen.set( 0, 0, _drawingBufferSize$1.width, _drawingBufferSize$1.height );
|
|
|
+ _screen.set( 0, 0, _drawingBufferSize.width, _drawingBufferSize.height );
|
|
|
|
|
|
const minDepth = ( viewport.minDepth === undefined ) ? 0 : viewport.minDepth;
|
|
|
const maxDepth = ( viewport.maxDepth === undefined ) ? 1 : viewport.maxDepth;
|
|
|
@@ -53597,9 +53727,10 @@ class Renderer {
|
|
|
* if the renderer has been initialized.
|
|
|
*
|
|
|
* @param {Node|Array<Node>} computeNodes - The compute node(s).
|
|
|
+ * @param {Array<number>|number} [dispatchSizeOrCount=null] - Array with [ x, y, z ] values for dispatch or a single number for the count.
|
|
|
* @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, dispatchSizeOrCount = null ) {
|
|
|
|
|
|
if ( this._isDeviceLost === true ) return;
|
|
|
|
|
|
@@ -53678,7 +53809,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, dispatchSizeOrCount );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -53695,13 +53826,14 @@ class Renderer {
|
|
|
*
|
|
|
* @async
|
|
|
* @param {Node|Array<Node>} computeNodes - The compute node(s).
|
|
|
+ * @param {Array<number>|number} [dispatchSizeOrCount=null] - Array with [ x, y, z ] values for dispatch or a single number for the count.
|
|
|
* @return {Promise} A Promise that resolve when the compute has finished.
|
|
|
*/
|
|
|
- async computeAsync( computeNodes ) {
|
|
|
+ async computeAsync( computeNodes, dispatchSizeOrCount = null ) {
|
|
|
|
|
|
if ( this._initialized === false ) await this.init();
|
|
|
|
|
|
- this.compute( computeNodes );
|
|
|
+ this.compute( computeNodes, dispatchSizeOrCount );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -54250,7 +54382,7 @@ class Renderer {
|
|
|
* @param {LightsNode} lightsNode - The current lights node.
|
|
|
* @param {?{start: number, count: number}} group - Only relevant for objects using multiple materials. This represents a group entry from the respective `BufferGeometry`.
|
|
|
* @param {ClippingContext} clippingContext - The clipping context.
|
|
|
- * @param {?string} [passId=null] - An optional ID for identifying the pass.
|
|
|
+ * @param {string} [passId] - An optional ID for identifying the pass.
|
|
|
*/
|
|
|
_renderObjectDirect( object, material, scene, camera, lightsNode, group, clippingContext, passId ) {
|
|
|
|
|
|
@@ -54305,7 +54437,7 @@ class Renderer {
|
|
|
* @param {LightsNode} lightsNode - The current lights node.
|
|
|
* @param {?{start: number, count: number}} group - Only relevant for objects using multiple materials. This represents a group entry from the respective `BufferGeometry`.
|
|
|
* @param {ClippingContext} clippingContext - The clipping context.
|
|
|
- * @param {?string} [passId=null] - An optional ID for identifying the pass.
|
|
|
+ * @param {string} [passId] - An optional ID for identifying the pass.
|
|
|
*/
|
|
|
_createObjectPipeline( object, material, scene, camera, lightsNode, group, clippingContext, passId ) {
|
|
|
|
|
|
@@ -61124,8 +61256,6 @@ class WebGLTimestampQueryPool extends TimestampQueryPool {
|
|
|
|
|
|
}
|
|
|
|
|
|
-const _drawingBufferSize = /*@__PURE__*/ new Vector2();
|
|
|
-
|
|
|
/**
|
|
|
* A backend implementation targeting WebGL 2.
|
|
|
*
|
|
|
@@ -61291,7 +61421,7 @@ class WebGLBackend extends Backend {
|
|
|
* A unique collection of bindings.
|
|
|
*
|
|
|
* @private
|
|
|
- * @type {WeakSet}
|
|
|
+ * @type {WeakSet<Array<BindGroup>>}
|
|
|
*/
|
|
|
this._knownBindings = new WeakSet();
|
|
|
|
|
|
@@ -61563,7 +61693,7 @@ class WebGLBackend extends Backend {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- const { width, height } = this.getDrawingBufferSize( _drawingBufferSize );
|
|
|
+ const { width, height } = this.getDrawingBufferSize();
|
|
|
state.viewport( 0, 0, width, height );
|
|
|
|
|
|
}
|
|
|
@@ -61763,7 +61893,7 @@ class WebGLBackend extends Backend {
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- const { width, height } = this.getDrawingBufferSize( _drawingBufferSize );
|
|
|
+ const { width, height } = this.getDrawingBufferSize();
|
|
|
state.viewport( 0, 0, width, height );
|
|
|
|
|
|
}
|
|
|
@@ -62023,8 +62153,9 @@ class WebGLBackend extends Backend {
|
|
|
* @param {Node} computeNode - The compute node.
|
|
|
* @param {Array<BindGroup>} bindings - The bindings.
|
|
|
* @param {ComputePipeline} pipeline - The compute pipeline.
|
|
|
+ * @param {number|null} [count=null] - The count of compute invocations. If `null`, the count is determined by the compute node.
|
|
|
*/
|
|
|
- compute( computeGroup, computeNode, bindings, pipeline ) {
|
|
|
+ compute( computeGroup, computeNode, bindings, pipeline, count = null ) {
|
|
|
|
|
|
const { state, gl } = this;
|
|
|
|
|
|
@@ -62061,13 +62192,23 @@ class WebGLBackend extends Backend {
|
|
|
gl.bindTransformFeedback( gl.TRANSFORM_FEEDBACK, transformFeedbackGPU );
|
|
|
gl.beginTransformFeedback( gl.POINTS );
|
|
|
|
|
|
+ count = ( count !== null ) ? count : computeNode.count;
|
|
|
+
|
|
|
+ if ( Array.isArray( count ) ) {
|
|
|
+
|
|
|
+ warnOnce( 'WebGLBackend.compute(): The count parameter must be a single number, not an array.' );
|
|
|
+
|
|
|
+ count = count[ 0 ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
if ( attributes[ 0 ].isStorageInstancedBufferAttribute ) {
|
|
|
|
|
|
- gl.drawArraysInstanced( gl.POINTS, 0, 1, computeNode.count );
|
|
|
+ gl.drawArraysInstanced( gl.POINTS, 0, 1, count );
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- gl.drawArrays( gl.POINTS, 0, computeNode.count );
|
|
|
+ gl.drawArrays( gl.POINTS, 0, count );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -62682,7 +62823,9 @@ class WebGLBackend extends Backend {
|
|
|
_getShaderErrors( gl, shader, type ) {
|
|
|
|
|
|
const status = gl.getShaderParameter( shader, gl.COMPILE_STATUS );
|
|
|
- const errors = gl.getShaderInfoLog( shader ).trim();
|
|
|
+
|
|
|
+ const shaderInfoLog = gl.getShaderInfoLog( shader ) || '';
|
|
|
+ const errors = shaderInfoLog.trim();
|
|
|
|
|
|
if ( status && errors === '' ) return '';
|
|
|
|
|
|
@@ -62714,11 +62857,11 @@ class WebGLBackend extends Backend {
|
|
|
|
|
|
const gl = this.gl;
|
|
|
|
|
|
- const programLog = gl.getProgramInfoLog( programGPU ).trim();
|
|
|
+ const programInfoLog = gl.getProgramInfoLog( programGPU ) || '';
|
|
|
+ const programLog = programInfoLog.trim();
|
|
|
|
|
|
if ( gl.getProgramParameter( programGPU, gl.LINK_STATUS ) === false ) {
|
|
|
|
|
|
-
|
|
|
if ( typeof this.renderer.debug.onShaderError === 'function' ) {
|
|
|
|
|
|
this.renderer.debug.onShaderError( gl, programGPU, glVertexShader, glFragmentShader );
|
|
|
@@ -68015,7 +68158,11 @@ ${ flowData.code }
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- this.computeShader = this._getWGSLComputeCode( shadersData.compute, ( this.object.workgroupSize || [ 64 ] ).join( ', ' ) );
|
|
|
+ // Early strictly validated in computeNode
|
|
|
+
|
|
|
+ const workgroupSize = this.object.workgroupSize;
|
|
|
+
|
|
|
+ this.computeShader = this._getWGSLComputeCode( shadersData.compute, workgroupSize );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -68220,36 +68367,40 @@ fn main( ${shaderData.varyings} ) -> ${shaderData.returnType} {
|
|
|
*/
|
|
|
_getWGSLComputeCode( shaderData, workgroupSize ) {
|
|
|
|
|
|
+ const [ workgroupSizeX, workgroupSizeY, workgroupSizeZ ] = workgroupSize;
|
|
|
+
|
|
|
return `${ this.getSignature() }
|
|
|
// directives
|
|
|
-${shaderData.directives}
|
|
|
+${ shaderData.directives }
|
|
|
|
|
|
// system
|
|
|
var<private> instanceIndex : u32;
|
|
|
|
|
|
// locals
|
|
|
-${shaderData.scopedArrays}
|
|
|
+${ shaderData.scopedArrays }
|
|
|
|
|
|
// structs
|
|
|
-${shaderData.structs}
|
|
|
+${ shaderData.structs }
|
|
|
|
|
|
// uniforms
|
|
|
-${shaderData.uniforms}
|
|
|
+${ shaderData.uniforms }
|
|
|
|
|
|
// codes
|
|
|
-${shaderData.codes}
|
|
|
+${ shaderData.codes }
|
|
|
|
|
|
-@compute @workgroup_size( ${workgroupSize} )
|
|
|
-fn main( ${shaderData.attributes} ) {
|
|
|
+@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}
|
|
|
+ ${ shaderData.vars }
|
|
|
|
|
|
// flow
|
|
|
- ${shaderData.flow}
|
|
|
+ ${ shaderData.flow }
|
|
|
|
|
|
}
|
|
|
`;
|
|
|
@@ -70134,7 +70285,7 @@ class WebGPUPipelineUtils {
|
|
|
*
|
|
|
* @private
|
|
|
* @param {Material} material - The material.
|
|
|
- * @return {string} The GPU color write mask.
|
|
|
+ * @return {number} The GPU color write mask.
|
|
|
*/
|
|
|
_getColorWriteMask( material ) {
|
|
|
|
|
|
@@ -71777,7 +71928,6 @@ class WebGPUBackend extends Backend {
|
|
|
|
|
|
const groupGPU = this.get( computeGroup );
|
|
|
|
|
|
-
|
|
|
const descriptor = {
|
|
|
label: 'computeGroup_' + computeGroup.id
|
|
|
};
|
|
|
@@ -71797,9 +71947,11 @@ class WebGPUBackend extends Backend {
|
|
|
* @param {Node} computeNode - The compute node.
|
|
|
* @param {Array<BindGroup>} bindings - The bindings.
|
|
|
* @param {ComputePipeline} pipeline - The compute pipeline.
|
|
|
+ * @param {Array<number>|number} [dispatchSizeOrCount=null] - Array with [ x, y, z ] values for dispatch or a single number for the count.
|
|
|
*/
|
|
|
- compute( computeGroup, computeNode, bindings, pipeline ) {
|
|
|
+ compute( computeGroup, computeNode, bindings, pipeline, dispatchSizeOrCount = null ) {
|
|
|
|
|
|
+ const computeNodeData = this.get( computeNode );
|
|
|
const { passEncoderGPU } = this.get( computeGroup );
|
|
|
|
|
|
// pipeline
|
|
|
@@ -71819,29 +71971,67 @@ class WebGPUBackend extends Backend {
|
|
|
|
|
|
}
|
|
|
|
|
|
- const maxComputeWorkgroupsPerDimension = this.device.limits.maxComputeWorkgroupsPerDimension;
|
|
|
+ let dispatchSize;
|
|
|
|
|
|
- const computeNodeData = this.get( computeNode );
|
|
|
+ if ( dispatchSizeOrCount === null ) {
|
|
|
+
|
|
|
+ dispatchSizeOrCount = computeNode.count;
|
|
|
|
|
|
- if ( computeNodeData.dispatchSize === undefined ) computeNodeData.dispatchSize = { x: 0, y: 1, z: 1 };
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( typeof dispatchSizeOrCount === 'number' ) {
|
|
|
+
|
|
|
+ // If a single number is given, we calculate the dispatch size based on the workgroup size
|
|
|
+
|
|
|
+ const count = dispatchSizeOrCount;
|
|
|
+
|
|
|
+ if ( computeNodeData.dispatchSize === undefined || computeNodeData.count !== count ) {
|
|
|
+
|
|
|
+ // cache dispatch size to avoid recalculating it every time
|
|
|
+
|
|
|
+ computeNodeData.dispatchSize = [ 0, 1, 1 ];
|
|
|
+ computeNodeData.count = count;
|
|
|
+
|
|
|
+ const workgroupSize = computeNode.workgroupSize;
|
|
|
+
|
|
|
+ let size = workgroupSize[ 0 ];
|
|
|
+
|
|
|
+ for ( let i = 1; i < workgroupSize.length; i ++ )
|
|
|
+ size *= workgroupSize[ i ];
|
|
|
+
|
|
|
+ const dispatchCount = Math.ceil( count / size );
|
|
|
|
|
|
- const { dispatchSize } = computeNodeData;
|
|
|
+ //
|
|
|
+
|
|
|
+ const maxComputeWorkgroupsPerDimension = this.device.limits.maxComputeWorkgroupsPerDimension;
|
|
|
+
|
|
|
+ dispatchSize = [ dispatchCount, 1, 1 ];
|
|
|
|
|
|
- if ( computeNode.dispatchCount > maxComputeWorkgroupsPerDimension ) {
|
|
|
+ if ( dispatchCount > maxComputeWorkgroupsPerDimension ) {
|
|
|
+
|
|
|
+ dispatchSize[ 0 ] = Math.min( dispatchCount, maxComputeWorkgroupsPerDimension );
|
|
|
+ dispatchSize[ 1 ] = Math.ceil( dispatchCount / maxComputeWorkgroupsPerDimension );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
- dispatchSize.x = Math.min( computeNode.dispatchCount, maxComputeWorkgroupsPerDimension );
|
|
|
- dispatchSize.y = Math.ceil( computeNode.dispatchCount / maxComputeWorkgroupsPerDimension );
|
|
|
+ computeNodeData.dispatchSize = dispatchSize;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ dispatchSize = computeNodeData.dispatchSize;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
- dispatchSize.x = computeNode.dispatchCount;
|
|
|
+ dispatchSize = dispatchSizeOrCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
+ //
|
|
|
+
|
|
|
passEncoderGPU.dispatchWorkgroups(
|
|
|
- dispatchSize.x,
|
|
|
- dispatchSize.y,
|
|
|
- dispatchSize.z
|
|
|
+ dispatchSize[ 0 ],
|
|
|
+ dispatchSize[ 1 ] || 1,
|
|
|
+ dispatchSize[ 2 ] || 1
|
|
|
);
|
|
|
|
|
|
}
|