Просмотр исходного кода

WebGPURenderer: Decouple samplers from textures. (#31899)

Michael Herzog 4 месяцев назад
Родитель
Сommit
56aa339d03

+ 4 - 11
src/renderers/common/Backend.js

@@ -273,20 +273,13 @@ class Backend {
 	// textures
 	// textures
 
 
 	/**
 	/**
-	 * Creates a GPU sampler for the given texture.
+	 * Updates a GPU sampler for the given texture.
 	 *
 	 *
 	 * @abstract
 	 * @abstract
-	 * @param {Texture} texture - The texture to create the sampler for.
+	 * @param {Texture} texture - The texture to update the sampler for.
+	 * @return {string} The current sampler key.
 	 */
 	 */
-	createSampler( /*texture*/ ) { }
-
-	/**
-	 * Destroys the GPU sampler for the given texture.
-	 *
-	 * @abstract
-	 * @param {Texture} texture - The texture to destroy the sampler for.
-	 */
-	destroySampler( /*texture*/ ) {}
+	updateSampler( /*texture*/ ) { }
 
 
 	/**
 	/**
 	 * Creates a default texture for the given texture that can be used
 	 * Creates a default texture for the given texture that can be used

+ 21 - 1
src/renderers/common/Bindings.js

@@ -215,6 +215,10 @@ class Bindings extends DataMap {
 
 
 				this.textures.updateTexture( binding.texture );
 				this.textures.updateTexture( binding.texture );
 
 
+			} else if ( binding.isSampler ) {
+
+				this.textures.updateSampler( binding.texture );
+
 			} else if ( binding.isStorageBuffer ) {
 			} else if ( binding.isStorageBuffer ) {
 
 
 				const attribute = binding.attribute;
 				const attribute = binding.attribute;
@@ -340,7 +344,23 @@ class Bindings extends DataMap {
 
 
 			} else if ( binding.isSampler ) {
 			} else if ( binding.isSampler ) {
 
 
-				binding.update();
+				const updated = binding.update();
+
+				if ( updated ) {
+
+					const samplerKey = this.textures.updateSampler( binding.texture );
+
+					if ( binding.samplerKey !== samplerKey ) {
+
+						binding.samplerKey = samplerKey;
+
+						needsBindingsUpdate = true;
+
+						cacheBindings = false;
+
+					}
+
+				}
 
 
 			}
 			}
 
 

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

@@ -58,6 +58,14 @@ class Sampler extends Binding {
 		 */
 		 */
 		this.generation = null;
 		this.generation = null;
 
 
+		/**
+		 * The binding's sampler key.
+		 *
+		 * @type {string}
+		 * @default ''
+		 */
+		this.samplerKey = '';
+
 		/**
 		/**
 		 * This flag can be used for type testing.
 		 * This flag can be used for type testing.
 		 *
 		 *

+ 18 - 8
src/renderers/common/Textures.js

@@ -210,7 +210,6 @@ class Textures extends DataMap {
 
 
 			// it's an update
 			// it's an update
 
 
-			backend.destroySampler( texture );
 			backend.destroyTexture( texture );
 			backend.destroyTexture( texture );
 
 
 		}
 		}
@@ -253,17 +252,12 @@ class Textures extends DataMap {
 
 
 		if ( isRenderTarget || texture.isStorageTexture === true || texture.isExternalTexture === true ) {
 		if ( isRenderTarget || texture.isStorageTexture === true || texture.isExternalTexture === true ) {
 
 
-			backend.createSampler( texture );
 			backend.createTexture( texture, options );
 			backend.createTexture( texture, options );
 
 
 			textureData.generation = texture.version;
 			textureData.generation = texture.version;
 
 
 		} else {
 		} else {
 
 
-			const needsCreate = textureData.initialized !== true;
-
-			if ( needsCreate ) backend.createSampler( texture );
-
 			if ( texture.version > 0 ) {
 			if ( texture.version > 0 ) {
 
 
 				const image = texture.image;
 				const image = texture.image;
@@ -365,6 +359,24 @@ class Textures extends DataMap {
 
 
 	}
 	}
 
 
+	/**
+	 * Updates the sampler for the given texture. This method has no effect
+	 * for the WebGL backend since it has no concept of samplers. Texture
+	 * parameters are configured with the `texParameter()` command for each
+	 * texture.
+	 *
+	 * In WebGPU, samplers are objects like textures and it's possible to share
+	 * them when the texture parameters match.
+	 *
+	 * @param {Texture} texture - The texture to update the sampler for.
+	 * @return {string} The current sampler key.
+	 */
+	updateSampler( texture ) {
+
+		return this.backend.updateSampler( texture );
+
+	}
+
 	/**
 	/**
 	 * Computes the size of the given texture and writes the result
 	 * Computes the size of the given texture and writes the result
 	 * into the target vector. This vector is also returned by the
 	 * into the target vector. This vector is also returned by the
@@ -481,8 +493,6 @@ class Textures extends DataMap {
 			// other textures.
 			// other textures.
 
 
 			const isDefaultTexture = this.get( texture ).isDefaultTexture;
 			const isDefaultTexture = this.get( texture ).isDefaultTexture;
-
-			this.backend.destroySampler( texture );
 			this.backend.destroyTexture( texture, isDefaultTexture );
 			this.backend.destroyTexture( texture, isDefaultTexture );
 
 
 			this.delete( texture );
 			this.delete( texture );

+ 4 - 0
src/renderers/common/nodes/NodeSampler.js

@@ -38,11 +38,15 @@ class NodeSampler extends Sampler {
 
 
 	/**
 	/**
 	 * Updates the texture value of this sampler.
 	 * Updates the texture value of this sampler.
+	 *
+	 * @return {boolean} Whether the sampler needs an update or not.
 	 */
 	 */
 	update() {
 	update() {
 
 
 		this.texture = this.textureNode.value;
 		this.texture = this.textureNode.value;
 
 
+		return super.update();
+
 	}
 	}
 
 
 }
 }

