Sfoglia il codice sorgente

WebGPUBindingUtils: Improve Bind Group Layout cache system. (#32451)

Michael Herzog 1 mese fa
parent
commit
1917b3e52d

+ 8 - 0
src/renderers/common/Backend.js

@@ -736,6 +736,14 @@ class Backend {
 
 	}
 
+	/**
+	 * Delete GPU data associated with a bind group.
+	 *
+	 * @abstract
+	 * @param {BindGroup} bindGroup - The bind group.
+	 */
+	deleteBindGroupData( /*bindGroup*/ ) { }
+
 	/**
 	 * Frees internal resources.
 	 *

+ 2 - 0
src/renderers/common/Bindings.js

@@ -164,6 +164,7 @@ class Bindings extends DataMap {
 
 		for ( const bindGroup of bindings ) {
 
+			this.backend.deleteBindGroupData( bindGroup );
 			this.delete( bindGroup );
 
 		}
@@ -181,6 +182,7 @@ class Bindings extends DataMap {
 
 		for ( const bindGroup of bindings ) {
 
+			this.backend.deleteBindGroupData( bindGroup );
 			this.delete( bindGroup );
 
 		}

+ 15 - 1
src/renderers/webgpu/WebGPUBackend.js

@@ -1667,7 +1667,9 @@ class WebGPUBackend extends Backend {
 
 					data[ 0 ] = i;
 
-					const bindGroupIndex = this.bindingUtils.createBindGroupIndex( data, bindingsData.layout );
+					const { layoutGPU } = bindingsData.layout;
+
+					const bindGroupIndex = this.bindingUtils.createBindGroupIndex( data, layoutGPU );
 
 					indexesGPU.push( bindGroupIndex );
 
@@ -2159,6 +2161,17 @@ class WebGPUBackend extends Backend {
 
 	}
 
+	/**
+	 * Delete data associated with the current bind group.
+	 *
+	 * @param {BindGroup} bindGroup - The bind group.
+	 */
+	deleteBindGroupData( bindGroup ) {
+
+		this.bindingUtils.deleteBindGroupData( bindGroup );
+
+	}
+
 	// attributes
 
 	/**
@@ -2488,6 +2501,7 @@ class WebGPUBackend extends Backend {
 
 	dispose() {
 
+		this.bindingUtils.dispose();
 		this.textureUtils.dispose();
 
 		if ( this.occludedResolveCache ) {

+ 290 - 180
src/renderers/webgpu/utils/WebGPUBindingUtils.js

@@ -7,6 +7,38 @@ import { FloatType, IntType, UnsignedIntType } from '../../../constants.js';
 import { NodeAccess } from '../../../nodes/core/constants.js';
 import { isTypedArray, error } from '../../../utils.js';
 
+/**
+ * Class representing a WebGPU bind group layout.
+ *
+ * @private
+ */
+class BindGroupLayout {
+
+	/**
+	 * Constructs a new layout.
+	 *
+	 * @param {GPUBindGroupLayout} layoutGPU - A GPU Bind Group Layout.
+	 */
+	constructor( layoutGPU ) {
+
+		/**
+		 * The current GPUBindGroupLayout.
+		 *
+		 * @type {GPUBindGroupLayout}
+		 */
+		this.layoutGPU = layoutGPU;
+
+		/**
+		 * The number of bind groups that use this layout.
+		 *
+		 * @type {number}
+		 */
+		this.usedTimes = 0;
+
+	}
+
+}
+
 /**
  * A WebGPU backend utility module for managing bindings.
  *
@@ -34,11 +66,12 @@ class WebGPUBindingUtils {
 		this.backend = backend;
 
 		/**
-		 * A cache for managing bind group layouts.
+		 * A cache that maps combinations of layout entries to existing bind group layouts.
 		 *
-		 * @type {WeakMap<Array<Binding>,GPUBindGroupLayout>}
+		 * @private
+		 * @type {Map<string, BindGroupLayout>}
 		 */
-		this.bindGroupLayoutCache = new WeakMap();
+		this._bindGroupLayoutCache = new Map();
 
 	}
 
