104 lines
2.5 KiB
TypeScript
104 lines
2.5 KiB
TypeScript
import type { CanvasRenderingContext2D } from "@napi-rs/canvas";
|
|
|
|
import type { Layout } from "./layout";
|
|
|
|
export type DebugStats = Record<string, string | number | boolean | undefined>;
|
|
|
|
export type DebugHudOptions = {
|
|
showGlobal: boolean;
|
|
showRenderer: boolean;
|
|
showCustom?: boolean;
|
|
};
|
|
|
|
export type DebugHudData = {
|
|
global: DebugStats | string[];
|
|
renderer: {
|
|
id: string;
|
|
label: string;
|
|
stats: DebugStats;
|
|
fps: number;
|
|
};
|
|
custom?: DebugStats;
|
|
};
|
|
|
|
export type DebugHud = {
|
|
draw: (ctx: CanvasRenderingContext2D, layout: Layout, data: DebugHudData) => void;
|
|
};
|
|
|
|
export function createDebugHud(options: DebugHudOptions): DebugHud {
|
|
const padding = 8;
|
|
const lineHeight = 16;
|
|
const bg = "rgba(0, 0, 0, 0.65)";
|
|
const fg = "yellow"; // global
|
|
const rendererFg = "#ff66cc"; // renderer
|
|
const customFg = "#00c6ff"; // custom
|
|
|
|
const drawBlock = (
|
|
ctx: CanvasRenderingContext2D,
|
|
x: number,
|
|
y: number,
|
|
title: string,
|
|
stats: DebugStats | string[],
|
|
color: string
|
|
): { width: number; height: number } => {
|
|
let lines: string[] = [];
|
|
if (Array.isArray(stats)) {
|
|
lines = stats
|
|
} else {
|
|
const keys = Object.keys(stats);
|
|
lines = [title, ...keys.map((k) => `${k}: ${String(stats[k])}`)];
|
|
}
|
|
ctx.font = "14px \"JetBrains Mono\", monospace";
|
|
const textWidth = Math.max(...lines.map((l) => ctx.measureText(l).width));
|
|
const height = lines.length * lineHeight + padding * 2;
|
|
const width = textWidth + padding * 2;
|
|
|
|
ctx.save();
|
|
ctx.fillStyle = bg;
|
|
ctx.fillRect(x, y, width, height);
|
|
ctx.fillStyle = color;
|
|
ctx.textBaseline = "top";
|
|
lines.forEach((line, i) => {
|
|
ctx.fillText(line, x + padding, y + padding + i * lineHeight);
|
|
});
|
|
ctx.restore();
|
|
|
|
return { width, height };
|
|
};
|
|
|
|
const draw = (ctx: CanvasRenderingContext2D, layout: Layout, data: DebugHudData) => {
|
|
if (!options.showGlobal && !options.showRenderer && !options.showCustom) return;
|
|
ctx.save();
|
|
ctx.imageSmoothingEnabled = false;
|
|
ctx.globalAlpha = 0.9;
|
|
|
|
let cursorY = padding;
|
|
const originX = padding;
|
|
|
|
if (options.showGlobal) {
|
|
const { height } = drawBlock(ctx, originX, cursorY, "Global", data.global, fg);
|
|
cursorY += height + padding;
|
|
}
|
|
|
|
if (options.showRenderer) {
|
|
const { height } = drawBlock(
|
|
ctx,
|
|
originX,
|
|
cursorY,
|
|
`Renderer: ${data.renderer.label}`,
|
|
{ fps: data.renderer.fps.toFixed(2), ...data.renderer.stats },
|
|
rendererFg
|
|
);
|
|
cursorY += height + padding;
|
|
}
|
|
|
|
if (options.showCustom && data.custom) {
|
|
drawBlock(ctx, originX, cursorY, "Custom", data.custom, customFg);
|
|
}
|
|
|
|
ctx.restore();
|
|
};
|
|
|
|
return { draw };
|
|
}
|