115 lines
2.8 KiB
JavaScript
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);
|