summaryrefslogtreecommitdiffstats
path: root/excalidraw-app/debug.ts
diff options
context:
space:
mode:
authorkj_sh6042026-03-15 16:19:35 -0400
committerkj_sh6042026-03-15 16:19:35 -0400
commitbfc2cec7d43eb8eaa46dd3f91084932381257059 (patch)
tree0857e3aac2cff922826d4871ff54536b26fad6fc /excalidraw-app/debug.ts
parent225db4a7805befe009fe055fc2ef5daedd6c04f9 (diff)
refactor: excalidraw-app/
Diffstat (limited to 'excalidraw-app/debug.ts')
-rw-r--r--excalidraw-app/debug.ts135
1 files changed, 135 insertions, 0 deletions
diff --git a/excalidraw-app/debug.ts b/excalidraw-app/debug.ts
new file mode 100644
index 0000000..38ba805
--- /dev/null
+++ b/excalidraw-app/debug.ts
@@ -0,0 +1,135 @@
+declare global {
+ interface Window {
+ debug: typeof Debug;
+ }
+}
+
+const lessPrecise = (num: number, precision = 5) =>
+ parseFloat(num.toPrecision(precision));
+
+const getAvgFrameTime = (times: number[]) =>
+ lessPrecise(times.reduce((a, b) => a + b) / times.length);
+
+const getFps = (frametime: number) => lessPrecise(1000 / frametime);
+
+export class Debug {
+ public static DEBUG_LOG_TIMES = true;
+
+ private static TIMES_AGGR: Record<string, { t: number; times: number[] }> =
+ {};
+ private static TIMES_AVG: Record<
+ string,
+ { t: number; times: number[]; avg: number | null }
+ > = {};
+ private static LAST_DEBUG_LOG_CALL = 0;
+ private static DEBUG_LOG_INTERVAL_ID: null | number = null;
+
+ private static setupInterval = () => {
+ if (Debug.DEBUG_LOG_INTERVAL_ID === null) {
+ console.info("%c(starting perf recording)", "color: lime");
+ Debug.DEBUG_LOG_INTERVAL_ID = window.setInterval(Debug.debugLogger, 1000);
+ }
+ Debug.LAST_DEBUG_LOG_CALL = Date.now();
+ };
+
+ private static debugLogger = () => {
+ if (
+ Date.now() - Debug.LAST_DEBUG_LOG_CALL > 600 &&
+ Debug.DEBUG_LOG_INTERVAL_ID !== null
+ ) {
+ window.clearInterval(Debug.DEBUG_LOG_INTERVAL_ID);
+ Debug.DEBUG_LOG_INTERVAL_ID = null;
+ for (const [name, { avg }] of Object.entries(Debug.TIMES_AVG)) {
+ if (avg != null) {
+ console.info(
+ `%c${name} run avg: ${avg}ms (${getFps(avg)} fps)`,
+ "color: blue",
+ );
+ }
+ }
+ console.info("%c(stopping perf recording)", "color: red");
+ Debug.TIMES_AGGR = {};
+ Debug.TIMES_AVG = {};
+ return;
+ }
+ if (Debug.DEBUG_LOG_TIMES) {
+ for (const [name, { t, times }] of Object.entries(Debug.TIMES_AGGR)) {
+ if (times.length) {
+ console.info(
+ name,
+ lessPrecise(times.reduce((a, b) => a + b)),
+ times.sort((a, b) => a - b).map((x) => lessPrecise(x)),
+ );
+ Debug.TIMES_AGGR[name] = { t, times: [] };
+ }
+ }
+ for (const [name, { t, times, avg }] of Object.entries(Debug.TIMES_AVG)) {
+ if (times.length) {
+ const avgFrameTime = getAvgFrameTime(times);
+ console.info(name, `${avgFrameTime}ms (${getFps(avgFrameTime)} fps)`);
+ Debug.TIMES_AVG[name] = {
+ t,
+ times: [],
+ avg:
+ avg != null ? getAvgFrameTime([avg, avgFrameTime]) : avgFrameTime,
+ };
+ }
+ }
+ }
+ };
+
+ public static logTime = (time?: number, name = "default") => {
+ Debug.setupInterval();
+ const now = performance.now();
+ const { t, times } = (Debug.TIMES_AGGR[name] = Debug.TIMES_AGGR[name] || {
+ t: 0,
+ times: [],
+ });
+ if (t) {
+ times.push(time != null ? time : now - t);
+ }
+ Debug.TIMES_AGGR[name].t = now;
+ };
+ public static logTimeAverage = (time?: number, name = "default") => {
+ Debug.setupInterval();
+ const now = performance.now();
+ const { t, times } = (Debug.TIMES_AVG[name] = Debug.TIMES_AVG[name] || {
+ t: 0,
+ times: [],
+ });
+ if (t) {
+ times.push(time != null ? time : now - t);
+ }
+ Debug.TIMES_AVG[name].t = now;
+ };
+
+ private static logWrapper =
+ (type: "logTime" | "logTimeAverage") =>
+ <T extends any[], R>(fn: (...args: T) => R, name = "default") => {
+ return (...args: T) => {
+ const t0 = performance.now();
+ const ret = fn(...args);
+ Debug.logTime(performance.now() - t0, name);
+ return ret;
+ };
+ };
+
+ public static logTimeWrap = Debug.logWrapper("logTime");
+ public static logTimeAverageWrap = Debug.logWrapper("logTimeAverage");
+
+ public static perfWrap = <T extends any[], R>(
+ fn: (...args: T) => R,
+ name = "default",
+ ) => {
+ return (...args: T) => {
+ // eslint-disable-next-line no-console
+ console.time(name);
+ const ret = fn(...args);
+ // eslint-disable-next-line no-console
+ console.timeEnd(name);
+ return ret;
+ };
+ };
+}
+//@ts-ignore
+window.debug = Debug;