| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- import { InspectorBase, TimestampQuery, warnOnce } from 'three/webgpu';
- class ObjectStats {
- constructor( uid, name ) {
- this.uid = uid;
- this.cid = uid.match( /^(.*):f(\d+)$/ )[ 1 ]; // call id
- this.name = name;
- this.timestamp = 0;
- this.cpu = 0;
- this.gpu = 0;
- this.fps = 0;
- this.children = [];
- this.parent = null;
- }
- }
- class RenderStats extends ObjectStats {
- constructor( uid, scene, camera, renderTarget ) {
- let name = scene.name;
- if ( name === '' ) {
- if ( scene.isScene ) {
- name = 'Scene';
- } else if ( scene.isQuadMesh ) {
- name = 'QuadMesh';
- }
- }
- super( uid, name );
- this.scene = scene;
- this.camera = camera;
- this.renderTarget = renderTarget;
- this.isRenderStats = true;
- }
- }
- class ComputeStats extends ObjectStats {
- constructor( uid, computeNode ) {
- super( uid, computeNode.name );
- this.computeNode = computeNode;
- this.isComputeStats = true;
- }
- }
- export class RendererInspector extends InspectorBase {
- constructor() {
- super();
- this.currentFrame = null;
- this.currentRender = null;
- this.currentNodes = null;
- this.lastFrame = null;
- this.frames = [];
- this.framesLib = {};
- this.maxFrames = 512;
- this._lastFinishTime = 0;
- this._resolveTimestampPromise = null;
- this.isRendererInspector = true;
- }
- getParent() {
- return this.currentRender || this.getFrame();
- }
- begin() {
- this.currentFrame = this._createFrame();
- this.currentRender = this.currentFrame;
- this.currentNodes = [];
- }
- finish() {
- const now = performance.now();
- const frame = this.currentFrame;
- frame.finishTime = now;
- frame.deltaTime = now - ( this._lastFinishTime > 0 ? this._lastFinishTime : now );
- this.addFrame( frame );
- this.fps = this._getFPS();
- this.lastFrame = frame;
- this.currentFrame = null;
- this.currentRender = null;
- this.currentNodes = null;
- this._lastFinishTime = now;
- }
- _getFPS() {
- let frameSum = 0;
- let timeSum = 0;
- for ( let i = this.frames.length - 1; i >= 0; i -- ) {
- const frame = this.frames[ i ];
- frameSum ++;
- timeSum += frame.deltaTime;
- if ( timeSum >= 1000 ) break;
- }
- return ( frameSum * 1000 ) / timeSum;
- }
- _createFrame() {
- return {
- frameId: this.nodeFrame.frameId,
- resolvedCompute: false,
- resolvedRender: false,
- deltaTime: 0,
- startTime: performance.now(),
- finishTime: 0,
- miscellaneous: 0,
- children: [],
- renders: [],
- computes: []
- };
- }
- getFrame() {
- return this.currentFrame || this.lastFrame;
- }
- getFrameById( frameId ) {
- return this.framesLib[ frameId ] || null;
- }
- resolveViewer() { }
- resolveFrame( /*frame*/ ) { }
- async resolveTimestamp() {
- if ( this._resolveTimestampPromise !== null ) {
- return this._resolveTimestampPromise;
- }
- this._resolveTimestampPromise = new Promise( ( resolve ) => {
- requestAnimationFrame( async () => {
- const renderer = this.getRenderer();
- await renderer.resolveTimestampsAsync( TimestampQuery.COMPUTE );
- await renderer.resolveTimestampsAsync( TimestampQuery.RENDER );
- const computeFrames = renderer.backend.getTimestampFrames( TimestampQuery.COMPUTE );
- const renderFrames = renderer.backend.getTimestampFrames( TimestampQuery.RENDER );
- const frameIds = [ ...new Set( [ ...computeFrames, ...renderFrames ] ) ];
- for ( const frameId of frameIds ) {
- const frame = this.getFrameById( frameId );
- if ( frame !== null ) {
- // resolve compute timestamps
- if ( frame.resolvedCompute === false ) {
- if ( frame.computes.length > 0 ) {
- if ( computeFrames.includes( frameId ) ) {
- for ( const stats of frame.computes ) {
- if ( renderer.backend.hasTimestamp( stats.uid ) ) {
- stats.gpu = renderer.backend.getTimestamp( stats.uid );
- } else {
- stats.gpu = 0;
- stats.gpuNotAvailable = true;
- }
- }
- frame.resolvedCompute = true;
- }
- } else {
- frame.resolvedCompute = true;
- }
- }
- // resolve render timestamps
- if ( frame.resolvedRender === false ) {
- if ( frame.renders.length > 0 ) {
- if ( renderFrames.includes( frameId ) ) {
- for ( const stats of frame.renders ) {
- if ( renderer.backend.hasTimestamp( stats.uid ) ) {
- stats.gpu = renderer.backend.getTimestamp( stats.uid );
- } else {
- stats.gpu = 0;
- stats.gpuNotAvailable = true;
- }
- }
- frame.resolvedRender = true;
- }
- } else {
- frame.resolvedRender = true;
- }
- }
- if ( frame.resolvedCompute === true && frame.resolvedRender === true ) {
- this.resolveFrame( frame );
- }
- }
- }
- this._resolveTimestampPromise = null;
- resolve();
- } );
- } );
- return this._resolveTimestampPromise;
- }
- get isAvailable() {
- const renderer = this.getRenderer();
- return renderer !== null;
- }
- addFrame( frame ) {
- // Limit to max frames.
- if ( this.frames.length >= this.maxFrames ) {
- const removedFrame = this.frames.shift();
- delete this.framesLib[ removedFrame.frameId ];
- }
- this.frames.push( frame );
- this.framesLib[ frame.frameId ] = frame;
- if ( this.isAvailable ) {
- this.resolveViewer();
- this.resolveTimestamp();
- }
- }
- inspect( node ) {
- const currentNodes = this.currentNodes;
- if ( currentNodes !== null ) {
- currentNodes.push( node );
- } else {
- warnOnce( 'RendererInspector: Unable to inspect node outside of frame scope. Use "renderer.setAnimationLoop()".' );
- }
- }
- beginCompute( uid, computeNode ) {
- const frame = this.getFrame();
- if ( ! frame ) return;
- const currentCompute = new ComputeStats( uid, computeNode );
- currentCompute.timestamp = performance.now();
- currentCompute.parent = this.currentCompute || this.getParent();
- frame.computes.push( currentCompute );
- if ( this.currentRender !== null ) {
- this.currentRender.children.push( currentCompute );
- } else {
- frame.children.push( currentCompute );
- }
- this.currentCompute = currentCompute;
- }
- finishCompute() {
- const frame = this.getFrame();
- if ( ! frame ) return;
- const currentCompute = this.currentCompute;
- currentCompute.cpu = performance.now() - currentCompute.timestamp;
- this.currentCompute = currentCompute.parent.isComputeStats ? currentCompute.parent : null;
- }
- beginRender( uid, scene, camera, renderTarget ) {
- const frame = this.getFrame();
- if ( ! frame ) return;
- const currentRender = new RenderStats( uid, scene, camera, renderTarget );
- currentRender.timestamp = performance.now();
- currentRender.parent = this.getParent();
- frame.renders.push( currentRender );
- if ( this.currentRender !== null ) {
- this.currentRender.children.push( currentRender );
- } else {
- frame.children.push( currentRender );
- }
- this.currentRender = currentRender;
- }
- finishRender() {
- const frame = this.getFrame();
- if ( ! frame ) return;
- const currentRender = this.currentRender;
- currentRender.cpu = performance.now() - currentRender.timestamp;
- this.currentRender = currentRender.parent;
- }
- }
|