Profiler: real-GPU breakdown by zoom, look and scene, with a baseline (t88 phase 0)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -80,6 +80,7 @@
|
||||
"ui/preview-selection.js",
|
||||
"ui/events.js",
|
||||
"ui/map.js",
|
||||
"ui/perf.js",
|
||||
"ui/loop.js"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ function smoothDraw(lighting, glowOn) {
|
||||
gl.viewport(0, 0, cw, ch);
|
||||
gl.useProgram(SM.prog);
|
||||
const bw = R.bw, bh = R.bh;
|
||||
const uploadStart = PERF_MARKS ? performance.now() : 0;
|
||||
smUpload('tCol', bw, bh, smBytes(R.px));
|
||||
smUpload('tColL', bw, bh, smBytes(R.px));
|
||||
smUpload('tBack', bw, bh, smBytes(R.backPx));
|
||||
@@ -71,6 +72,8 @@ function smoothDraw(lighting, glowOn) {
|
||||
smUpload('tField', bw, bh, smBytes(R.fieldPx));
|
||||
if (lighting && R.lightImg) smUpload('tLight', R.lw, R.lh, smBytes(R.lightImg.data)); else smUpload('tLight', 1, 1, SM.white);
|
||||
if (glowOn) smUpload('tGlow', R.glowC.width, R.glowC.height, smBytes(R.glowImg.data)); else smUpload('tGlow', 1, 1, SM.white);
|
||||
const blitStart = PERF_MARKS ? performance.now() : 0;
|
||||
if (PERF_MARKS) { perfMarks.upload = blitStart - uploadStart; perfMarks.prepare += uploadStart - t0; }
|
||||
gl.uniform2f(u.uSize, bw, bh);
|
||||
gl.uniform2f(u.uOrigin, R.vx0, R.vy0);
|
||||
gl.uniform4f(u.uView, R.camX - R.vx0, R.camY - R.vy0, R.zp * (ch / R.canvas.height), ch);
|
||||
@@ -85,6 +88,7 @@ function smoothDraw(lighting, glowOn) {
|
||||
const ctx = R.ctx;
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.drawImage(cv, 0, 0, cw, ch, 0, 0, R.canvas.width, R.canvas.height);
|
||||
if (PERF_MARKS) perfMarks.blit = performance.now() - blitStart;
|
||||
smoothPace(performance.now() - t0);
|
||||
}
|
||||
// Keep the frame rate up: drop the GPU pass's resolution when it is slow, raise it again when there
|
||||
|
||||
+16
-1
@@ -3,6 +3,8 @@ function fogColor(c, k) { return (c & 0xff000000) | (((c >>> 16 & 255) * k) << 1
|
||||
// drawWorld: the per-grain pixel buffer (pixel look), also the input of the smooth look.
|
||||
// ---------- World pixels ----------
|
||||
function drawWorld() {
|
||||
const perfStart = PERF_MARKS ? performance.now() : 0;
|
||||
if (PERF_MARKS) perfMarks.light = 0;
|
||||
const bw = R.bw, bh = R.bh, px = R.px;
|
||||
R.vx0 = Math.floor(R.camX); R.vy0 = Math.floor(R.camY);
|
||||
const vx0 = R.vx0, vy0 = R.vy0;
|
||||
@@ -31,7 +33,12 @@ function drawWorld() {
|
||||
const useFog = SET.fog;
|
||||
const OUT = sm ? 0x04000000 : 0xff000000; // unexplored and off the map: black
|
||||
const lighting = SET.lighting !== 'off' && !R.lens;
|
||||
if (lighting && (R.frame % SET.lightEvery === 0 || !R.lw)) computeLight();
|
||||
if (lighting && (R.frame % SET.lightEvery === 0 || !R.lw)) {
|
||||
const lightStart = PERF_MARKS ? performance.now() : 0;
|
||||
computeLight();
|
||||
if (PERF_MARKS) perfMarks.light = performance.now() - lightStart;
|
||||
}
|
||||
const colorStart = PERF_MARKS ? performance.now() : 0;
|
||||
const grain = SET.grain, incand = SET.incandescence;
|
||||
const anim = R.frame;
|
||||
const lens = R.lens, tl = THERM[SET.thermalPalette] || THERM.inferno;
|
||||
@@ -374,8 +381,15 @@ function drawWorld() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (PERF_MARKS) {
|
||||
perfMarks.color = performance.now() - colorStart;
|
||||
perfMarks.prepare = colorStart - perfStart - perfMarks.light;
|
||||
}
|
||||
if (sm) { smoothDraw(lighting, glowOn); return; }
|
||||
const uploadStart = PERF_MARKS ? performance.now() : 0;
|
||||
R.bctx.putImageData(R.img, 0, 0);
|
||||
const blitStart = PERF_MARKS ? performance.now() : 0;
|
||||
if (PERF_MARKS) perfMarks.upload = blitStart - uploadStart;
|
||||
const ctx = R.ctx, zp = R.zp;
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
const ox = -(R.camX - vx0) * zp, oy = -(R.camY - vy0) * zp;
|
||||
@@ -401,4 +415,5 @@ function drawWorld() {
|
||||
ctx.filter = 'none';
|
||||
ctx.restore();
|
||||
}
|
||||
if (PERF_MARKS) perfMarks.blit = performance.now() - blitStart;
|
||||
}
|
||||
|
||||
@@ -218,6 +218,7 @@ table.mt td.dip { color: var(--melt); }
|
||||
|
||||
#perf { position: fixed; right: max(16px, env(safe-area-inset-right, 0px)); bottom: calc(max(12px, env(safe-area-inset-bottom, 0px)) + 4px); font-family: var(--font-mono); font-size: 11px; color: var(--ash); pointer-events: none; text-align: right; white-space: pre; }
|
||||
#hud .br { bottom: max(12px, env(safe-area-inset-bottom, 0px)); }
|
||||
#perf:not(:empty) { background: var(--plate); color: var(--bone); padding: 6px 8px; border-radius: 4px; }
|
||||
#perf:not(:empty) ~ .br { }
|
||||
|
||||
/* ---------- modals ---------- */
|
||||
|
||||
+11
-3
@@ -19,6 +19,7 @@ function frame(now) {
|
||||
if (GAME.loading) { acc = 0; return; }
|
||||
if (!GAME.running) { pollPad(); return; }
|
||||
const t0 = performance.now();
|
||||
let frameSim = 0, frameSteps = 0;
|
||||
const pad = pollPad();
|
||||
if (GAME.usingPad) { GAME.aim.x = bot.x + bot.w / 2 + GAME.padAim.x; GAME.aim.y = bot.y + bot.h / 2 + GAME.padAim.y; }
|
||||
else { const w = screenToWorld(GAME.mouse.x, GAME.mouse.y); GAME.aim.x = w.x; GAME.aim.y = w.y; }
|
||||
@@ -37,7 +38,8 @@ function frame(now) {
|
||||
GAME.padRun = pm > 0.92 ? GAME.padRun + 1 : 0;
|
||||
botStep(ix, iy, keys.has('Shift') || GAME.padRun > 24);
|
||||
toolTick();
|
||||
const s0 = performance.now(); simTick(); simMs = simMs * 0.9 + (performance.now() - s0) * 0.1;
|
||||
const s0 = performance.now(); simTick(); const elapsed = performance.now() - s0;
|
||||
simMs = simMs * 0.9 + elapsed * 0.1; frameSim += elapsed; frameSteps++;
|
||||
if (devPaused) devSteps--; else acc -= step;
|
||||
steps++;
|
||||
GAME.playTime += step;
|
||||
@@ -56,7 +58,10 @@ function frame(now) {
|
||||
if (!GAME.drag || Math.hypot(bot.vx, bot.vy) > 0.08) updateCamera(dt, lookX, lookY);
|
||||
{ const v = viewCells(); setBuildRange(R.camX + v.w / 2, R.camY + v.h / 2, v.w, v.h); }
|
||||
R.frame++;
|
||||
if (SET.showPerf) perfLast.computeLight = 0;
|
||||
drawWorld();
|
||||
if (SET.showPerf) perfTimes.computeLight = (perfTimes.computeLight || 0) * 0.9 + perfLast.computeLight * 0.1;
|
||||
const overlayStart = SET.showPerf ? performance.now() : 0;
|
||||
drawCracks();
|
||||
if (!weather.seen && (R.rain || 0) > 0.12) {
|
||||
weather.seen = true;
|
||||
@@ -81,6 +86,7 @@ function frame(now) {
|
||||
drawRain();
|
||||
drawBot();
|
||||
if (!GAME.hidePreviews) drawPreviews(); // tools/shot.py hides them for clean screenshots
|
||||
if (SET.showPerf) perfTimes.overlays = (perfTimes.overlays || 0) * 0.9 + (performance.now() - overlayStart) * 0.1;
|
||||
if (GAME.replace !== GAME.replaceShown) { GAME.replaceShown = GAME.replace; hudDirty = true; }
|
||||
renderBuildBar();
|
||||
if (hudDirty || R.frame % 6 === 0) { renderSelCard(); renderBag(); renderWallet(); hudDirty = false; }
|
||||
@@ -89,11 +95,13 @@ function frame(now) {
|
||||
updateOverlayUI();
|
||||
if (now > ambientT) { ambientT = now + 200; auAmbient(scanVisible()); }
|
||||
updateMap(now);
|
||||
frameMs = frameMs * 0.9 + (performance.now() - t0) * 0.1;
|
||||
const frameWork = performance.now() - t0;
|
||||
frameMs = frameMs * 0.9 + frameWork * 0.1;
|
||||
if (PERF_FRAME) PERF_FRAME({ at: now, work: frameWork, sim: frameSim, steps: frameSteps });
|
||||
fpsSm = fpsSm * 0.95 + (1 / Math.max(0.001, dt)) * 0.05;
|
||||
if (SET.showPerf && R.frame % 15 === 0) {
|
||||
let aw = 0, ht = 0; for (let c = 0; c < NCH; c++) { if (awake[c]) aw++; if (hot[c]) ht++; }
|
||||
$('perf').textContent = `${fpsSm.toFixed(0)} fps · frame ${frameMs.toFixed(1)} ms · sim ${simMs.toFixed(1)} ms/tick\nmoving areas ${aw} · hot areas ${ht} / ${NCH} · bars ${ents.length} · view ${R.bw}×${R.bh}`;
|
||||
$('perf').textContent = `${fpsSm.toFixed(0)} fps · frame ${frameMs.toFixed(1)} ms · sim ${simMs.toFixed(1)} ms/tick (move ${(perfTimes.simMove || 0).toFixed(1)}, heat ${(perfTimes.heatPass || 0).toFixed(1)}, rest ${Math.max(0, simMs - (perfTimes.simMove || 0) - (perfTimes.heatPass || 0)).toFixed(1)})\nworld ${(perfTimes.drawWorld || 0).toFixed(1)} ms · light ${(perfTimes.computeLight || 0).toFixed(1)} ms · overlays ${(perfTimes.overlays || 0).toFixed(1)} ms\nmoving areas ${aw} · hot areas ${ht} / ${NCH} · bars ${ents.length} · view ${R.bw}×${R.bh}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Browser-only clocks; simulation code stays usable without a browser or a clock.
|
||||
let PERF_MARKS = false, PERF_FRAME = null;
|
||||
const perfMarks = {}, perfTimes = {}, perfLast = {};
|
||||
for (const name of ['drawWorld', 'computeLight', 'simMove', 'heatPass']) {
|
||||
const original = window[name];
|
||||
window[name] = function (...args) {
|
||||
if (!SET.showPerf && !PERF_MARKS) return original.apply(this, args);
|
||||
const start = performance.now();
|
||||
const result = original.apply(this, args);
|
||||
const ms = performance.now() - start;
|
||||
perfLast[name] = ms;
|
||||
if (name !== 'computeLight') perfTimes[name] = (perfTimes[name] || 0) * 0.9 + ms * 0.1;
|
||||
return result;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user