StatsTab.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. module INSPECTOR {
  2. export class StatsTab extends Tab {
  3. private _inspector: Inspector;
  4. /**
  5. * Properties in this array will be updated
  6. * in a render loop - Mostly stats properties
  7. */
  8. private _updatableProperties: Array<{ elem: HTMLElement, updateFct: () => string }> = [];
  9. private _scene: BABYLON.Scene;
  10. private _engine: BABYLON.Engine;
  11. private _glInfo: any;
  12. private _updateLoopHandler: any;
  13. private _sceneInstrumentation: BABYLON.Nullable<BABYLON.SceneInstrumentation>;
  14. private _engineInstrumentation: BABYLON.Nullable<BABYLON.EngineInstrumentation>;
  15. private _connectToInstrumentation() {
  16. if (this._sceneInstrumentation) {
  17. return;
  18. }
  19. this._sceneInstrumentation = new BABYLON.SceneInstrumentation(this._scene);
  20. this._sceneInstrumentation.captureActiveMeshesEvaluationTime = true;
  21. this._sceneInstrumentation.captureRenderTargetsRenderTime = true;
  22. this._sceneInstrumentation.captureFrameTime = true;
  23. this._sceneInstrumentation.captureRenderTime = true;
  24. this._sceneInstrumentation.captureInterFrameTime = true;
  25. this._sceneInstrumentation.captureParticlesRenderTime = true;
  26. this._sceneInstrumentation.captureSpritesRenderTime = true;
  27. this._sceneInstrumentation.capturePhysicsTime = true;
  28. this._sceneInstrumentation.captureAnimationsTime = true;
  29. this._engineInstrumentation = new BABYLON.EngineInstrumentation(this._engine);
  30. this._engineInstrumentation.captureGPUFrameTime = true;
  31. }
  32. constructor(tabbar: TabBar, insp: Inspector) {
  33. super(tabbar, 'Stats');
  34. this._inspector = insp;
  35. this._scene = this._inspector.scene;
  36. this._engine = this._scene.getEngine();
  37. this._glInfo = this._engine.getGlInfo();
  38. this._connectToInstrumentation();
  39. // Build the stats panel: a div that will contains all stats
  40. this._panel = Helpers.CreateDiv('tab-panel') as HTMLDivElement;
  41. this._panel.classList.add("stats-panel")
  42. let title = Helpers.CreateDiv('stat-title1', this._panel);
  43. let fpsSpan = Helpers.CreateElement('span', 'stats-fps');
  44. this._updatableProperties.push({
  45. elem: fpsSpan,
  46. updateFct: () => { return BABYLON.Tools.Format(this._inspector.scene.getEngine().getFps(), 0) + " fps" }
  47. });
  48. let versionSpan = Helpers.CreateElement('span');
  49. versionSpan.textContent = `Babylon.js v${BABYLON.Engine.Version} - `;
  50. title.appendChild(versionSpan);
  51. title.appendChild(fpsSpan);
  52. this._updateLoopHandler = this._update.bind(this);
  53. // Count block
  54. title = Helpers.CreateDiv('stat-title2', this._panel);
  55. title.textContent = "Count";
  56. {
  57. this._createStatLabel("Total meshes", this._panel);
  58. let elemValue = Helpers.CreateDiv('stat-value', this._panel);
  59. this._updatableProperties.push({
  60. elem: elemValue,
  61. updateFct: () => { return this._scene.meshes.length.toString() }
  62. });
  63. this._createStatLabel("Draw calls", this._panel);
  64. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  65. this._updatableProperties.push({
  66. elem: elemValue,
  67. updateFct: () => { return this._sceneInstrumentation!.drawCallsCounter.current.toString() }
  68. });
  69. this._createStatLabel("Total lights", this._panel);
  70. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  71. this._updatableProperties.push({
  72. elem: elemValue,
  73. updateFct: () => { return this._scene.lights.length.toString() }
  74. });
  75. this._createStatLabel("Total vertices", this._panel);
  76. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  77. this._updatableProperties.push({
  78. elem: elemValue,
  79. updateFct: () => { return this._scene.getTotalVertices().toString() }
  80. });
  81. this._createStatLabel("Total materials", this._panel);
  82. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  83. this._updatableProperties.push({
  84. elem: elemValue,
  85. updateFct: () => { return this._scene.materials.length.toString() }
  86. });
  87. this._createStatLabel("Total textures", this._panel);
  88. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  89. this._updatableProperties.push({
  90. elem: elemValue,
  91. updateFct: () => { return this._scene.textures.length.toString() }
  92. });
  93. this._createStatLabel("Active meshes", this._panel);
  94. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  95. this._updatableProperties.push({
  96. elem: elemValue,
  97. updateFct: () => { return this._scene.getActiveMeshes().length.toString() }
  98. });
  99. this._createStatLabel("Active indices", this._panel);
  100. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  101. this._updatableProperties.push({
  102. elem: elemValue,
  103. updateFct: () => { return this._scene.getActiveIndices().toString() }
  104. });
  105. this._createStatLabel("Active bones", this._panel);
  106. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  107. this._updatableProperties.push({
  108. elem: elemValue,
  109. updateFct: () => { return this._scene.getActiveBones().toString() }
  110. });
  111. this._createStatLabel("Active particles", this._panel);
  112. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  113. this._updatableProperties.push({
  114. elem: elemValue,
  115. updateFct: () => { return this._scene.getActiveParticles().toString() }
  116. });
  117. }
  118. title = Helpers.CreateDiv('stat-title2', this._panel);
  119. title.textContent = "Duration";
  120. {
  121. this._createStatLabel("Meshes selection", this._panel);
  122. let elemValue = Helpers.CreateDiv('stat-value', this._panel);
  123. this._updatableProperties.push({
  124. elem: elemValue,
  125. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.activeMeshesEvaluationTimeCounter.current) }
  126. });
  127. this._createStatLabel("Render targets", this._panel);
  128. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  129. this._updatableProperties.push({
  130. elem: elemValue,
  131. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.renderTargetsRenderTimeCounter.current) }
  132. });
  133. this._createStatLabel("Particles", this._panel);
  134. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  135. this._updatableProperties.push({
  136. elem: elemValue,
  137. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.particlesRenderTimeCounter.current) }
  138. });
  139. this._createStatLabel("Sprites", this._panel);
  140. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  141. this._updatableProperties.push({
  142. elem: elemValue,
  143. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.spritesRenderTimeCounter.current) }
  144. });
  145. this._createStatLabel("Animations", this._panel);
  146. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  147. this._updatableProperties.push({
  148. elem: elemValue,
  149. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.animationsTimeCounter.current) }
  150. });
  151. this._createStatLabel("Physics", this._panel);
  152. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  153. this._updatableProperties.push({
  154. elem: elemValue,
  155. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.physicsTimeCounter.current) }
  156. });
  157. this._createStatLabel("Render", this._panel);
  158. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  159. this._updatableProperties.push({
  160. elem: elemValue,
  161. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.renderTimeCounter.current) }
  162. });
  163. this._createStatLabel("Frame", this._panel);
  164. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  165. this._updatableProperties.push({
  166. elem: elemValue,
  167. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.frameTimeCounter.current) }
  168. });
  169. this._createStatLabel("Inter-frame", this._panel);
  170. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  171. this._updatableProperties.push({
  172. elem: elemValue,
  173. updateFct: () => { return BABYLON.Tools.Format(this._sceneInstrumentation!.interFrameTimeCounter.current) }
  174. });
  175. this._createStatLabel("GPU Frame time", this._panel);
  176. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  177. this._updatableProperties.push({
  178. elem: elemValue,
  179. updateFct: () => { return BABYLON.Tools.Format(this._engineInstrumentation!.gpuFrameTimeCounter.current * 0.000001) }
  180. });
  181. this._createStatLabel("GPU Frame time (average)", this._panel);
  182. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  183. this._updatableProperties.push({
  184. elem: elemValue,
  185. updateFct: () => { return BABYLON.Tools.Format(this._engineInstrumentation!.gpuFrameTimeCounter.average * 0.000001) }
  186. });
  187. this._createStatLabel("Potential FPS", this._panel);
  188. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  189. this._updatableProperties.push({
  190. elem: elemValue,
  191. updateFct: () => { return BABYLON.Tools.Format(1000.0 / this._sceneInstrumentation!.frameTimeCounter.current, 0) }
  192. });
  193. this._createStatLabel("Resolution", this._panel);
  194. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  195. this._updatableProperties.push({
  196. elem: elemValue,
  197. updateFct: () => { return this._engine.getRenderWidth() + "x" + this._engine.getRenderHeight() }
  198. });
  199. }
  200. title = Helpers.CreateDiv('stat-title2', this._panel);
  201. title.textContent = "Extensions";
  202. {
  203. this._createStatLabel("Std derivatives", this._panel);
  204. let elemValue = Helpers.CreateDiv('stat-value', this._panel);
  205. this._updatableProperties.push({
  206. elem: elemValue,
  207. updateFct: () => { return (this._engine.getCaps().standardDerivatives ? "Yes" : "No") }
  208. });
  209. this._createStatLabel("Compressed textures", this._panel);
  210. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  211. this._updatableProperties.push({
  212. elem: elemValue,
  213. updateFct: () => { return (this._engine.getCaps().s3tc ? "Yes" : "No") }
  214. });
  215. this._createStatLabel("Hardware instances", this._panel);
  216. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  217. this._updatableProperties.push({
  218. elem: elemValue,
  219. updateFct: () => { return (this._engine.getCaps().instancedArrays ? "Yes" : "No") }
  220. });
  221. this._createStatLabel("Texture float", this._panel);
  222. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  223. this._updatableProperties.push({
  224. elem: elemValue,
  225. updateFct: () => { return (this._engine.getCaps().textureFloat ? "Yes" : "No") }
  226. });
  227. this._createStatLabel("32bits indices", this._panel);
  228. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  229. this._updatableProperties.push({
  230. elem: elemValue,
  231. updateFct: () => { return (this._engine.getCaps().uintIndices ? "Yes" : "No") }
  232. });
  233. this._createStatLabel("Fragment depth", this._panel);
  234. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  235. this._updatableProperties.push({
  236. elem: elemValue,
  237. updateFct: () => { return (this._engine.getCaps().fragmentDepthSupported ? "Yes" : "No") }
  238. });
  239. this._createStatLabel("High precision shaders", this._panel);
  240. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  241. this._updatableProperties.push({
  242. elem: elemValue,
  243. updateFct: () => { return (this._engine.getCaps().highPrecisionShaderSupported ? "Yes" : "No") }
  244. });
  245. this._createStatLabel("Draw buffers", this._panel);
  246. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  247. this._updatableProperties.push({
  248. elem: elemValue,
  249. updateFct: () => { return (this._engine.getCaps().drawBuffersExtension ? "Yes" : "No") }
  250. });
  251. this._createStatLabel("Vertex array object", this._panel);
  252. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  253. this._updatableProperties.push({
  254. elem: elemValue,
  255. updateFct: () => { return (this._engine.getCaps().vertexArrayObject ? "Yes" : "No") }
  256. });
  257. this._createStatLabel("Timer query", this._panel);
  258. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  259. this._updatableProperties.push({
  260. elem: elemValue,
  261. updateFct: () => { return (this._engine.getCaps().timerQuery ? "Yes" : "No") }
  262. });
  263. }
  264. title = Helpers.CreateDiv('stat-title2', this._panel);
  265. title.textContent = "Caps.";
  266. {
  267. this._createStatLabel("Stencil", this._panel);
  268. let elemValue = Helpers.CreateDiv('stat-value', this._panel);
  269. this._updatableProperties.push({
  270. elem: elemValue,
  271. updateFct: () => { return (this._engine.isStencilEnable ? "Enabled" : "Disabled") }
  272. });
  273. this._createStatLabel("Max textures units", this._panel);
  274. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  275. this._updatableProperties.push({
  276. elem: elemValue,
  277. updateFct: () => { return this._engine.getCaps().maxTexturesImageUnits.toString() }
  278. });
  279. this._createStatLabel("Max textures size", this._panel);
  280. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  281. this._updatableProperties.push({
  282. elem: elemValue,
  283. updateFct: () => { return this._engine.getCaps().maxTextureSize.toString() }
  284. });
  285. this._createStatLabel("Max anisotropy", this._panel);
  286. elemValue = Helpers.CreateDiv('stat-value', this._panel);
  287. this._updatableProperties.push({
  288. elem: elemValue,
  289. updateFct: () => { return this._engine.getCaps().maxAnisotropy.toString() }
  290. });
  291. }
  292. title = Helpers.CreateDiv('stat-title2', this._panel);
  293. title.textContent = "Info";
  294. {
  295. let elemValue = Helpers.CreateDiv('stat-infos', this._panel);
  296. this._updatableProperties.push({
  297. elem: elemValue,
  298. updateFct: () => { return "WebGL v" + this._engine.webGLVersion + " - " + this._glInfo.version + " - " + this._glInfo.renderer }
  299. });
  300. }
  301. }
  302. private _createStatLabel(content: string, parent: HTMLElement): HTMLElement {
  303. let elem = Helpers.CreateDiv('stat-label', parent);
  304. elem.textContent = content;
  305. return elem;
  306. }
  307. /** Update each properties of the stats panel */
  308. private _update() {
  309. for (let prop of this._updatableProperties) {
  310. prop.elem.textContent = prop.updateFct();
  311. }
  312. }
  313. public dispose() {
  314. this._scene.unregisterAfterRender(this._updateLoopHandler);
  315. this._sceneInstrumentation!.dispose();
  316. this._sceneInstrumentation = null;
  317. this._engineInstrumentation!.dispose();
  318. this._engineInstrumentation = null;
  319. }
  320. public active(b: boolean) {
  321. super.active(b);
  322. if (b) {
  323. this._connectToInstrumentation();
  324. this._scene.registerAfterRender(this._updateLoopHandler);
  325. }
  326. }
  327. }
  328. }