git-subtree-dir: apps/viewer-3d git-subtree-mainline:5e10bb1be4git-subtree-split:a71790070d
59 lines
1.6 KiB
TypeScript
59 lines
1.6 KiB
TypeScript
/**
|
|
* On-screen FPS meter.
|
|
*
|
|
* Driven from the viewer's animate loop: call `sample(now)` once per frame with
|
|
* the requestAnimationFrame timestamp. It computes an instantaneous fps from the
|
|
* inter-frame delta, smooths it into a rolling average (EMA), and throttles DOM
|
|
* writes to ~every 250ms so updating the overlay doesn't itself cause jank.
|
|
* Kept dependency-free in the src/ui/progress.ts style.
|
|
*/
|
|
|
|
const DOM_INTERVAL_MS = 250;
|
|
// EMA smoothing factor: higher = more responsive, lower = smoother.
|
|
const EMA_ALPHA = 0.1;
|
|
|
|
export interface FpsMeter {
|
|
/** Overlay element; the viewer appends this to its container. */
|
|
el: HTMLElement;
|
|
/** Feed one frame timestamp (DOMHighResTimeStamp). */
|
|
sample(now: number): void;
|
|
/** Current rolling-average fps. */
|
|
fps(): number;
|
|
}
|
|
|
|
/** Create an FPS overlay element + sampler. */
|
|
export function createFpsMeter(): FpsMeter {
|
|
const el = document.createElement("div");
|
|
el.className = "fps";
|
|
el.textContent = "-- FPS";
|
|
|
|
let last = 0;
|
|
let avg = 0;
|
|
let lastDom = 0;
|
|
|
|
return {
|
|
el,
|
|
sample(now: number): void {
|
|
if (last === 0) {
|
|
last = now;
|
|
return;
|
|
}
|
|
const dt = now - last;
|
|
last = now;
|
|
if (dt <= 0) return;
|
|
const inst = 1000 / dt;
|
|
avg = avg === 0 ? inst : avg + EMA_ALPHA * (inst - avg);
|
|
|
|
if (now - lastDom >= DOM_INTERVAL_MS) {
|
|
lastDom = now;
|
|
const v = Math.round(avg);
|
|
el.textContent = `${v} FPS`;
|
|
el.style.color = v >= 60 ? "#5cff5c" : v >= 30 ? "#ffb74a" : "#ff5c5c";
|
|
}
|
|
},
|
|
fps(): number {
|
|
return avg;
|
|
},
|
|
};
|
|
}
|