@@ -53,185 +86,40 @@ class WebGPUBindingUtils {
 		const backend = this.backend;
 		const device = backend.device;
 
-		const entries = [];
-
-		let index = 0;
-
-		for ( const binding of bindGroup.bindings ) {
-
-			const bindingGPU = {
-				binding: index ++,
-				visibility: binding.visibility
-			};
-
-			if ( binding.isUniformBuffer || binding.isStorageBuffer ) {
-
-				const buffer = {}; // GPUBufferBindingLayout
-
-				if ( binding.isStorageBuffer ) {
-
-					if ( binding.visibility & GPUShaderStage.COMPUTE ) {
-
-						// compute
-
-						if ( binding.access === NodeAccess.READ_WRITE || binding.access === NodeAccess.WRITE_ONLY ) {
-
-							buffer.type = GPUBufferBindingType.Storage;
-
-						} else {
-
-							buffer.type = GPUBufferBindingType.ReadOnlyStorage;
-
-						}
-
-					} else {
-
-						buffer.type = GPUBufferBindingType.ReadOnlyStorage;
-
-					}
-
-				}
-
-				bindingGPU.buffer = buffer;
-
-			} else if ( binding.isSampledTexture && binding.store ) {
-
-				const storageTexture = {}; // GPUStorageTextureBindingLayout
-				storageTexture.format = this.backend.get( binding.texture ).texture.format;
-
-				const access = binding.access;
-
-				if ( access === NodeAccess.READ_WRITE ) {
-
-					storageTexture.access = GPUStorageTextureAccess.ReadWrite;
-
-				} else if ( access === NodeAccess.WRITE_ONLY ) {
-
-					storageTexture.access = GPUStorageTextureAccess.WriteOnly;
-
-				} else {
-
-					storageTexture.access = GPUStorageTextureAccess.ReadOnly;
-
-				}
-
-				if ( binding.texture.isArrayTexture ) {
-
-					storageTexture.viewDimension = GPUTextureViewDimension.TwoDArray;
-
-				} else if ( binding.texture.is3DTexture ) {
-
-					storageTexture.viewDimension = GPUTextureViewDimension.ThreeD;
-
-				}
-
-				bindingGPU.storageTexture = storageTexture;
-
-			} else if ( binding.isSampledTexture ) {
-
-				const texture = {}; // GPUTextureBindingLayout
-
-				const { primarySamples } = backend.utils.getTextureSampleData( binding.texture );
-
-				if ( primarySamples > 1 ) {
-
-					texture.multisampled = true;
-
-					if ( ! binding.texture.isDepthTexture ) {
-
-						texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
-
-					}
-
-				}
-
-				if ( binding.texture.isDepthTexture ) {
-
-					if ( backend.compatibilityMode && binding.texture.compareFunction === null ) {
-
-						texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
-
-					} else {
-
-						texture.sampleType = GPUTextureSampleType.Depth;
-
-					}
-
-				} else if ( binding.texture.isDataTexture || binding.texture.isDataArrayTexture || binding.texture.isData3DTexture ) {
-
-					const type = binding.texture.type;
-
-					if ( type === IntType ) {
-
-						texture.sampleType = GPUTextureSampleType.SInt;
-
-					} else if ( type === UnsignedIntType ) {
-
-						texture.sampleType = GPUTextureSampleType.UInt;
-
-					} else if ( type === FloatType ) {
-
-						if ( this.backend.hasFeature( 'float32-filterable' ) ) {
-
-							texture.sampleType = GPUTextureSampleType.Float;
-
-						} else {
-
-							texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
-
-						}
-
-					}
-
-				}
-
-				if ( binding.isSampledCubeTexture ) {
-
-					texture.viewDimension = GPUTextureViewDimension.Cube;
-
-				} else if ( binding.texture.isArrayTexture || binding.texture.isDataArrayTexture || binding.texture.isCompressedArrayTexture ) {
-
-					texture.viewDimension = GPUTextureViewDimension.TwoDArray;
-
-				} else if ( binding.isSampledTexture3D ) {
-
-					texture.viewDimension = GPUTextureViewDimension.ThreeD;
-
-				}
-
-				bindingGPU.texture = texture;
-
-			} else if ( binding.isSampler ) {
+		const bindingsData = backend.get( bindGroup );
 
-				const sampler = {}; // GPUSamplerBindingLayout
+		// check if the the bind group already has a layout
 
-				if ( binding.texture.isDepthTexture ) {
+		if ( bindingsData.layout ) {
 
-					if ( binding.texture.compareFunction !== null ) {
+			return bindingsData.layout.layoutGPU;
 
-						sampler.type = GPUSamplerBindingType.Comparison;
+		}
 
-					} else if ( backend.compatibilityMode ) {
+		// if not, assing one
 
-						sampler.type = GPUSamplerBindingType.NonFiltering;
+		const entries = this._createLayoutEntries( bindGroup );
+		const bindGroupLayoutKey = JSON.stringify( entries );
 
-					}
+		// try to find an existing layout in the cache
 
-				}
+		let bindGroupLayout = this._bindGroupLayoutCache.get( bindGroupLayoutKey );
 
-				bindingGPU.sampler = sampler;
+		// if not create a new one
 
-			} else {
+		if ( bindGroupLayout === undefined ) {
 
-				error( `WebGPUBindingUtils: Unsupported binding "${ binding }".` );
+			bindGroupLayout = new BindGroupLayout( device.createBindGroupLayout( { entries } ) );
+			this._bindGroupLayoutCache.set( bindGroupLayoutKey, bindGroupLayout );
 
-			}
+		}
 
-			entries.push( bindingGPU );
+		bindGroupLayout.usedTimes ++;
 
-		}
+		bindingsData.layout = bindGroupLayout;
+		bindingsData.layoutKey = bindGroupLayoutKey;
 
-		return device.createBindGroupLayout( { entries } );
+		return bindGroupLayout.layoutGPU;
 
 	}
 
