Performance.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import { Tab } from '../ui/Tab.js';
  2. import { List } from '../ui/List.js';
  3. import { Graph } from '../ui/Graph.js';
  4. import { Item } from '../ui/Item.js';
  5. import { createValueSpan, setText } from '../ui/utils.js';
  6. class Performance extends Tab {
  7. constructor( options = {} ) {
  8. super( 'Performance', options );
  9. const perfList = new List( 'Name', 'CPU', 'GPU', 'Total' );
  10. perfList.setGridStyle( 'minmax(200px, 2fr) 80px 80px 80px' );
  11. perfList.domElement.style.minWidth = '600px';
  12. const scrollWrapper = document.createElement( 'div' );
  13. scrollWrapper.className = 'list-scroll-wrapper';
  14. scrollWrapper.appendChild( perfList.domElement );
  15. this.content.appendChild( scrollWrapper );
  16. //
  17. const graphContainer = document.createElement( 'div' );
  18. graphContainer.className = 'graph-container';
  19. const graph = new Graph();
  20. graph.addLine( 'fps', 'var( --color-fps )' );
  21. graph.addLine( 'cpu', 'var( --color-yellow )' );
  22. graph.addLine( 'gpu', 'var( --color-green )' );
  23. graphContainer.append( graph.domElement );
  24. //
  25. /*
  26. const label = document.createElement( 'label' );
  27. label.className = 'custom-checkbox';
  28. const checkbox = document.createElement( 'input' );
  29. checkbox.type = 'checkbox';
  30. const checkmark = document.createElement( 'span' );
  31. checkmark.className = 'checkmark';
  32. label.appendChild( checkbox );
  33. label.appendChild( checkmark );
  34. */
  35. this.graphFpsCounter = createValueSpan();
  36. const graphStats = new Item( 'Graph Stats', createValueSpan(), createValueSpan(), this.graphFpsCounter );
  37. perfList.add( graphStats );
  38. const graphItem = new Item( graphContainer );
  39. graphItem.itemRow.childNodes[ 0 ].style.gridColumn = '1 / -1';
  40. graphStats.add( graphItem );
  41. //
  42. const frameStats = new Item( 'Frame Stats', createValueSpan(), createValueSpan(), createValueSpan() );
  43. perfList.add( frameStats );
  44. const miscellaneous = new Item( 'Miscellaneous & Idle', createValueSpan(), createValueSpan(), createValueSpan() );
  45. miscellaneous.domElement.firstChild.style.backgroundColor = '#00ff0b1a';
  46. miscellaneous.domElement.firstChild.classList.add( 'no-hover' );
  47. frameStats.add( miscellaneous );
  48. //
  49. this.notInUse = new Map();
  50. this.frameStats = frameStats;
  51. this.graphStats = graphStats;
  52. this.graph = graph;
  53. this.miscellaneous = miscellaneous;
  54. //
  55. this.currentRender = null;
  56. this.currentItem = null;
  57. this.frameItems = new Map();
  58. }
  59. resolveStats( inspector, stats ) {
  60. const data = inspector.getStatsData( stats.cid );
  61. let item = data.item;
  62. if ( item === undefined ) {
  63. item = new Item( createValueSpan(), createValueSpan(), createValueSpan(), createValueSpan() );
  64. if ( stats.name ) {
  65. if ( stats.isComputeStats === true ) {
  66. stats.name = `${ stats.name } [ Compute ]`;
  67. }
  68. } else {
  69. stats.name = `Unnamed ${ stats.cid }`;
  70. }
  71. item.userData.name = stats.name;
  72. this.currentItem.add( item );
  73. data.item = item;
  74. } else {
  75. item.userData.name = stats.name;
  76. if ( this.notInUse.has( stats.cid ) ) {
  77. item.domElement.firstElementChild.classList.remove( 'alert' );
  78. this.notInUse.delete( stats.cid );
  79. }
  80. const statsIndex = stats.parent.children.indexOf( stats );
  81. if ( item.parent === null || item.parent.children.indexOf( item ) !== statsIndex ) {
  82. this.currentItem.add( item, statsIndex );
  83. }
  84. }
  85. let name = item.userData.name;
  86. if ( stats.isComputeStats ) {
  87. name += ' [ Compute ]';
  88. }
  89. setText( item.data[ 0 ], name );
  90. setText( item.data[ 1 ], data.cpu.toFixed( 2 ) );
  91. setText( item.data[ 2 ], stats.gpuNotAvailable === true ? '-' : data.gpu.toFixed( 2 ) );
  92. setText( item.data[ 3 ], data.total.toFixed( 2 ) );
  93. //
  94. const previousItem = this.currentItem;
  95. this.currentItem = item;
  96. for ( const child of stats.children ) {
  97. this.resolveStats( inspector, child );
  98. }
  99. this.currentItem = previousItem;
  100. this.frameItems.set( stats.cid, item );
  101. }
  102. updateGraph( inspector, frame ) {
  103. const fps = inspector.fps;
  104. this.graph.addPoint( 'fps', fps );
  105. if ( frame ) {
  106. const cpuValue = Math.min( ( ( frame.cpu || 0 ) * fps ) / 1000, 1.0 ) * fps;
  107. const gpuValue = Math.min( ( ( frame.gpu || 0 ) * fps ) / 1000, 1.0 ) * fps;
  108. this.graph.addPoint( 'cpu', cpuValue );
  109. this.graph.addPoint( 'gpu', gpuValue );
  110. }
  111. this.graph.update();
  112. }
  113. addNotInUse( cid, item ) {
  114. item.domElement.firstElementChild.classList.add( 'alert' );
  115. this.notInUse.set( cid, {
  116. item,
  117. time: performance.now()
  118. } );
  119. this.updateNotInUse( cid );
  120. }
  121. updateNotInUse( cid ) {
  122. const { item, time } = this.notInUse.get( cid );
  123. const current = performance.now();
  124. const duration = 5;
  125. const remaining = duration - Math.floor( ( current - time ) / 1000 );
  126. if ( remaining >= 0 ) {
  127. const counter = '*'.repeat( Math.max( 0, remaining ) );
  128. const element = item.domElement.querySelector( '.list-item-cell .value' );
  129. setText( element, item.userData.name + ' (not in use) ' + counter );
  130. } else {
  131. item.domElement.firstElementChild.classList.remove( 'alert' );
  132. item.parent.remove( item );
  133. this.notInUse.delete( cid );
  134. }
  135. }
  136. updateText( inspector, frame ) {
  137. const oldFrameItems = new Map( this.frameItems );
  138. this.frameItems.clear();
  139. this.currentItem = this.frameStats;
  140. for ( const child of frame.children ) {
  141. this.resolveStats( inspector, child );
  142. }
  143. // remove unused frame items
  144. for ( const [ cid, item ] of oldFrameItems ) {
  145. if ( ! this.frameItems.has( cid ) ) {
  146. this.addNotInUse( cid, item );
  147. oldFrameItems.delete( cid );
  148. }
  149. }
  150. // update not in use items
  151. for ( const cid of this.notInUse.keys() ) {
  152. this.updateNotInUse( cid );
  153. }
  154. //
  155. setText( this.graphFpsCounter, inspector.fps.toFixed() + ' FPS' );
  156. //
  157. setText( this.frameStats.data[ 1 ], frame.cpu.toFixed( 2 ) );
  158. setText( this.frameStats.data[ 2 ], inspector.getRenderer().backend.hasTimestamp ? frame.gpu.toFixed( 2 ) : '-' );
  159. setText( this.frameStats.data[ 3 ], frame.total.toFixed( 2 ) );
  160. //
  161. setText( this.miscellaneous.data[ 1 ], frame.miscellaneous.toFixed( 2 ) );
  162. setText( this.miscellaneous.data[ 2 ], '-' );
  163. setText( this.miscellaneous.data[ 3 ], frame.miscellaneous.toFixed( 2 ) );
  164. //
  165. this.currentItem = null;
  166. }
  167. }
  168. export { Performance };
粤ICP备19079148号