Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPURenderer: Introduce TimestampQueryPool #30359

Merged
merged 13 commits into from
Jan 21, 2025

Conversation

RenaudRohlinger
Copy link
Collaborator

@RenaudRohlinger RenaudRohlinger commented Jan 18, 2025

Description
Refactor of timestamp and timer query handling in WebGPU/WebGL for the WebGPURenderer.

It is now more stable and performant through the use of TimestampQueryPool, which maintains a single querySet per type (render/compute) and global buffers. This resolves multiple errors and limitations that users have previously reported.

The API now supports multiple render passes (postprocessing) through a new resolveTimestampsAsync method in the Renderer, which resolves all timestamps appended in the loop from different render/compute calls.

Another significant improvement addresses a previous limitation by separating compute and render contexts. Compute operations can now be monitored independently of render calls, as they are often isolated for tasks such as simulations and other complex calculations (with the exception of material.geometryNode).

Example of the new API:

// Render to collision position render target
renderer.setRenderTarget( collisionPosRT );
renderer.render( scene, collisionCamera ); // doesn't require to use renderAsync anymore to calculate timestamp, everything gets calculated in resolveAllTimestampsAsync

// Independent compute pass
renderer.computeAsync( computeParticles );
renderer.resolveTimestampsAsync( 'compute' ); // Resolve compute-specific timing data

// Final render with post-processing for example
renderer.setRenderTarget( null );
await postProcessing.renderAsync();
renderer.resolveTimestampsAsync(); // Resolve all render-specific pass timings

// Performance monitoring or do something with renderer.info
stats.update();

While testing the tool I took the opportunity to improve one of the main example that seems to be affected by excessive buffer access within the loop, resolved by using const and vars:
Before (in WebGPU velocity affects performances 8/9ms per compute):
Screenshot 2025-01-19 at 15 34 29
After quick optimization tests (velocity compute program speed up by ~50%):
Screenshot 2025-01-19 at 15 51 25

If that's ok this refactor will introduce breaking changes. Rather than attempting to maintain backwards compatibility, which would significantly pollute the codebase, I will document the necessary updates in the migration guide for affected components.

This contribution is funded by Utsubo

Copy link

github-actions bot commented Jan 18, 2025

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 336.25
78.28
336.25
78.28
+0 B
+0 B
WebGPU 503.66
139.88
507.53
140.94
+3.87 kB
+1.06 kB
WebGPU Nodes 503.13
139.77
506.99
140.84
+3.87 kB
+1.07 kB

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 465.25
112.13
465.25
112.13
+0 B
+0 B
WebGPU 577.44
156.5
581.32
157.56
+3.88 kB
+1.06 kB
WebGPU Nodes 532.82
146.06
536.7
147.16
+3.88 kB
+1.1 kB

@RenaudRohlinger RenaudRohlinger changed the title WebGPURenderer: Introduce renderer.resolveAllTimestampsAsync(type) WebGPURenderer: Introduce TimestampQueryPool Jan 18, 2025
@sunag sunag added this to the r173 milestone Jan 18, 2025
@RenaudRohlinger RenaudRohlinger marked this pull request as ready for review January 19, 2025 07:04

if ( renderContextData.activeQuery !== null ) {

this.gl.beginQuery( this.disjoint.TIME_ELAPSED_EXT, renderContextData.activeQuery );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice to see the timestamp related code is moved into specific modules. That makes the backends easier to maintain.


// compute

await renderer.computeAsync( computeParticles );
renderer.compute( computeParticles );
renderer.resolveTimestampsAsync( 'compute' );
Copy link
Collaborator

@sunag sunag Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to have a static definition for this like TimestampQuery.COMPUTE ?

@sunag sunag merged commit 806d427 into mrdoob:dev Jan 21, 2025
12 checks passed
@sunag
Copy link
Collaborator

sunag commented Jan 24, 2025

@RenaudRohlinger Do you now why I am getting this warning in some examples like webgpu_compute_particles?

  • WebGPUTimestampQueryPool: Maximum number of queries exceeded.

@RenaudRohlinger
Copy link
Collaborator Author

RenaudRohlinger commented Jan 24, 2025

@sunag

It's because trackTimestamp is being used but we need resolveTimestampsAsync to resolve the timestamp queries. In the example it's currently never being called so the timestamp queries gets accumulated ultimately reaching the max number of queries.
Here we should add resolveTimestampsAsync:

await renderer.computeAsync( computeParticles );
renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );

await renderer.renderAsync( scene, camera );
renderer.resolveTimestampsAsync( THREE.TimestampQuery.RENDER );

@RenaudRohlinger
Copy link
Collaborator Author

Maybe we could improve the log:

Maximum number of queries exceeded, renderer.resolveTimestampsAsync is probably missing.
or:
Maximum number of queries exceeded,
when using trackTimestamp it is necessary to resolves the queries via renderer.resolveTimestampsAsync.

Comment on lines -17 to +18
"three": "../build/three.webgpu.js",
"three/webgpu": "../build/three.webgpu.js",
"three": "../src/Three.WebGPU.js",
"three/webgpu": "../src/Three.WebGPU.js",
Copy link
Owner

@mrdoob mrdoob Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants