2023-07-08 23:33:34 +02:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
2023-11-03 13:11:34 +01:00
|
|
|
//@ts-ignore
|
2023-07-08 23:33:34 +02:00
|
|
|
window.debug = Debug;
|