瀏覽代碼

WebGPURenderer: Add Timestamp Queries WebGL fallback support (#27870)

* add timestamp fallback support through EXT_disjoint_timer_query_webgl2

* Decrement i to ensure the next element is not skipped
Renaud Rohlinger 1 年之前
父節點
當前提交
ec8d19be78

+ 1 - 1
examples/jsm/renderers/common/Renderer.js

@@ -889,6 +889,7 @@ class Renderer {
 		const previousRenderId = nodeFrame.renderId;
 
 		//
+		if ( this.info.autoReset === true ) this.info.resetCompute();
 
 		this.info.calls ++;
 		this.info.compute.calls ++;
@@ -897,7 +898,6 @@ class Renderer {
 		nodeFrame.renderId = this.info.calls;
 
 		//
-		if ( this.info.autoReset === true ) this.info.resetCompute();
 
 		const backend = this.backend;
 		const pipelines = this._pipelines;

+ 84 - 2
examples/jsm/renderers/webgl/WebGLBackend.js

@@ -45,8 +45,10 @@ class WebGLBackend extends Backend {
 		this.vaoCache = {};
 		this.transformFeedbackCache = {};
 		this.discard = false;
+		this.trackTimestamp = ( parameters.trackTimestamp === true );
 
 		this.extensions.get( 'EXT_color_buffer_float' );
+		this.disjoint = this.extensions.get( 'EXT_disjoint_timer_query_webgl2' );
 		this.parallel = this.extensions.get( 'KHR_parallel_shader_compile' );
 		this._currentContext = null;
 
@@ -64,6 +66,81 @@ class WebGLBackend extends Backend {
 
 	}
 
+
+	initTimestampQuery( renderContext ) {
+
+		if ( ! this.disjoint || ! this.trackTimestamp ) return;
+
+		const renderContextData = this.get( renderContext );
+
+		if ( renderContextData.activeQuery ) {
+
+			// End the previous query if it's still active
+			this.gl.endQuery( this.disjoint.TIME_ELAPSED_EXT );
+
+		}
+
+		renderContextData.activeQuery = this.gl.createQuery();
+		if ( renderContextData.activeQuery !== null ) {
+
+			this.gl.beginQuery( this.disjoint.TIME_ELAPSED_EXT, renderContextData.activeQuery );
+
+		}
+
+
+	}
+
+	// timestamp utils
+
+	prepareTimestampBuffer( renderContext ) {
+
+		if ( ! this.disjoint || ! this.trackTimestamp ) return;
+
+		const renderContextData = this.get( renderContext );
+
+		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;
+
+		}
+
+	}
+
+	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 ++ ) {
+
+			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 ) {
+
+				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 );
+
+			}
+
+		}
+
+	}
+
 	getContext() {
 
 		return this.gl;
@@ -78,6 +155,7 @@ class WebGLBackend extends Backend {
 		//
 
 		//
+		this.initTimestampQuery( renderContext );
 
 		renderContextData.previousContext = this._currentContext;
 		this._currentContext = renderContext;
@@ -219,6 +297,7 @@ class WebGLBackend extends Backend {
 
 		}
 
+		this.prepareTimestampBuffer( renderContext );
 
 	}
 
@@ -379,11 +458,12 @@ class WebGLBackend extends Backend {
 
 	}
 
-	beginCompute( /*computeGroup*/ ) {
+	beginCompute( computeGroup ) {
 
 		const gl = this.gl;
 
 		gl.bindFramebuffer( gl.FRAMEBUFFER, null );
+		this.initTimestampQuery( computeGroup );
 
 	}
 
@@ -456,7 +536,7 @@ class WebGLBackend extends Backend {
 
 	}
 
-	finishCompute( /*computeGroup*/ ) {
+	finishCompute( computeGroup ) {
 
 		const gl = this.gl;
 
@@ -464,6 +544,8 @@ class WebGLBackend extends Backend {
 
 		gl.disable( gl.RASTERIZER_DISCARD );
 
+		this.prepareTimestampBuffer( computeGroup );
+
 	}
 
 	draw( renderObject, info ) {

+ 1 - 0
examples/jsm/renderers/webgl/utils/WebGLConstants.js

@@ -7,5 +7,6 @@ export const GLFeatureName = {
 	'WEBKIT_WEBGL_compressed_texture_pvrtc': 'texture-compression-pvrtc',
 	'WEBGL_compressed_texture_s3tc': 'texture-compression-bc',
 	'EXT_texture_compression_bptc': 'texture-compression-bptc',
+	'EXT_disjoint_timer_query_webgl2': 'timestamp-query',
 
 };

+ 2 - 2
examples/webgpu_compute_particles.html

@@ -294,8 +294,8 @@
 
 						timestamps.innerHTML = `
 
-							Compute ${renderer.info.compute.computeCalls} pass in ${renderer.info.timestamp.compute}ms<br>
-							Draw ${renderer.info.render.drawCalls} pass in ${renderer.info.timestamp.render}ms`;
+							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`;
 
 					}
 

+ 37 - 1
examples/webgpu_storage_buffer.html

@@ -11,6 +11,32 @@
 			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a>
 			<br />This example demonstrate the fetch of external element from a StorageBuffer.
 			<br /> Left canvas is using WebGPU Backend, right canvas is WebGL Backend.
+			<div id="timestamps" style="
+				position: absolute;
+				top: 60px;
+				left: 0;
+				padding: 10px;
+				background: rgba( 0, 0, 0, 0.5 );
+				color: #fff;
+				font-family: monospace;
+				font-size: 12px;
+				line-height: 1.5;
+				pointer-events: none;
+				text-align: left;
+			"></div>
+			<div id="timestamps_webgl" style="
+			position: absolute;
+			top: 60px;
+			right: 0;
+			padding: 10px;
+			background: rgba( 0, 0, 0, 0.5 );
+			color: #fff;
+			font-family: monospace;
+			font-size: 12px;
+			line-height: 1.5;
+			pointer-events: none;
+			text-align: left;
+		"></div>
 		</div>
 
 		<script type="importmap">
@@ -31,6 +57,11 @@
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
 			import StorageInstancedBufferAttribute from 'three/addons/renderers/common/StorageInstancedBufferAttribute.js';
 
+			const timestamps = {
+				webgpu: document.getElementById( 'timestamps' ),
+				webgl: document.getElementById( 'timestamps_webgl' )
+			};
+
 			// WebGPU Backend
 			init();
 
@@ -153,7 +184,7 @@
 				const plane = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), material );
 				scene.add( plane );
 
-				const renderer = new WebGPURenderer( { antialias: false, forceWebGL: forceWebGL } );
+				const renderer = new WebGPURenderer( { antialias: false, forceWebGL: forceWebGL, trackTimestamp: true } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth / 2, window.innerHeight );
 
@@ -184,6 +215,11 @@
 					await renderer.computeAsync( compute );
 					await renderer.renderAsync( scene, camera );
 
+					timestamps[ forceWebGL ? 'webgl' : 'webgpu' ].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`;
+
 					setTimeout( stepAnimation, 1000 );
 			
 				};