Przeglądaj źródła

WebGPURenderer: Improve Infos logic (#27889)

* improve info system on threejs

* cleanup and add stats-gl in example external

* fix bot warning

* remove unused dom
Renaud Rohlinger 1 rok temu
rodzic
commit
f4a695c408

+ 2 - 0
examples/jsm/renderers/common/Animation.js

@@ -18,6 +18,8 @@ class Animation {
 
 			this.requestId = self.requestAnimationFrame( update );
 
+			if ( this.info.autoReset === true ) this.info.reset();
+
 			this.nodes.nodeFrame.update();
 
 			this.info.frame = this.nodes.nodeFrame.frameId;

+ 11 - 19
examples/jsm/renderers/common/Info.js

@@ -12,12 +12,14 @@ class Info {
 			drawCalls: 0,
 			triangles: 0,
 			points: 0,
-			lines: 0
+			lines: 0,
+			timestamp: 0
 		};
 
 		this.compute = {
 			calls: 0,
-			computeCalls: 0
+			computeCalls: 0,
+			timestamp: 0
 		};
 
 		this.memory = {
@@ -25,11 +27,6 @@ class Info {
 			textures: 0
 		};
 
-		this.timestamp = {
-			compute: 0,
-			render: 0
-		};
-
 	}
 
 	update( object, count, instanceCount ) {
@@ -62,26 +59,21 @@ class Info {
 
 	updateTimestamp( type, time ) {
 
-		this.timestamp[ type ] += time;
-
-	}
-
-	resetCompute() {
-
-		this.compute.computeCalls = 0;
-
-		this.timestamp.compute = 0;
+		this[ type ].timestamp += time;
 
 	}
 
 	reset() {
 
 		this.render.drawCalls = 0;
+		this.compute.computeCalls = 0;
+
 		this.render.triangles = 0;
 		this.render.points = 0;
 		this.render.lines = 0;
 
-		this.timestamp.render = 0;
+		this.render.timestamp = 0;
+		this.compute.timestamp = 0;
 
 	}
 
@@ -94,8 +86,8 @@ class Info {
 		this.render.calls = 0;
 		this.compute.calls = 0;
 
-		this.timestamp.compute = 0;
-		this.timestamp.render = 0;
+		this.render.timestamp = 0;
+		this.compute.timestamp = 0;
 		this.memory.geometries = 0;
 		this.memory.textures = 0;
 

+ 2 - 5
examples/jsm/renderers/common/Renderer.js

@@ -312,9 +312,7 @@ class Renderer {
 
 		if ( this._initialized === false ) await this.init();
 
-		const renderContext = this._renderScene( scene, camera );
-
-		await this.backend.resolveTimestampAsync( renderContext, 'render' );
+		this._renderScene( scene, camera );
 
 	}
 
@@ -379,7 +377,6 @@ class Renderer {
 
 		if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
 
-		if ( this.info.autoReset === true ) this.info.reset();
 
 		//
 
@@ -509,6 +506,7 @@ class Renderer {
 		sceneRef.onAfterRender( this, scene, camera, renderTarget );
 
 		//
+		this.backend.resolveTimestampAsync( renderContext, 'render' );
 
 		return renderContext;
 
@@ -898,7 +896,6 @@ class Renderer {
 		const previousRenderId = nodeFrame.renderId;
 
 		//
-		if ( this.info.autoReset === true ) this.info.resetCompute();
 
 		this.info.calls ++;
 		this.info.compute.calls ++;

+ 34 - 19
examples/jsm/renderers/webgl/WebGLBackend.js

@@ -73,26 +73,35 @@ class WebGLBackend extends Backend {
 
 		const renderContextData = this.get( renderContext );
 
+		if ( this.queryRunning ) {
+
+		  if ( ! renderContextData.queryQueue ) renderContextData.queryQueue = [];
+		  renderContextData.queryQueue.push( renderContext );
+		  return;
+
+		}
+
 		if ( renderContextData.activeQuery ) {
 
-			// End the previous query if it's still active
-			this.gl.endQuery( this.disjoint.TIME_ELAPSED_EXT );
+		  this.gl.endQuery( this.disjoint.TIME_ELAPSED_EXT );
+		  renderContextData.activeQuery = null;
 
 		}
 
 		renderContextData.activeQuery = this.gl.createQuery();
+
 		if ( renderContextData.activeQuery !== null ) {
 
-			this.gl.beginQuery( this.disjoint.TIME_ELAPSED_EXT, renderContextData.activeQuery );
+		  this.gl.beginQuery( this.disjoint.TIME_ELAPSED_EXT, renderContextData.activeQuery );
+		  this.queryRunning = true;
 
 		}
 
-
 	}
 
 	// timestamp utils
 
-	prepareTimestampBuffer( renderContext ) {
+	  prepareTimestampBuffer( renderContext ) {
 
 		if ( ! this.disjoint || ! this.trackTimestamp ) return;
 
@@ -100,39 +109,45 @@ class WebGLBackend extends Backend {
 
 		if ( renderContextData.activeQuery ) {
 
-			this.gl.endQuery( this.disjoint.TIME_ELAPSED_EXT );
-			// Add the active query to the gpuQueries array and reset it
-			if ( renderContextData.gpuQueries === undefined ) renderContextData.gpuQueries = [];
-			renderContextData.gpuQueries.push( { query: renderContextData.activeQuery } );
-			renderContextData.activeQuery = null;
+		  this.gl.endQuery( this.disjoint.TIME_ELAPSED_EXT );
+
+		  if ( ! renderContextData.gpuQueries ) renderContextData.gpuQueries = [];
+		  renderContextData.gpuQueries.push( { query: renderContextData.activeQuery } );
+		  renderContextData.activeQuery = null;
+		  this.queryRunning = false;
+
+		  if ( renderContextData.queryQueue && renderContextData.queryQueue.length > 0 ) {
+
+				const nextRenderContext = renderContextData.queryQueue.shift();
+				this.initTimestampQuery( nextRenderContext );
+
+			}
 
 		}
 
 	}
 
-	async resolveTimestampAsync( renderContext, type = 'render' ) {
+	  async resolveTimestampAsync( renderContext, type = 'render' ) {
 
 		if ( ! this.disjoint || ! this.trackTimestamp ) return;
 
 		const renderContextData = this.get( renderContext );
 
-		for ( let i = 0; i < renderContextData.gpuQueries.length; i ++ ) {
+		if ( ! renderContextData.gpuQueries ) renderContextData.gpuQueries = [];
 
-			const queryInfo = renderContextData.gpuQueries[ i ];
+		for ( let i = 0; i < renderContextData.gpuQueries.length; i ++ ) {
 
-			const available = this.gl.getQueryParameter( queryInfo.query, this.gl.QUERY_RESULT_AVAILABLE );
-			const disjoint = this.gl.getParameter( this.disjoint.GPU_DISJOINT_EXT );
+		  const queryInfo = renderContextData.gpuQueries[ i ];
+		  const available = this.gl.getQueryParameter( queryInfo.query, this.gl.QUERY_RESULT_AVAILABLE );
+		  const disjoint = this.gl.getParameter( this.disjoint.GPU_DISJOINT_EXT );
 
-			if ( available && ! disjoint ) {
+		  if ( available && ! disjoint ) {
 
 				const elapsed = this.gl.getQueryParameter( queryInfo.query, this.gl.QUERY_RESULT );
 				const duration = Number( elapsed ) / 1000000; // Convert nanoseconds to milliseconds
-
 				this.gl.deleteQuery( queryInfo.query );
 				renderContextData.gpuQueries.splice( i, 1 ); // Remove the processed query
-
 				i --;
-
 				this.renderer.info.updateTimestamp( type, duration );
 
 			}

+ 22 - 24
examples/jsm/renderers/webgpu/WebGPUBackend.js

@@ -1049,9 +1049,11 @@ class WebGPUBackend extends Backend {
 				beginningOfPassWriteIndex: 0, // Write timestamp in index 0 when pass begins.
 				endOfPassWriteIndex: 1, // Write timestamp in index 1 when pass ends.
 			};
+
 			Object.assign( descriptor, {
 				timestampWrites,
 			} );
+
 			renderContextData.timeStampQuerySet = timeStampQuerySet;
 
 		}
@@ -1084,33 +1086,29 @@ class WebGPUBackend extends Backend {
 
 	}
 
-	async resolveTimestampAsync( renderContext, type = 'render' ) {
-
-		if ( ! this.hasFeature( GPUFeatureName.TimestampQuery ) || ! this.trackTimestamp ) return;
-
-		const renderContextData = this.get( renderContext );
-
-		// handle timestamp query results
-
+	async resolveTimestampAsync(renderContext, type = 'render') {
+		if (!this.hasFeature(GPUFeatureName.TimestampQuery) || !this.trackTimestamp) return;
+	
+		const renderContextData = this.get(renderContext);
 		const { currentTimestampQueryBuffer } = renderContextData;
-
-		if ( currentTimestampQueryBuffer ) {
-
-			renderContextData.currentTimestampQueryBuffer = null;
-
-			await currentTimestampQueryBuffer.mapAsync( GPUMapMode.READ );
-
-			const times = new BigUint64Array( currentTimestampQueryBuffer.getMappedRange() );
-
-			const duration = Number( times[ 1 ] - times[ 0 ] ) / 1000000;
-			// console.log( `Compute ${type} duration: ${Number( times[ 1 ] - times[ 0 ] ) / 1000000}ms` );
-			this.renderer.info.updateTimestamp( type, duration );
-
-			currentTimestampQueryBuffer.unmap();
-
+	
+		if (currentTimestampQueryBuffer === undefined) return;
+
+		const buffer = currentTimestampQueryBuffer;
+
+		try {
+			await buffer.mapAsync(GPUMapMode.READ);
+			const times = new BigUint64Array(buffer.getMappedRange());
+			const duration = Number(times[1] - times[0]) / 1000000;
+			this.renderer.info.updateTimestamp(type, duration);
+		} catch (error) {
+			console.error(`Error mapping buffer: ${error}`);
+			// Optionally handle the error, e.g., re-queue the buffer or skip it
+		} finally {
+			buffer.unmap(); 
 		}
-
 	}
+	
 
 	// node builder
 

+ 1 - 0
examples/tags.json

@@ -102,6 +102,7 @@
 	"webgl_materials_modified": [ "onBeforeCompile" ],
 	"webgl_raycaster_bvh": [ "external", "query", "bounds", "tree", "accelerate", "performance", "community", "extension", "plugin", "library", "three-mesh-bvh" ],
 	"webgl_renderer_pathtracer": [ "external", "raytracing", "pathtracing", "library", "plugin", "extension", "community", "three-gpu-pathtracer", "three-mesh-bvh" ],
+	"webgpu_compute_particles_snow": [ "external", "webgpu", "stats-gl" ],
 	"webgl_shadowmap_csm": [ "cascade" ],
 	"webgl_shadowmap_pcss": [ "soft" ],
 	"webgl_simple_gi": [ "global illumination" ],

+ 2 - 6
examples/webgpu_compute_particles.html

@@ -192,8 +192,6 @@
 
 				//
 
-				renderer.info.autoReset = false;
-
 				renderer.compute( computeInit );
 
 				// click event
@@ -294,8 +292,8 @@
 
 						timestamps.innerHTML = `
 
-							Compute ${renderer.info.compute.computeCalls} pass in ${renderer.info.timestamp.compute.toFixed( 6 )}ms<br>
-							Draw ${renderer.info.render.drawCalls} pass in ${renderer.info.timestamp.render.toFixed( 6 )}ms`;
+							Compute ${renderer.info.compute.computeCalls} pass in ${renderer.info.compute.timestamp.toFixed( 6 )}ms<br>
+							Draw ${renderer.info.render.drawCalls} pass in ${renderer.info.render.timestamp.toFixed( 6 )}ms`;
 
 					}
 
@@ -305,8 +303,6 @@
 
 				}
 
-				renderer.info.resetCompute();
-				renderer.info.reset();
 
 			}
 

+ 28 - 22
examples/webgpu_compute_particles_snow.html

@@ -16,7 +16,8 @@
 				"imports": {
 					"three": "../build/three.module.js",
 					"three/addons/": "./jsm/",
-					"three/nodes": "./jsm/nodes/Nodes.js"
+					"three/nodes": "./jsm/nodes/Nodes.js",
+					"stats-gl": "https://www.unpkg.com/[email protected]/dist/main.js"
 				}
 			}
 		</script>
@@ -37,7 +38,7 @@
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
-			import Stats from 'three/addons/libs/stats.module.js';
+			import Stats from 'stats-gl';
 
 			const maxParticleCount = 100000;
 
@@ -50,7 +51,7 @@
 
 			init();
 
-			function init() {
+			async function init() {
 
 				if ( WebGPU.isAvailable() === false && WebGL.isWebGL2Available() === false ) {
 
@@ -285,9 +286,25 @@
 				renderer.setAnimationLoop( animate );
 				document.body.appendChild( renderer.domElement );
 
-				stats = new Stats();
+				stats = new Stats( {
+					precision: 3,
+					horizontal: false
+				} );
+				stats.init( renderer );
 				document.body.appendChild( stats.dom );
 
+
+				//
+
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, 10, 0 );
+				controls.minDistance = 25;
+				controls.maxDistance = 35;
+				controls.maxPolarAngle = Math.PI / 1.7;
+				controls.autoRotate = true;
+				controls.autoRotateSpeed = - 0.7;
+				controls.update();
+
 				// post processing
 
 				const scenePass = pass( scene, camera );
@@ -314,18 +331,7 @@
 
 				//
 
-				renderer.compute( computeInit );
-
-				//
-
-				controls = new OrbitControls( camera, renderer.domElement );
-				controls.target.set( 0, 10, 0 );
-				controls.minDistance = 25;
-				controls.maxDistance = 35;
-				controls.maxPolarAngle = Math.PI / 1.7;
-				controls.autoRotate = true;
-				controls.autoRotateSpeed = - 0.7;
-				controls.update();
+				await renderer.computeAsync( computeInit );
 
 				//
 
@@ -344,9 +350,7 @@
 
 			}
 
-			function animate() {
-
-				stats.update();
+			async function animate() {
 
 				controls.update();
 
@@ -354,18 +358,20 @@
 
 				scene.overrideMaterial = collisionPosMaterial;
 				renderer.setRenderTarget( collisionPosRT );
-				renderer.render( scene, collisionCamera );
+				await renderer.renderAsync( scene, collisionCamera );
 
 				// compute
 
-				renderer.compute( computeParticles );
+				await renderer.computeAsync( computeParticles );
 
 				// result
 
 				scene.overrideMaterial = null;
 				renderer.setRenderTarget( null );
 
-				postProcessing.render();
+				await postProcessing.renderAsync();
+			
+				stats.update();
 
 			}