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

Examples: add GPU stats panel #21509

Merged
merged 4 commits into from
May 27, 2021
Merged

Examples: add GPU stats panel #21509

merged 4 commits into from
May 27, 2021

Conversation

gkjohnson
Copy link
Collaborator

@gkjohnson gkjohnson commented Mar 25, 2021

Related issue: --

Description

Inspired by @brunosimon's tweet here this PR adds a custom GPU stats panel for tracking time spent doing GPU work when rendering using disjoint_timer_query for WebGL1 and 2. The MDN docs on the extension are out of date / non existent but the khronos spec document has a pretty easy to follow example. This can ultimately be used to optimize materials and check the impact of shader changes. A timing query is not guaranteed to be available on the next frame so the panel iteratively checks whether a query is complete on subsequent frames by starting a chain of requestAnimationFrame callbacks to check it. This ensures that no frame measurement is missed and that the panel works with the on demand rendering use case.

You can use it like so:

import Stats from 'three/examples/jsm/libs/stats.module.js';
import { GPUStatsPanel } from 'three/examples/jsm/utils/GPUStatsPanel.js';

// ... init
stats = new Stats();
document.body.appendChild( stats.domElement );

gpuPanel = new GPUStatsPanel();
stats.addPanel( gpuPanel );
stats.showPanel( 0 );

// ... render
stats.update();
gpuPanel.startQuery();
renderer.render( camera, scene );
gpuPanel.endQuery();

And some screenshots showing the time difference on my lower powered Surface Laptop between rending the GLTFLoader example with a basic material vs the default standard material:

MeshBasicMaterial MeshStandardMaterial
time ~1ms ~3ms
screenshot image image

The extension actually reports times in nanoseconds so it would be nice to display another decimal or two of precision in the stats panel (doesn't look like that's possible at the moment). As an aside this extension generally seems really useful. I plan to use it to auto-scale the scene fidelity based on measured performance which I was previously doing based on JS frame time and using it to find which objects and shaders are a bottle neck in scenes.

@Mugen87
Copy link
Collaborator

Mugen87 commented Mar 25, 2021

How about making this change directly in https://github.com/mrdoob/stats.js/?

It's then only necessary to update the library file in the repo.

@brunosimon
Copy link
Contributor

I've been testing my own version on a project and I can say, it's a game changer. Being able to see the time it takes to render instead of a framerate that will be good anyway if using a good computer is so great.

I agree with @Mugen87. Having it on the Stats repo might be a better idea because it's related to WebGL more than Three.js.

But I recommend testing it on multiple projects first. I'm having this strange issue where the calculated time is randomly way too big until I resize my window once. I'll try to test your version, maybe did something wrong.

@gkjohnson
Copy link
Collaborator Author

I've been testing my own version on a project and I can say, it's a game changer. Being able to see the time it takes to render instead of a framerate that will be good anyway if using a good computer is so great.

I can imagine. I've been wanting a way to measure this stuff in WebGL for so long. As far as I can tell, though, it's not available in Firefox or Safari so using it to iteratively adjust render fidelity would have limited utility. But for development and optimizing it seems great.

But I recommend testing it on multiple projects first. I'm having this strange issue where the calculated time is randomly way too big until I resize my window once

This I didn't see in my testing. I'll try it on a few other examples when I have time. I've used it a bit on my internal projects, as well, though.

How about making this change directly in https://github.com/mrdoob/stats.js/?

I'd be happy to add this panel to the stats repo. @mrdoob would you be open to adding this panel there? I imagine the API would look something like so:

// instantiate stats with optional webgl context
stats = new Stats( renderer.getContext() );
document.body.appendChild( stats.domElement );

// ... render
stats.update();
stats.beginGPUQuery();
renderer.render( ... );
stats.endGPUQuery();

@zalo
Copy link
Contributor

zalo commented May 6, 2021

Thanks for the PR! I've added this to my local project. Locally, I've switched it to us (microseconds) for the additional granularity.

Does this method handle multiple render calls in between beginning and ending the query (or reset the timer for the duration of the latest render call)?

@mrdoob mrdoob modified the milestones: rXXX, r129 May 6, 2021
@mrdoob
Copy link
Owner

mrdoob commented May 6, 2021

Sorry for the delay!
Could you add the panel to one of the examples?

@gkjohnson
Copy link
Collaborator Author

@mrdoob

Could you add the panel to one of the examples?

Done! I've added it to the webgl_lines_fat example because it shows an interesting case -- you can increase the thickness of the fat lines and toggle between it and gl.LINE to see GPU performance differences. On my MS surface laptop I see a difference of 2ms to 1ms when changing the line thickness. Is there a reliable way to show the last panel added to Stats.js? I see a user added panel is not guaranteed to be index 3. It would be nice to show the GPU time by default.

https://raw.githack.com/gkjohnson/three.js/gpu-stats-panel/examples/webgl_lines_fat.html

Also any thoughts on update Stats.js to support variable decimal precision? The query timer support nanosecond timing so it's possible to get useful information at a far greater granularity than 1ms. I was hoping to display 2-3 decimals for the GPU panel only.

@zalo

Does this method handle multiple render calls in between beginning and ending the query (or reset the timer for the duration of the latest render call)?

Any WebGL commands issued between the startQuery and the endQuery calls will be measured even if render is called multiple times.

@mrdoob mrdoob merged commit 60b151c into mrdoob:dev May 27, 2021
@mrdoob
Copy link
Owner

mrdoob commented May 27, 2021

Thanks!

@gkjohnson
Copy link
Collaborator Author

Woo!

@mrdoob Any thoughts on this?

Also any thoughts on update Stats.js to support variable decimal precision? The gpu query timer supports nanosecond timing so it's possible to get useful information at a far greater granularity than 1ms. I was hoping to display 2-3 decimals for the GPU panel only.

Right now the the precision of the panel makes it hard to see granular timing differences. If you're up for it I can add an option to the stats panel class to support variable precision. I can add it directly in this repo or into the stats.js repo if you'd prefer.

@munrocket
Copy link
Contributor

I make a new performance monitor with disjoint_timer_query extension for webgl1 and webgl2
https://github.com/munsocket/gl-bench/tree/36f8dff21891543cf94ba496395ab8e9f4815121

All was ok on my machine but when I created a post in discorse I realised that it not working almost for everyone in WebGL1 due to memory exploit. So I ended up with another implementation that show CPU/GPU load in percent.

@Mugen87
Copy link
Collaborator

Mugen87 commented Jun 1, 2021

@gkjohnson When generating the js version of GPUStatsPanel, the following warning occurs:

5:30 warning 'Stats' is not defined no-undef

Probably best to add // eslint-disable-line no-undef at the class definition. What do you think?

@marcofugaro
Copy link
Contributor

@Mugen87 or we could add Stats to the list globals here in the eslintConfig:

three.js/package.json

Lines 41 to 47 in a3147d2

"globals": {
"potpack": true,
"fflate": true,
"ZSTDDecoder": true,
"bodymovin": true,
"OIMO": true
},

@Mugen87
Copy link
Collaborator

Mugen87 commented Jun 1, 2021

Sounds good! Let's go for this option.

@Mugen87
Copy link
Collaborator

Mugen87 commented Jun 1, 2021

a7f8e88

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.

7 participants