diff --git a/packages/stats/src/Core.ts b/packages/stats/src/Core.ts
index b7e8b8da..98989656 100644
--- a/packages/stats/src/Core.ts
+++ b/packages/stats/src/Core.ts
@@ -1,4 +1,5 @@
import DrawCallHook from "./hooks/DrawCallHook";
+import { RequestHook } from "./hooks/RequestHook";
import ShaderHook from "./hooks/ShaderHook";
import TextureHook from "./hooks/TextureHook";
@@ -16,6 +17,7 @@ export class Core {
private drawCallHook: DrawCallHook;
private textureHook: TextureHook;
private shaderHook: ShaderHook;
+ private requestHook: RequestHook;
private samplingFrames: number = 60;
private samplingIndex: number = 0;
private updateCounter: number = 0;
@@ -30,6 +32,7 @@ export class Core {
this.drawCallHook = new DrawCallHook(gl);
this.textureHook = new TextureHook(gl);
this.shaderHook = new ShaderHook(gl);
+ this.requestHook = new RequestHook();
}
/**
@@ -68,15 +71,21 @@ export class Core {
let data: PerformanceData = {
fps: Math.round((this.updateCounter * 1000) / (now - this.updateTime)),
- memory: performance.memory && (performance.memory.usedJSHeapSize / 1048576) >> 0,
+ memory:
+ performance.memory &&
+ (performance.memory.usedJSHeapSize / 1048576) >> 0,
drawCall: this.drawCallHook.drawCall,
triangles: this.drawCallHook.triangles,
lines: this.drawCallHook.lines,
points: this.drawCallHook.points,
textures: this.textureHook.textures,
+ size: this.requestHook.size,
shaders: this.shaderHook.shaders,
webglContext:
- window.hasOwnProperty("WebGL2RenderingContext") && this.gl instanceof WebGL2RenderingContext ? "2.0" : "1.0"
+ window.hasOwnProperty("WebGL2RenderingContext") &&
+ this.gl instanceof WebGL2RenderingContext
+ ? "2.0"
+ : "1.0",
};
this.reset();
@@ -97,5 +106,6 @@ interface PerformanceData {
points: number;
textures: number;
shaders: number;
+ size: string;
webglContext: string;
}
diff --git a/packages/stats/src/Monitor.ts b/packages/stats/src/Monitor.ts
index 80996cf7..dab79e3e 100644
--- a/packages/stats/src/Monitor.ts
+++ b/packages/stats/src/Monitor.ts
@@ -14,6 +14,8 @@ let tpl = `
0
Shaders
0
+ Network Size (MB)
+ 0
WebGL
@@ -62,7 +64,16 @@ export default class Monitor {
constructor(gl: WebGLRenderingContext | WebGL2RenderingContext) {
this.core = new Core(gl);
this.items = [];
- this.items = ["fps", "memory", "drawCall", "triangles", "textures", "shaders", "webglContext"];
+ this.items = [
+ "fps",
+ "memory",
+ "drawCall",
+ "triangles",
+ "textures",
+ "shaders",
+ "size",
+ "webglContext",
+ ];
this.createContainer();
this.update = this.update.bind(this);
}
diff --git a/packages/stats/src/hooks/RequestHook.ts b/packages/stats/src/hooks/RequestHook.ts
new file mode 100644
index 00000000..41cc133a
--- /dev/null
+++ b/packages/stats/src/hooks/RequestHook.ts
@@ -0,0 +1,90 @@
+let requestSize = 0;
+
+let originalSend = XMLHttpRequest.prototype.send;
+
+const cacheMap = new Map();
+function addRequestSize(url: string, size: number) {
+ if (cacheMap.get(url) == undefined) {
+ cacheMap.set(url, size);
+ console.log(`request(${size}): ${url}`);
+ requestSize += size;
+ }
+}
+
+XMLHttpRequest.prototype.send = function (body) {
+ this.addEventListener(
+ "load",
+ function () {
+ let size = 0;
+ if (this.responseType === "" || this.responseType === "text") {
+ size = new Blob([JSON.stringify(this.responseText)]).size;
+ } else if (this.response instanceof Blob) {
+ size = this.response.size;
+ } else if (this.response instanceof ArrayBuffer) {
+ size = this.response.byteLength;
+ } else if (this.responseType === "json") {
+ size = new Blob([JSON.stringify(this.response)]).size;
+ }
+
+ addRequestSize((this as XMLHttpRequest).responseURL, size);
+ },
+ false
+ );
+
+ originalSend.call(this, body);
+
+ var originalImageSrc = Object.getOwnPropertyDescriptor(
+ Image.prototype,
+ "src"
+ ).set;
+
+ this.originalImageSrc = originalImageSrc;
+
+ Object.defineProperty(Image.prototype, "src", {
+ set: function (value) {
+ fetch(value).then((response) => {
+ if (response.ok) {
+ response.blob().then((blob) => {
+ addRequestSize((this as XMLHttpRequest).responseURL, blob.size);
+ });
+ }
+ });
+ originalImageSrc.call(this, value);
+ },
+ });
+};
+
+export class RequestHook {
+ private _originalSend;
+ private _hooked = false;
+
+ get size() {
+ return formatNumber(requestSize / 1024 / 1024);
+ }
+
+ constructor() {
+ this._hooked = true;
+ }
+
+ public reset(): void {
+ requestSize = 0;
+ }
+
+ public release(): void {
+ if (this._hooked) {
+ XMLHttpRequest.prototype.send = this._originalSend;
+ Object.defineProperty(Image.prototype, "src", {
+ set: function (value) {
+ this.src.call(this, value);
+ },
+ });
+ }
+ this._hooked = false;
+ }
+}
+
+function formatNumber(num: number): string {
+ return Number(num).toFixed(
+ Math.max(6 - num.toString().split(".")[0].length, 0)
+ );
+}