+ 4 - 10
src/renderers/webgl-fallback/WebGLBackend.js

@@ -1331,21 +1331,15 @@ class WebGLBackend extends Backend {
 	/**
 	/**
 	 * This method does nothing since WebGL 2 has no concept of samplers.
 	 * This method does nothing since WebGL 2 has no concept of samplers.
 	 *
 	 *
-	 * @param {Texture} texture - The texture to create the sampler for.
+	 * @param {Texture} texture - The texture to update the sampler for.
+	 * @return {string} The current sampler key.
 	 */
 	 */
-	createSampler( /*texture*/ ) {
+	updateSampler( /*texture*/ ) {
 
 
-		//warn( 'Abstract class.' );
+		return '';
 
 
 	}
 	}
 
 
-	/**
-	 * This method does nothing since WebGL 2 has no concept of samplers.
-	 *
-	 * @param {Texture} texture - The texture to destroy the sampler for.
-	 */
-	destroySampler( /*texture*/ ) {}
-
 	// node builder
 	// node builder
 
 
 	/**
 	/**

+ 13 - 16
src/renderers/webgpu/WebGPUBackend.js

@@ -1834,24 +1834,14 @@ class WebGPUBackend extends Backend {
 	// textures
 	// textures
 
 
 	/**
 	/**
-	 * Creates a GPU sampler for the given texture.
+	 * Updates a GPU sampler for the given texture.
 	 *
 	 *
-	 * @param {Texture} texture - The texture to create the sampler for.
+	 * @param {Texture} texture - The texture to update the sampler for.
+	 * @return {string} The current sampler key.
 	 */
 	 */
-	createSampler( texture ) {
+	updateSampler( texture ) {
 
 
-		this.textureUtils.createSampler( texture );
-
-	}
-
-	/**
-	 * Destroys the GPU sampler for the given texture.
-	 *
-	 * @param {Texture} texture - The texture to destroy the sampler for.
-	 */
-	destroySampler( texture ) {
-
-		this.textureUtils.destroySampler( texture );
+		return this.textureUtils.updateSampler( texture );
 
 
 	}
 	}
 
 
@@ -1860,10 +1850,11 @@ class WebGPUBackend extends Backend {
 	 * as a placeholder until the actual texture is ready for usage.
 	 * as a placeholder until the actual texture is ready for usage.
 	 *
 	 *
 	 * @param {Texture} texture - The texture to create a default texture for.
 	 * @param {Texture} texture - The texture to create a default texture for.
+	 * @return {boolean} Whether the sampler has been updated or not.
 	 */
 	 */
 	createDefaultTexture( texture ) {
 	createDefaultTexture( texture ) {
 
 
-		this.textureUtils.createDefaultTexture( texture );
+		return this.textureUtils.createDefaultTexture( texture );
 
 
 	}
 	}
 
 
@@ -2452,6 +2443,12 @@ class WebGPUBackend extends Backend {
 
 
 	}
 	}
 
 
+	dispose() {
+
+		this.textureUtils.dispose();
+
+	}
+
 }
 }
 
 
 export default WebGPUBackend;
 export default WebGPUBackend;

+ 78 - 34
src/renderers/webgpu/utils/WebGPUTextureUtils.js

@@ -103,45 +103,94 @@ class WebGPUTextureUtils {
 		this.depthTexture = new DepthTexture();
 		this.depthTexture = new DepthTexture();
 		this.depthTexture.name = 'depthBuffer';
 		this.depthTexture.name = 'depthBuffer';
 
 
+		/**
+		 * A cache of shared texture samplers.
+		 *
+		 * @type {Map<string, Object>}
+		 */
+		this._samplerCache = new Map();
+
 	}
 	}
 
 
 	/**
 	/**
 	 * Creates a GPU sampler for the given texture.
 	 * Creates a GPU sampler for the given texture.
 	 *
 	 *
 	 * @param {Texture} texture - The texture to create the sampler for.
 	 * @param {Texture} texture - The texture to create the sampler for.
+	 * @return {string} The current sampler key.
 	 */
 	 */