@@ -245,19 +133,12 @@ class WebGPUBindingUtils {
 	 */
 	createBindings( bindGroup, bindings, cacheIndex, version = 0 ) {
 
-		const { backend, bindGroupLayoutCache } = this;
+		const { backend } = this;
 		const bindingsData = backend.get( bindGroup );
 
 		// setup (static) binding layout and (dynamic) binding group
 
-		let bindLayoutGPU = bindGroupLayoutCache.get( bindGroup.bindingsReference );
-
-		if ( bindLayoutGPU === undefined ) {
-
-			bindLayoutGPU = this.createBindingsLayout( bindGroup );
-			bindGroupLayoutCache.set( bindGroup.bindingsReference, bindLayoutGPU );
-
-		}
+		const bindLayoutGPU = this.createBindingsLayout( bindGroup );
 
 		let bindGroupGPU;
 
@@ -292,7 +173,6 @@ class WebGPUBindingUtils {
 		}
 
 		bindingsData.group = bindGroupGPU;
-		bindingsData.layout = bindLayoutGPU;
 
 	}
 
@@ -354,10 +234,10 @@ class WebGPUBindingUtils {
 	 * Creates a GPU bind group for the camera index.
 	 *
 	 * @param {Uint32Array} data - The index data.
-	 * @param {GPUBindGroupLayout} layout - The GPU bind group layout.
+	 * @param {GPUBindGroupLayout} layoutGPU - The GPU bind group layout.
 	 * @return {GPUBindGroup} The GPU bind group.
 	 */
-	createBindGroupIndex( data, layout ) {
+	createBindGroupIndex( data, layoutGPU ) {
 
 		const backend = this.backend;
 		const device = backend.device;
@@ -377,7 +257,7 @@ class WebGPUBindingUtils {
 
 		return device.createBindGroup( {
 			label: 'bindGroupCameraIndex_' + index,
-			layout,
+			layout: layoutGPU,
 			entries
 		} );
 
@@ -538,6 +418,236 @@ class WebGPUBindingUtils {
 
 	}
 
+	/**
+	 * Creates a GPU bind group layout entries for the given bind group.
+	 *
+	 * @private
+	 * @param {BindGroup} bindGroup - The bind group.
+	 * @return {Array<GPUBindGroupLayoutEntry>} The GPU bind group layout entries.
+	 */
+	_createLayoutEntries( bindGroup ) {
+
+		const entries = [];
+		let index = 0;
+
+		for ( const binding of bindGroup.bindings ) {
+
+			const backend = this.backend;
+
+			const bindingGPU = {
+				binding: index,
+				visibility: binding.visibility
+			};
+
+			if ( binding.isUniformBuffer || binding.isStorageBuffer ) {
+
+				const buffer = {}; // GPUBufferBindingLayout
+
+				if ( binding.isStorageBuffer ) {
+
+					if ( binding.visibility & GPUShaderStage.COMPUTE ) {
+
+						// compute
+
+						if ( binding.access === NodeAccess.READ_WRITE || binding.access === NodeAccess.WRITE_ONLY ) {
+
+							buffer.type = GPUBufferBindingType.Storage;
+
+						} else {
+
+							buffer.type = GPUBufferBindingType.ReadOnlyStorage;
+
+						}
+
+					} else {
+
+						buffer.type = GPUBufferBindingType.ReadOnlyStorage;
+
+					}
+
+				}
+
+				bindingGPU.buffer = buffer;
+
+			} else if ( binding.isSampledTexture && binding.store ) {
+
+				const storageTexture = {}; // GPUStorageTextureBindingLayout
+				storageTexture.format = this.backend.get( binding.texture ).texture.format;
+
+				const access = binding.access;
+
+				if ( access === NodeAccess.READ_WRITE ) {
+
+					storageTexture.access = GPUStorageTextureAccess.ReadWrite;
+
+				} else if ( access === NodeAccess.WRITE_ONLY ) {
+
+					storageTexture.access = GPUStorageTextureAccess.WriteOnly;
+
+				} else {
+
+					storageTexture.access = GPUStorageTextureAccess.ReadOnly;
+
+				}
+
+				if ( binding.texture.isArrayTexture ) {
+
+					storageTexture.viewDimension = GPUTextureViewDimension.TwoDArray;
+
+				} else if ( binding.texture.is3DTexture ) {
+
+					storageTexture.viewDimension = GPUTextureViewDimension.ThreeD;
+
+				}
+
+				bindingGPU.storageTexture = storageTexture;
+
+			} else if ( binding.isSampledTexture ) {
+
+				const texture = {}; // GPUTextureBindingLayout
+
+				const { primarySamples } = backend.utils.getTextureSampleData( binding.texture );
+
+				if ( primarySamples > 1 ) {
+
+					texture.multisampled = true;
+
+					if ( ! binding.texture.isDepthTexture ) {
+
+						texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
+
+					}
+
+				}
+
+				if ( binding.texture.isDepthTexture ) {
+
+					if ( backend.compatibilityMode && binding.texture.compareFunction === null ) {
+
+						texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
+
+					} else {
+
+						texture.sampleType = GPUTextureSampleType.Depth;
+
+					}
+
+				} else if ( binding.texture.isDataTexture || binding.texture.isDataArrayTexture || binding.texture.isData3DTexture ) {
+
+					const type = binding.texture.type;
+
+					if ( type === IntType ) {
+
+						texture.sampleType = GPUTextureSampleType.SInt;
+
+					} else if ( type === UnsignedIntType ) {
+
+						texture.sampleType = GPUTextureSampleType.UInt;
+
+					} else if ( type === FloatType ) {
+
+						if ( this.backend.hasFeature( 'float32-filterable' ) ) {
+
+							texture.sampleType = GPUTextureSampleType.Float;
+
+						} else {
+
+							texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
+
+						}
+
+					}
+
+				}
+
+				if ( binding.isSampledCubeTexture ) {
+
+					texture.viewDimension = GPUTextureViewDimension.Cube;
+
+				} else if ( binding.texture.isArrayTexture || binding.texture.isDataArrayTexture || binding.texture.isCompressedArrayTexture ) {
+
+					texture.viewDimension = GPUTextureViewDimension.TwoDArray;
+
+				} else if ( binding.isSampledTexture3D ) {
+
+					texture.viewDimension = GPUTextureViewDimension.ThreeD;
+
+				}
+
+				bindingGPU.texture = texture;
+
+			} else if ( binding.isSampler ) {
+
+				const sampler = {}; // GPUSamplerBindingLayout
+
+				if ( binding.texture.isDepthTexture ) {
+
+					if ( binding.texture.compareFunction !== null ) {
+
+						sampler.type = GPUSamplerBindingType.Comparison;
+
+					} else if ( backend.compatibilityMode ) {
+
+						sampler.type = GPUSamplerBindingType.NonFiltering;
+
+					}
+
+				}
+
+				bindingGPU.sampler = sampler;
+
+			} else {
+
+				error( `WebGPUBindingUtils: Unsupported binding "${ binding }".` );
+
+			}
+
+			entries.push( bindingGPU );
+			index ++;
+
+		}
+
+		return entries;
+
+	}
+
+	/**
+	 * Delete the data associated with a bind group.
+	 *
+	 * @param {BindGroup} bindGroup - The bind group.
+	 */
+	deleteBindGroupData( bindGroup ) {
+
+		const { backend } = this;
+
+		const bindingsData = backend.get( bindGroup );
+
+		if ( bindingsData.layout ) {
+
+			bindingsData.layout.usedTimes --;
+
+			if ( bindingsData.layout.usedTimes === 0 ) {
+
+				this._bindGroupLayoutCache.delete( bindingsData.layoutKey );
+
+			}
+
+			bindingsData.layout = undefined;
+			bindingsData.layoutKey = undefined;
+
+		}
+
+	}
+
+	/**
+	 * Frees internal resources.
+	 */
+	dispose() {
+
+		this._bindGroupLayoutCache.clear();
+
+	}
+
 }
 
 export default WebGPUBindingUtils;

+ 4 - 2
src/renderers/webgpu/utils/WebGPUPipelineUtils.js

@@ -106,8 +106,9 @@ class WebGPUPipelineUtils {
 		for ( const bindGroup of renderObject.getBindings() ) {
 
 			const bindingsData = backend.get( bindGroup );
+			const { layoutGPU } = bindingsData.layout;
 
-			bindGroupLayouts.push( bindingsData.layout );
+			bindGroupLayouts.push( layoutGPU );
 
 		}
 
@@ -341,8 +342,9 @@ class WebGPUPipelineUtils {
 		for ( const bindingsGroup of bindings ) {
 
 			const bindingsData = backend.get( bindingsGroup );
+			const { layoutGPU } = bindingsData.layout;
 
-			bindGroupLayouts.push( bindingsData.layout );
+			bindGroupLayouts.push( layoutGPU );
 
 		}
 

粤ICP备19079148号