Jelajahi Sumber

Backend: Introduce `createUniformBuffer()` (#33413)

sunag 2 bulan lalu
induk
melakukan
4018d68d8a

+ 8 - 0
examples/jsm/inspector/tabs/Memory.js

@@ -70,6 +70,9 @@ class Memory extends Tab {
 		this.textures = new Item( 'Textures', createValueSpan(), createValueSpan() );
 		this.memoryStats.add( this.textures );
 
+		this.uniformBuffers = new Item( 'Uniform Buffers', createValueSpan(), createValueSpan() );
+		this.memoryStats.add( this.uniformBuffers );
+
 		this.graph = graph;
 
 	}
@@ -100,6 +103,7 @@ class Memory extends Tab {
 
 		setText( this.attributes.data[ 1 ], memory.attributes.toString() );
 		setText( this.attributes.data[ 2 ], formatBytes( memory.attributesSize ) );
+
 		setText( this.geometries.data[ 1 ], memory.geometries.toString() );
 
 		setText( this.indexAttributes.data[ 1 ], memory.indexAttributes.toString() );
@@ -118,9 +122,13 @@ class Memory extends Tab {
 
 		setText( this.storageAttributes.data[ 1 ], memory.storageAttributes.toString() );
 		setText( this.storageAttributes.data[ 2 ], formatBytes( memory.storageAttributesSize ) );
+
 		setText( this.textures.data[ 1 ], memory.textures.toString() );
 		setText( this.textures.data[ 2 ], formatBytes( memory.texturesSize ) );
 
+		setText( this.uniformBuffers.data[ 1 ], memory.uniformBuffers.toString() );
+		setText( this.uniformBuffers.data[ 2 ], formatBytes( memory.uniformBuffersSize ) );
+
 	}
 
 }

+ 21 - 3
examples/jsm/inspector/tabs/Timeline.js

@@ -1044,11 +1044,29 @@ class Timeline extends Tab {
 			case 'updateBindings': {
 
 				const bindGroup = args[ 0 ];
-				const details = { group: bindGroup.name || 'unknown' };
 
-				if ( bindGroup.bindings ) {
+				const details = {
+					group: bindGroup.name || 'unknown',
+					count: bindGroup.bindings.length
+				};
+
+				return details;
+
+			}
+
+			case 'createUniformBuffer':
+			case 'destroyUniformBuffer': {
+
+				const binding = args[ 0 ];
+
+				const details = {
+					group: binding.groupNode.name || 'unknown',
+					size: binding.byteLength + ' bytes'
+				};
+
+				if ( binding.name !== details.group ) {
 
-					details.count = bindGroup.bindings.length;
+					details.name = binding.name;
 
 				}
 

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

@@ -389,6 +389,22 @@ class Backend {
 	 */
 	createStorageAttribute( /*attribute*/ ) { }
 
+	/**
+	 * Creates a uniform buffer.
+	 *
+	 * @abstract
+	 * @param {Buffer} uniformBuffer - The uniform buffer.
+	 */
+	createUniformBuffer( /*uniformBuffer*/ ) { }
+
+	/**
+	 * Destroys a uniform buffer.
+	 *
+	 * @abstract
+	 * @param {Buffer} uniformBuffer - The uniform buffer.
+	 */
+	destroyUniformBuffer( /*uniformBuffer*/ ) { }
+
 	/**
 	 * Updates the GPU buffer of a shader attribute.
 	 *

+ 97 - 46
src/renderers/common/Bindings.js

@@ -79,21 +79,15 @@ class Bindings extends DataMap {
 
 		const bindings = renderObject.getBindings();
 
-		for ( const bindGroup of bindings ) {
-
-			const groupData = this.get( bindGroup );
+		const renderObjectData = this.get( renderObject );
 
-			if ( groupData.bindGroup === undefined ) {
+		if ( renderObjectData.initialized !== true ) {
 
-				// each object defines an array of bindings (ubos, textures, samplers etc.)
+			// bind groups are created once per object
 
-				this._init( bindGroup );
+			this._createBindings( bindings );
 
-				this.backend.createBindings( bindGroup, bindings, 0 );
-
-				groupData.bindGroup = bindGroup;
-
-			}
+			renderObjectData.initialized = true;
 
 		}
 
@@ -110,20 +104,15 @@ class Bindings extends DataMap {
 	getForCompute( computeNode ) {
 
 		const bindings = this.nodes.getForCompute( computeNode ).bindings;
+		const computeNodeData = this.get( computeNode );
 
-		for ( const bindGroup of bindings ) {
-
-			const groupData = this.get( bindGroup );
-
-			if ( groupData.bindGroup === undefined ) {
+		if ( computeNodeData.initialized !== true ) {
 
-				this._init( bindGroup );
+			// bind groups are created once per object
 
-				this.backend.createBindings( bindGroup, bindings, 0 );
+			this._createBindings( bindings );
 
-				groupData.bindGroup = bindGroup;
-
-			}
+			computeNodeData.initialized = true;
 
 		}
 
@@ -162,12 +151,9 @@ class Bindings extends DataMap {
 
 		const bindings = this.nodes.getForCompute( computeNode ).bindings;
 
-		for ( const bindGroup of bindings ) {
+		this._destroyBindings( bindings );
 
-			this.backend.deleteBindGroupData( bindGroup );
-			this.delete( bindGroup );
-
-		}
+		this.delete( computeNode );
 
 	}
 
@@ -180,53 +166,103 @@ class Bindings extends DataMap {
 
 		const bindings = renderObject.getBindings();
 
-		for ( const bindGroup of bindings ) {
-
-			this.backend.deleteBindGroupData( bindGroup );
-			this.delete( bindGroup );
+		this._destroyBindings( bindings );
 
-		}
+		this.delete( renderObject );
 
 	}
 
 	/**
-	 * Updates the given array of bindings.
+	 * Creates the bindings for the given array of bindings.
 	 *
 	 * @param {Array<BindGroup>} bindings - The bind groups.
 	 */
-	_updateBindings( bindings ) {
+	_createBindings( bindings ) {
 
 		for ( const bindGroup of bindings ) {
 
-			this._update( bindGroup, bindings );
+			// binding group
+
+			const groupData = this.get( bindGroup );
+
+			if ( groupData.bindGroup === undefined ) {
+
+				// initialize
+
+				for ( const binding of bindGroup.bindings ) {
+
+					if ( binding.isUniformBuffer ) {
+
+						this.backend.createUniformBuffer( binding );
+						this.info.createUniformBuffer( binding );
+
+					} else if ( binding.isSampledTexture ) {
+
+						this.textures.updateTexture( binding.texture );
+
+					} else if ( binding.isSampler ) {
+
+						this.textures.updateSampler( binding.texture );
+
+					} else if ( binding.isStorageBuffer ) {
+
+						const attribute = binding.attribute;
+						const attributeType = attribute.isIndirectStorageBufferAttribute ? AttributeType.INDIRECT : AttributeType.STORAGE;
+
+						this.attributes.update( attribute, attributeType );
+
+					}
+
+				}
+
+				// each object defines an array of bindings (ubos, textures, samplers etc.)
+
+				this.backend.createBindings( bindGroup, bindings, 0 );
+
+				groupData.bindGroup = bindGroup;
+				groupData.usedTimes = 1;
+
+			} else {
+
+				groupData.usedTimes ++;
+
+			}
 
 		}
 
 	}
 
 	/**
-	 * Initializes the given bind group.
+	 * Deletes the given array of bindings.
 	 *
-	 * @param {BindGroup} bindGroup - The bind group to initialize.
+	 * @param {Array<BindGroup>} bindings - The bind groups.
 	 */
-	_init( bindGroup ) {
+	_destroyBindings( bindings ) {
 
-		for ( const binding of bindGroup.bindings ) {
+		for ( const bindGroup of bindings ) {
 
-			if ( binding.isSampledTexture ) {
+			const groupData = this.get( bindGroup );
+			groupData.usedTimes --;
 
-				this.textures.updateTexture( binding.texture );
+			if ( groupData.usedTimes === 0 ) {
 
-			} else if ( binding.isSampler ) {
+				for ( const binding of bindGroup.bindings ) {
 
-				this.textures.updateSampler( binding.texture );
+					if ( binding.isUniformBuffer ) {
 
-			} else if ( binding.isStorageBuffer ) {
+						this.backend.destroyUniformBuffer( binding );
+						this.info.destroyUniformBuffer( binding );
 
-				const attribute = binding.attribute;
-				const attributeType = attribute.isIndirectStorageBufferAttribute ? AttributeType.INDIRECT : AttributeType.STORAGE;
+						// release arrays
 
-				this.attributes.update( attribute, attributeType );
+						binding.release();
+
+					}
+
+				}
+
+				this.backend.deleteBindGroupData( bindGroup );
+				this.delete( bindGroup );
 
 			}
 
@@ -234,6 +270,21 @@ class Bindings extends DataMap {
 
 	}
 
+	/**
+	 * Updates the given array of bindings.
+	 *
+	 * @param {Array<BindGroup>} bindings - The bind groups.
+	 */
+	_updateBindings( bindings ) {
+
+		for ( const bindGroup of bindings ) {
+
+			this._update( bindGroup, bindings );
+
+		}
+
+	}
+
 	/**
 	 * Updates the given bind group.
 	 *

+ 9 - 0
src/renderers/common/Buffer.js

@@ -122,6 +122,15 @@ class Buffer extends Binding {
 
 	}
 
+	/**
+	 * Releases the buffer.
+	 */
+	release() {
+
+		this._buffer = null;
+
+	}
+
 }
 
 export default Buffer;

+ 63 - 22
src/renderers/common/Info.js

@@ -94,42 +94,46 @@ class Info {
 		 *
 		 * @type {Object}
 		 * @readonly
-		 * @property {number} geometries - The number of active geometries.
-		 * @property {number} textures - The number of active textures.
 		 * @property {number} attributes - The number of active attributes.
+		 * @property {number} attributesSize - The memory size of active attributes in bytes.
+		 * @property {number} geometries - The number of active geometries.
 		 * @property {number} indexAttributes - The number of active index attributes.
-		 * @property {number} storageAttributes - The number of active storage attributes.
+		 * @property {number} indexAttributesSize - The memory size of active index attributes in bytes.
 		 * @property {number} indirectStorageAttributes - The number of active indirect storage attributes.
-		 * @property {number} readbackBuffers - The number of active readback buffers.
+		 * @property {number} indirectStorageAttributesSize - The memory size of active indirect storage attributes in bytes.
 		 * @property {number} programs - The number of active programs.
+		 * @property {number} programsSize - The memory size of active programs in bytes.
+		 * @property {number} readbackBuffers - The number of active readback buffers.
+		 * @property {number} readbackBuffersSize - The memory size of active readback buffers in bytes.
 		 * @property {number} renderTargets - The number of active renderTargets.
-		 * @property {number} total - The total memory size in bytes.
-		 * @property {number} texturesSize - The memory size of active textures in bytes.
-		 * @property {number} attributesSize - The memory size of active attributes in bytes.
-		 * @property {number} indexAttributesSize - The memory size of active index attributes in bytes.
+		 * @property {number} storageAttributes - The number of active storage attributes.
 		 * @property {number} storageAttributesSize - The memory size of active storage attributes in bytes.
-		 * @property {number} indirectStorageAttributesSize - The memory size of active indirect storage attributes in bytes.
-		 * @property {number} readbackBuffersSize - The memory size of active readback buffers in bytes.
-		 * @property {number} programsSize - The memory size of active programs in bytes.
+		 * @property {number} textures - The number of active textures.
+		 * @property {number} texturesSize - The memory size of active textures in bytes.
+		 * @property {number} uniformBuffers - The number of active uniform buffers.
+		 * @property {number} uniformBuffersSize - The memory size of active uniform buffers in bytes.
+		 * @property {number} total - The total memory size in bytes.
 		 */
 		this.memory = {
-			geometries: 0,
-			textures: 0,
 			attributes: 0,
+			attributesSize: 0,
+			geometries: 0,
 			indexAttributes: 0,
-			storageAttributes: 0,
+			indexAttributesSize: 0,
 			indirectStorageAttributes: 0,
-			readbackBuffers: 0,
+			indirectStorageAttributesSize: 0,
 			programs: 0,
+			programsSize: 0,
+			readbackBuffers: 0,
+			readbackBuffersSize: 0,
 			renderTargets: 0,
-			total: 0,
-			texturesSize: 0,
-			attributesSize: 0,
-			indexAttributesSize: 0,
+			storageAttributes: 0,
 			storageAttributesSize: 0,
-			indirectStorageAttributesSize: 0,
-			readbackBuffersSize: 0,
-			programsSize: 0
+			textures: 0,
+			texturesSize: 0,
+			uniformBuffers: 0,
+			uniformBuffersSize: 0,
+			total: 0
 		};
 
 		/**
@@ -365,6 +369,43 @@ class Info {
 
 	}
 
+	/**
+	 * Tracks a uniform buffer memory explicitly.
+	 *
+	 * @param {UniformBuffer} uniformBuffer - The uniform buffer to track.
+	 */
+	createUniformBuffer( uniformBuffer ) {
+
+		const size = uniformBuffer.byteLength;
+		this.memoryMap.set( uniformBuffer, { size, type: 'uniformBuffers' } );
+
+		this.memory.uniformBuffers ++;
+		this.memory.total += size;
+		this.memory.uniformBuffersSize += size;
+
+	}
+
+	/**
+	 * Tracks a uniform buffer memory explicitly.
+	 *
+	 * @param {UniformBuffer} uniformBuffer - The uniform buffer to track.
+	 */
+	destroyUniformBuffer( uniformBuffer ) {
+
+		const data = this.memoryMap.get( uniformBuffer );
+
+		if ( data ) {
+
+			this.memoryMap.delete( uniformBuffer );
+
+			this.memory.uniformBuffers --;
+			this.memory.total -= data.size;
+			this.memory.uniformBuffersSize -= data.size;
+
+		}
+
+	}
+
 	/**
 	 * Tracks program memory explicitly, updating counts and byte tracking.
 	 *

+ 11 - 0
src/renderers/common/UniformsGroup.js

@@ -238,6 +238,17 @@ class UniformsGroup extends UniformBuffer {
 
 	}
 
+	/**
+	 * Releases the buffer.
+	 */
+	release() {
+
+		super.release();
+
+		this._values = null;
+
+	}
+
 	/**
 	 * Updates a given uniform by calling an update method matching
 	 * the uniforms type.

+ 40 - 19
src/renderers/webgl-fallback/WebGLBackend.js

@@ -1834,24 +1834,9 @@ class WebGLBackend extends Backend {
 			if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
 
 				const array = binding.buffer;
-				let { bufferGPU } = this.get( array );
+				const bufferGPU = map.bufferGPU;
 
-				if ( bufferGPU === undefined ) {
-
-					// create
-
-					bufferGPU = gl.createBuffer();
-
-					gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
-					gl.bufferData( gl.UNIFORM_BUFFER, array.byteLength, gl.DYNAMIC_DRAW );
-
-					this.set( array, { bufferGPU } );
-
-				} else {
-
-					gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
-
-				}
+				gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
 
 				// update
 
@@ -1883,8 +1868,6 @@ class WebGLBackend extends Backend {
 
 				}
 
-				map.bufferGPU = bufferGPU;
-
 				this.set( binding, map );
 
 			} else if ( binding.isSampledTexture ) {
@@ -1951,6 +1934,44 @@ class WebGLBackend extends Backend {
 
 	// attributes
 
+	/**
+	 * Creates a uniform buffer.
+	 *
+	 * @param {Buffer} uniformBuffer - The uniform buffer.
+	 */
+	createUniformBuffer( uniformBuffer ) {
+
+		const uniformBufferData = this.get( uniformBuffer );
+
+		if ( uniformBufferData.bufferGPU === undefined ) {
+
+			const gl = this.gl;
+			const array = uniformBuffer.buffer;
+
+			uniformBufferData.bufferGPU = gl.createBuffer();
+
+			gl.bindBuffer( gl.UNIFORM_BUFFER, uniformBufferData.bufferGPU );
+			gl.bufferData( gl.UNIFORM_BUFFER, array.byteLength, gl.DYNAMIC_DRAW );
+
+		}
+
+	}
+
+	/**
+	 * Destroys the GPU data for the given uniform buffer.
+	 *
+	 * @param {Buffer} uniformBuffer - The uniform buffer.
+	 */
+	destroyUniformBuffer( uniformBuffer ) {
+
+		const uniformBufferData = this.get( uniformBuffer );
+
+		this.gl.deleteBuffer( uniformBufferData.bufferGPU );
+
+		this.delete( uniformBuffer );
+
+	}
+
 	/**
 	 * Creates the GPU buffer of an indexed shader attribute.
 	 *

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

@@ -2,7 +2,7 @@
 import 'https://greggman.github.io/webgpu-avoid-redundant-state-setting/webgpu-check-redundant-state-setting.js';
 //*/
 
-import { GPUFeatureName, GPULoadOp, GPUStoreOp, GPUIndexFormat, GPUTextureViewDimension, GPUFeatureMap } from './utils/WebGPUConstants.js';
+import { GPUFeatureName, GPULoadOp, GPUStoreOp, GPUIndexFormat, GPUTextureViewDimension, GPUFeatureMap, GPUShaderStage } from './utils/WebGPUConstants.js';
 
 import WGSLNodeBuilder from './nodes/WGSLNodeBuilder.js';
 import Backend from '../common/Backend.js';
@@ -2178,6 +2178,70 @@ class WebGPUBackend extends Backend {
 
 	// bindings
 
+	/**
+	 * Creates a uniform buffer.
+	 *
+	 * @param {Buffer} uniformBuffer - The uniform buffer.
+	 */
+	createUniformBuffer( uniformBuffer ) {
+
+		const uniformBufferData = this.get( uniformBuffer );
+
+		if ( uniformBufferData.buffer === undefined ) {
+
+			const byteLength = uniformBuffer.byteLength;
+
+			const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
+
+			const visibilities = [];
+
+			if ( uniformBuffer.visibility & GPUShaderStage.VERTEX ) {
+
+				visibilities.push( 'vertex' );
+
+			}
+
+			if ( uniformBuffer.visibility & GPUShaderStage.FRAGMENT ) {
+
+				visibilities.push( 'fragment' );
+
+			}
+
+			if ( uniformBuffer.visibility & GPUShaderStage.COMPUTE ) {
+
+				visibilities.push( 'compute' );
+
+			}
+
+			const bufferVisibility = `(${visibilities.join( ',' )})`;
+
+			const bufferGPU = this.device.createBuffer( {
+				label: `bindingBuffer${uniformBuffer.id}_${uniformBuffer.name}_${bufferVisibility}`,
+				size: byteLength,
+				usage: usage
+			} );
+
+			uniformBufferData.buffer = bufferGPU;
+
+		}
+
+	}
+
+	/**
+	 * Destroys the GPU data for the given uniform buffer.
+	 *
+	 * @param {Buffer} uniformBuffer - The uniform buffer.
+	 */
+	destroyUniformBuffer( uniformBuffer ) {
+
+		const uniformBufferData = this.get( uniformBuffer );
+
+		uniformBufferData.buffer.destroy();
+
+		this.delete( uniformBuffer );
+
+	}
+
 	/**
 	 * Creates bindings from the given bind group definition.
 	 *

+ 0 - 37
src/renderers/webgpu/utils/WebGPUBindingUtils.js

@@ -283,43 +283,6 @@ class WebGPUBindingUtils {
 
 				const bindingData = backend.get( binding );
 
-				if ( bindingData.buffer === undefined ) {
-
-					const byteLength = binding.byteLength;
-
-					const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
-
-					const visibilities = [];
-					if ( binding.visibility & GPUShaderStage.VERTEX ) {
-
-						visibilities.push( 'vertex' );
-
-					}
-
-					if ( binding.visibility & GPUShaderStage.FRAGMENT ) {
-
-						visibilities.push( 'fragment' );
-
-					}
-
-					if ( binding.visibility & GPUShaderStage.COMPUTE ) {
-
-						visibilities.push( 'compute' );
-
-					}
-
-					const bufferVisibility = `(${visibilities.join( ',' )})`;
-
-					const bufferGPU = device.createBuffer( {
-						label: `bindingBuffer${binding.id}_${binding.name}_${bufferVisibility}`,
-						size: byteLength,
-						usage: usage
-					} );
-
-					bindingData.buffer = bufferGPU;
-
-				}
-
 				entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
 
 			} else if ( binding.isStorageBuffer ) {

粤ICP备19079148号