RendererInspector.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. import { InspectorBase, TimestampQuery, warnOnce } from 'three/webgpu';
  2. class ObjectStats {
  3. constructor( uid, name ) {
  4. this.uid = uid;
  5. this.cid = uid.match( /^(.*):f(\d+)$/ )[ 1 ]; // call id
  6. this.name = name;
  7. this.timestamp = 0;
  8. this.cpu = 0;
  9. this.gpu = 0;
  10. this.fps = 0;
  11. this.children = [];
  12. this.parent = null;
  13. }
  14. }
  15. class RenderStats extends ObjectStats {
  16. constructor( uid, scene, camera, renderTarget ) {
  17. let name = scene.name;
  18. if ( name === '' ) {
  19. if ( scene.isScene ) {
  20. name = 'Scene';
  21. } else if ( scene.isQuadMesh ) {
  22. name = 'QuadMesh';
  23. }
  24. }
  25. super( uid, name );
  26. this.scene = scene;
  27. this.camera = camera;
  28. this.renderTarget = renderTarget;
  29. this.isRenderStats = true;
  30. }
  31. }
  32. class ComputeStats extends ObjectStats {
  33. constructor( uid, computeNode ) {
  34. super( uid, computeNode.name );
  35. this.computeNode = computeNode;
  36. this.isComputeStats = true;
  37. }
  38. }
  39. export class RendererInspector extends InspectorBase {
  40. constructor() {
  41. super();
  42. this.currentFrame = null;
  43. this.currentRender = null;
  44. this.currentNodes = null;
  45. this.lastFrame = null;
  46. this.frames = [];
  47. this.framesLib = {};
  48. this.maxFrames = 512;
  49. this._lastFinishTime = 0;
  50. this._resolveTimestampPromise = null;
  51. this.isRendererInspector = true;
  52. }
  53. getParent() {
  54. return this.currentRender || this.getFrame();
  55. }
  56. begin() {
  57. this.currentFrame = this._createFrame();
  58. this.currentRender = this.currentFrame;
  59. this.currentNodes = [];
  60. }
  61. finish() {
  62. const now = performance.now();
  63. const frame = this.currentFrame;
  64. frame.finishTime = now;
  65. frame.deltaTime = now - ( this._lastFinishTime > 0 ? this._lastFinishTime : now );
  66. this.addFrame( frame );
  67. this.fps = this._getFPS();
  68. this.lastFrame = frame;
  69. this.currentFrame = null;
  70. this.currentRender = null;
  71. this.currentNodes = null;
  72. this._lastFinishTime = now;
  73. }
  74. _getFPS() {
  75. let frameSum = 0;
  76. let timeSum = 0;
  77. for ( let i = this.frames.length - 1; i >= 0; i -- ) {
  78. const frame = this.frames[ i ];
  79. frameSum ++;
  80. timeSum += frame.deltaTime;
  81. if ( timeSum >= 1000 ) break;
  82. }
  83. return ( frameSum * 1000 ) / timeSum;
  84. }
  85. _createFrame() {
  86. return {
  87. frameId: this.nodeFrame.frameId,
  88. resolvedCompute: false,
  89. resolvedRender: false,
  90. deltaTime: 0,
  91. startTime: performance.now(),
  92. finishTime: 0,
  93. miscellaneous: 0,
  94. children: [],
  95. renders: [],
  96. computes: []
  97. };
  98. }
  99. getFrame() {
  100. return this.currentFrame || this.lastFrame;
  101. }
  102. getFrameById( frameId ) {
  103. return this.framesLib[ frameId ] || null;
  104. }
  105. updateTabs() { }
  106. resolveFrame( /*frame*/ ) { }
  107. async resolveTimestamp() {
  108. if ( this._resolveTimestampPromise !== null ) {
  109. return this._resolveTimestampPromise;
  110. }
  111. this._resolveTimestampPromise = new Promise( ( resolve ) => {
  112. requestAnimationFrame( async () => {
  113. const renderer = this.getRenderer();
  114. await renderer.resolveTimestampsAsync( TimestampQuery.COMPUTE );
  115. await renderer.resolveTimestampsAsync( TimestampQuery.RENDER );
  116. const computeFrames = renderer.backend.getTimestampFrames( TimestampQuery.COMPUTE );
  117. const renderFrames = renderer.backend.getTimestampFrames( TimestampQuery.RENDER );
  118. const frameIds = [ ...new Set( [ ...computeFrames, ...renderFrames ] ) ];
  119. for ( const frameId of frameIds ) {
  120. const frame = this.getFrameById( frameId );
  121. if ( frame !== null ) {
  122. // resolve compute timestamps
  123. if ( frame.resolvedCompute === false ) {
  124. if ( frame.computes.length > 0 ) {
  125. if ( computeFrames.includes( frameId ) ) {
  126. for ( const stats of frame.computes ) {
  127. if ( renderer.backend.hasTimestamp( stats.uid ) ) {
  128. stats.gpu = renderer.backend.getTimestamp( stats.uid );
  129. } else {
  130. stats.gpu = 0;
  131. stats.gpuNotAvailable = true;
  132. }
  133. }
  134. frame.resolvedCompute = true;
  135. }
  136. } else {
  137. frame.resolvedCompute = true;
  138. }
  139. }
  140. // resolve render timestamps
  141. if ( frame.resolvedRender === false ) {
  142. if ( frame.renders.length > 0 ) {
  143. if ( renderFrames.includes( frameId ) ) {
  144. for ( const stats of frame.renders ) {
  145. if ( renderer.backend.hasTimestamp( stats.uid ) ) {
  146. stats.gpu = renderer.backend.getTimestamp( stats.uid );
  147. } else {
  148. stats.gpu = 0;
  149. stats.gpuNotAvailable = true;
  150. }
  151. }
  152. frame.resolvedRender = true;
  153. }
  154. } else {
  155. frame.resolvedRender = true;
  156. }
  157. }
  158. if ( frame.resolvedCompute === true && frame.resolvedRender === true ) {
  159. this.resolveFrame( frame );
  160. }
  161. }
  162. }
  163. this._resolveTimestampPromise = null;
  164. resolve();
  165. } );
  166. } );
  167. return this._resolveTimestampPromise;
  168. }
  169. get isAvailable() {
  170. const renderer = this.getRenderer();
  171. return renderer !== null;
  172. }
  173. addFrame( frame ) {
  174. // Limit to max frames.
  175. if ( this.frames.length >= this.maxFrames ) {
  176. const removedFrame = this.frames.shift();
  177. delete this.framesLib[ removedFrame.frameId ];
  178. }
  179. this.frames.push( frame );
  180. this.framesLib[ frame.frameId ] = frame;
  181. if ( this.isAvailable ) {
  182. this.updateTabs();
  183. this.resolveTimestamp();
  184. }
  185. }
  186. inspect( node ) {
  187. const currentNodes = this.currentNodes;
  188. if ( currentNodes !== null ) {
  189. currentNodes.push( node );
  190. } else {
  191. warnOnce( 'RendererInspector: Unable to inspect node outside of frame scope. Use "renderer.setAnimationLoop()".' );
  192. }
  193. }
  194. beginCompute( uid, computeNode ) {
  195. const frame = this.getFrame();
  196. if ( ! frame ) return;
  197. const currentCompute = new ComputeStats( uid, computeNode );
  198. currentCompute.timestamp = performance.now();
  199. currentCompute.parent = this.currentCompute || this.getParent();
  200. frame.computes.push( currentCompute );
  201. if ( this.currentRender !== null ) {
  202. this.currentRender.children.push( currentCompute );
  203. } else {
  204. frame.children.push( currentCompute );
  205. }
  206. this.currentCompute = currentCompute;
  207. }
  208. finishCompute() {
  209. const frame = this.getFrame();
  210. if ( ! frame ) return;
  211. const currentCompute = this.currentCompute;
  212. currentCompute.cpu = performance.now() - currentCompute.timestamp;
  213. this.currentCompute = currentCompute.parent.isComputeStats ? currentCompute.parent : null;
  214. }
  215. beginRender( uid, scene, camera, renderTarget ) {
  216. const frame = this.getFrame();
  217. if ( ! frame ) return;
  218. const currentRender = new RenderStats( uid, scene, camera, renderTarget );
  219. currentRender.timestamp = performance.now();
  220. currentRender.parent = this.getParent();
  221. frame.renders.push( currentRender );
  222. if ( this.currentRender !== null ) {
  223. this.currentRender.children.push( currentRender );
  224. } else {
  225. frame.children.push( currentRender );
  226. }
  227. this.currentRender = currentRender;
  228. }
  229. finishRender() {
  230. const frame = this.getFrame();
  231. if ( ! frame ) return;
  232. const currentRender = this.currentRender;
  233. currentRender.cpu = performance.now() - currentRender.timestamp;
  234. this.currentRender = currentRender.parent;
  235. }
  236. }
粤ICP备19079148号