RendererInspector.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  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. if ( renderer.backend.hasTimestamp ) {
  115. await renderer.resolveTimestampsAsync( TimestampQuery.COMPUTE );
  116. await renderer.resolveTimestampsAsync( TimestampQuery.RENDER );
  117. const computeFrames = renderer.backend.getTimestampFrames( TimestampQuery.COMPUTE );
  118. const renderFrames = renderer.backend.getTimestampFrames( TimestampQuery.RENDER );
  119. const frameIds = [ ...new Set( [ ...computeFrames, ...renderFrames ] ) ];
  120. for ( const frameId of frameIds ) {
  121. const frame = this.getFrameById( frameId );
  122. if ( frame !== null ) {
  123. // resolve compute timestamps
  124. if ( frame.resolvedCompute === false ) {
  125. if ( frame.computes.length > 0 ) {
  126. if ( computeFrames.includes( frameId ) ) {
  127. for ( const stats of frame.computes ) {
  128. if ( renderer.backend.hasTimestampQuery( stats.uid ) ) {
  129. stats.gpu = renderer.backend.getTimestamp( stats.uid );
  130. } else {
  131. stats.gpu = 0;
  132. stats.gpuNotAvailable = true;
  133. }
  134. }
  135. frame.resolvedCompute = true;
  136. }
  137. } else {
  138. frame.resolvedCompute = true;
  139. }
  140. }
  141. // resolve render timestamps
  142. if ( frame.resolvedRender === false ) {
  143. if ( frame.renders.length > 0 ) {
  144. if ( renderFrames.includes( frameId ) ) {
  145. for ( const stats of frame.renders ) {
  146. if ( renderer.backend.hasTimestampQuery( stats.uid ) ) {
  147. stats.gpu = renderer.backend.getTimestamp( stats.uid );
  148. } else {
  149. stats.gpu = 0;
  150. stats.gpuNotAvailable = true;
  151. }
  152. }
  153. frame.resolvedRender = true;
  154. }
  155. } else {
  156. frame.resolvedRender = true;
  157. }
  158. }
  159. if ( frame.resolvedCompute === true && frame.resolvedRender === true ) {
  160. this.resolveFrame( frame );
  161. }
  162. }
  163. }
  164. } else {
  165. for ( const frame of this.frames ) {
  166. if ( frame.resolvedCompute === true && frame.resolvedRender === true ) {
  167. continue;
  168. }
  169. const nextFrame = this.getFrameById( frame.frameId + 1 );
  170. if ( nextFrame === null ) continue;
  171. if ( frame.resolvedCompute === false ) {
  172. for ( const stats of frame.computes ) {
  173. stats.gpu = 0;
  174. stats.gpuNotAvailable = true;
  175. }
  176. frame.resolvedCompute = true;
  177. }
  178. if ( frame.resolvedRender === false ) {
  179. for ( const stats of frame.renders ) {
  180. stats.gpu = 0;
  181. stats.gpuNotAvailable = true;
  182. }
  183. frame.resolvedRender = true;
  184. }
  185. if ( frame.resolvedCompute === true && frame.resolvedRender === true ) {
  186. this.resolveFrame( frame );
  187. }
  188. }
  189. }
  190. this._resolveTimestampPromise = null;
  191. resolve();
  192. } );
  193. } );
  194. return this._resolveTimestampPromise;
  195. }
  196. get isAvailable() {
  197. const renderer = this.getRenderer();
  198. return renderer !== null;
  199. }
  200. addFrame( frame ) {
  201. // Limit to max frames.
  202. if ( this.frames.length >= this.maxFrames ) {
  203. const removedFrame = this.frames.shift();
  204. delete this.framesLib[ removedFrame.frameId ];
  205. }
  206. this.frames.push( frame );
  207. this.framesLib[ frame.frameId ] = frame;
  208. if ( this.isAvailable ) {
  209. this.updateTabs();
  210. this.resolveTimestamp();
  211. }
  212. }
  213. inspect( node ) {
  214. const currentNodes = this.currentNodes;
  215. if ( currentNodes !== null ) {
  216. currentNodes.push( node );
  217. } else {
  218. warnOnce( 'RendererInspector: Unable to inspect node outside of frame scope. Use "renderer.setAnimationLoop()".' );
  219. }
  220. }
  221. beginCompute( uid, computeNode ) {
  222. const frame = this.getFrame();
  223. if ( ! frame ) return;
  224. const currentCompute = new ComputeStats( uid, computeNode );
  225. currentCompute.timestamp = performance.now();
  226. currentCompute.parent = this.currentCompute || this.getParent();
  227. frame.computes.push( currentCompute );
  228. if ( this.currentRender !== null ) {
  229. this.currentRender.children.push( currentCompute );
  230. } else {
  231. frame.children.push( currentCompute );
  232. }
  233. this.currentCompute = currentCompute;
  234. }
  235. finishCompute() {
  236. const frame = this.getFrame();
  237. if ( ! frame ) return;
  238. const currentCompute = this.currentCompute;
  239. currentCompute.cpu = performance.now() - currentCompute.timestamp;
  240. this.currentCompute = currentCompute.parent.isComputeStats ? currentCompute.parent : null;
  241. }
  242. beginRender( uid, scene, camera, renderTarget ) {
  243. const frame = this.getFrame();
  244. if ( ! frame ) return;
  245. const currentRender = new RenderStats( uid, scene, camera, renderTarget );
  246. currentRender.timestamp = performance.now();
  247. currentRender.parent = this.getParent();
  248. frame.renders.push( currentRender );
  249. if ( this.currentRender !== null ) {
  250. this.currentRender.children.push( currentRender );
  251. } else {
  252. frame.children.push( currentRender );
  253. }
  254. this.currentRender = currentRender;
  255. }
  256. finishRender() {
  257. const frame = this.getFrame();
  258. if ( ! frame ) return;
  259. const currentRender = this.currentRender;
  260. currentRender.cpu = performance.now() - currentRender.timestamp;
  261. this.currentRender = currentRender.parent;
  262. }
  263. }
粤ICP备19079148号