tarinai/js/perf_profiler.js
2026-06-25 14:51:58 +09:00

115 lines
2.8 KiB
JavaScript

"use strict";
(function (global) {
const nowMs = () => (global.performance?.now ? global.performance.now() : Date.now());
const buckets = new Map();
let frameOpen = null;
let frameCount = 0;
let lastQualityCheckAt = 0;
let tier = "high";
let dprScale = 1;
let resizeRequested = false;
function bucketFor(label) {
let b = buckets.get(label);
if (!b) {
b = { label, ms: 0, avg: 0, max: 0, calls: 0, last: 0 };
buckets.set(label, b);
}
return b;
}
function begin(label) {
const start = nowMs();
return () => {
const elapsed = Math.max(0, nowMs() - start);
const b = bucketFor(label);
b.ms += elapsed;
b.calls += 1;
b.last = elapsed;
if (elapsed > b.max) b.max = elapsed;
return elapsed;
};
}
function beginFrame() {
frameOpen = nowMs();
return frameOpen;
}
function smoothBuckets() {
for (const b of buckets.values()) {
b.avg = b.avg ? b.avg * 0.82 + b.ms * 0.18 : b.ms;
b.ms = 0;
b.calls = 0;
}
}
function qualityFromFps(fps) {
const f = Number(fps) || 0;
if (f > 55) return { tier: "high", dpr: 1 };
if (f < 32) return { tier: "low", dpr: 0.72 };
if (f < 45) return { tier: "mid", dpr: 0.86 };
return { tier: tier === "low" ? "mid" : tier, dpr: tier === "low" ? 0.86 : dprScale };
}
function endFrame(rawDt = 0, fps = global.__tarinaiFps || 0) {
frameCount += 1;
if (frameOpen != null) {
const b = bucketFor("frame.total");
const elapsed = Math.max(0, nowMs() - frameOpen);
b.ms += elapsed;
b.calls += 1;
b.last = elapsed;
if (elapsed > b.max) b.max = elapsed;
frameOpen = null;
}
const t = nowMs();
if (t - lastQualityCheckAt >= 750) {
lastQualityCheckAt = t;
smoothBuckets();
const next = qualityFromFps(fps);
const nextDpr = next.dpr;
if (next.tier !== tier || Math.abs(nextDpr - dprScale) > 0.04) {
tier = next.tier;
dprScale = nextDpr;
resizeRequested = true;
}
}
}
function renderQualityTier() {
return tier;
}
function dprScaleValue() {
return dprScale;
}
function consumeResizeRequest() {
const v = resizeRequested;
resizeRequested = false;
return v;
}
function snapshot() {
const entries = [...buckets.values()].map(b => ({
label: b.label,
avg: Number((b.avg || 0).toFixed(2)),
max: Number((b.max || 0).toFixed(2)),
last: Number((b.last || 0).toFixed(2)),
calls: b.calls || 0,
})).sort((a, b) => b.avg - a.avg);
return { tier, dprScale, frameCount, entries };
}
global.TarinaiPerf = Object.freeze({
begin,
beginFrame,
endFrame,
renderQualityTier,
dprScale: dprScaleValue,
consumeResizeRequest,
snapshot,
});
})(typeof window !== "undefined" ? window : globalThis);