-	createSampler( texture ) {
+	updateSampler( texture ) {
 
 
 		const backend = this.backend;
 		const backend = this.backend;
-		const device = backend.device;
-
-		const textureGPU = backend.get( texture );
-
-		const samplerDescriptorGPU = {
-			addressModeU: this._convertAddressMode( texture.wrapS ),
-			addressModeV: this._convertAddressMode( texture.wrapT ),
-			addressModeW: this._convertAddressMode( texture.wrapR ),
-			magFilter: this._convertFilterMode( texture.magFilter ),
-			minFilter: this._convertFilterMode( texture.minFilter ),
-			mipmapFilter: this._convertFilterMode( texture.minFilter ),
-			maxAnisotropy: 1
-		};
 
 
-		// anisotropy can only be used when all filter modes are set to linear.
+		const samplerKey = texture.minFilter + '-' + texture.magFilter + '-' +
+			texture.wrapS + '-' + texture.wrapT + '-' + ( texture.wrapR || '0' ) + '-' +
+			texture.anisotropy + '-' + ( texture.compareFunction || 0 );
+
+		let samplerData = this._samplerCache.get( samplerKey );
+
+		if ( samplerData === undefined ) {
+
+			const samplerDescriptorGPU = {
+				addressModeU: this._convertAddressMode( texture.wrapS ),
+				addressModeV: this._convertAddressMode( texture.wrapT ),
+				addressModeW: this._convertAddressMode( texture.wrapR ),
+				magFilter: this._convertFilterMode( texture.magFilter ),
+				minFilter: this._convertFilterMode( texture.minFilter ),
+				mipmapFilter: this._convertFilterMode( texture.minFilter ),
+				maxAnisotropy: 1
+			};
+
+			// anisotropy can only be used when all filter modes are set to linear.
+
+			if ( samplerDescriptorGPU.magFilter === GPUFilterMode.Linear && samplerDescriptorGPU.minFilter === GPUFilterMode.Linear && samplerDescriptorGPU.mipmapFilter === GPUFilterMode.Linear ) {
+
+				samplerDescriptorGPU.maxAnisotropy = texture.anisotropy;
+
+			}
+
+			if ( texture.isDepthTexture && texture.compareFunction !== null ) {
+
+				samplerDescriptorGPU.compare = _compareToWebGPU[ texture.compareFunction ];
+
+			}
+
+			const sampler = backend.device.createSampler( samplerDescriptorGPU );
 
 
-		if ( samplerDescriptorGPU.magFilter === GPUFilterMode.Linear && samplerDescriptorGPU.minFilter === GPUFilterMode.Linear && samplerDescriptorGPU.mipmapFilter === GPUFilterMode.Linear ) {
+			samplerData = { sampler, usedTimes: 0 };
 
 
-			samplerDescriptorGPU.maxAnisotropy = texture.anisotropy;
+			this._samplerCache.set( samplerKey, samplerData );
 
 
 		}
 		}
 
 
-		if ( texture.isDepthTexture && texture.compareFunction !== null ) {
+		const textureData = backend.get( texture );
+
+		if ( textureData.sampler !== samplerData.sampler ) {
+
+			// check if previous sampler is unused so it can be deleted
+
+			if ( textureData.sampler !== undefined ) {
+
+				const oldSamplerData = this._samplerCache.get( textureData.samplerKey );
+				oldSamplerData.usedTimes --;
+
+				if ( oldSamplerData.usedTimes === 0 ) {
 
 
-			samplerDescriptorGPU.compare = _compareToWebGPU[ texture.compareFunction ];
+					this._samplerCache.delete( textureData.samplerKey );
+
+				}
+
+			}
+
+			// update to new sampler data
+
+			textureData.samplerKey = samplerKey;
+			textureData.sampler = samplerData.sampler;
+
+			samplerData.usedTimes ++;
 
 
 		}
 		}
 
 
-		textureGPU.sampler = device.createSampler( samplerDescriptorGPU );
+		return samplerKey;
 
 
 	}
 	}
 
 
@@ -308,20 +357,6 @@ class WebGPUTextureUtils {
 
 
 	}
 	}
 
 
-	/**
-	 * Destroys the GPU sampler for the given texture.
-	 *
-	 * @param {Texture} texture - The texture to destroy the sampler for.
-	 */
-	destroySampler( texture ) {
-
-		const backend = this.backend;
-		const textureData = backend.get( texture );
-
-		delete textureData.sampler;
-
-	}
-
 	/**
 	/**
 	 * Generates mipmaps for the given texture.
 	 * Generates mipmaps for the given texture.
 	 *
 	 *
@@ -577,6 +612,15 @@ class WebGPUTextureUtils {
 
 
 	}
 	}
 
 
+	/**
+	 * Frees all internal resources.
+	 */
+	dispose() {
+
+		this._samplerCache.clear();
+
+	}
+
 	/**
 	/**
 	 * Returns the default GPU texture for the given format.
 	 * Returns the default GPU texture for the given format.
 	 *
 	 *

粤ICP备19079148号