WebGPU renderer complete: dirty uploads, default limits, legacy removed, WebGPU required, warm-up (t88 phase 2d)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+2
-2
@@ -60,7 +60,7 @@ function toggleGateAt(x, y) {
|
||||
const m = mat[i];
|
||||
if (m !== EMPTY && PH[m] !== PH_GAS) {
|
||||
const u = i - W;
|
||||
if (u >= 0 && mat[u] === EMPTY && !part[u]) { mat[u] = m; temp[u] = temp[i]; life[u] = life[i]; flags[u] = flags[i]; }
|
||||
if (u >= 0 && mat[u] === EMPTY && !part[u]) { mat[u] = m; temp[u] = temp[i]; life[u] = life[i]; flags[u] = flags[i]; renderDirtyIdx(u); }
|
||||
}
|
||||
part[i] = 0; mat[i] = GATE; flags[i] = 0; life[i] = 0;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ function interactAt(x, y) {
|
||||
return open ? 'Rack open.' : 'Rack closed.';
|
||||
}
|
||||
if (blockP[pb] === BP_PUMP || blockP[pb] === BP_OUTLET) {
|
||||
blockV[pb] ^= 4; emit({ t: 'sfx', s: 'ui' });
|
||||
blockV[pb] ^= 4; renderDirtyXY((pb % BW) * BS, ((pb / BW) | 0) * BS); emit({ t: 'sfx', s: 'ui' });
|
||||
return blockP[pb] === BP_PUMP ? (blockV[pb] & 4 ? 'Pump off.' : 'Pump on.') : (blockV[pb] & 4 ? 'Outlet closed.' : 'Outlet open.');
|
||||
}
|
||||
const id = mach[y * W + x];
|
||||
|
||||
@@ -83,6 +83,7 @@ function placeBlock(code, v, bx, by, free, extra) {
|
||||
else if (isPipeCode(code)) setCell(i, PIPE, AMB[y], 0);
|
||||
}
|
||||
if (isPipeCode(code)) pipesDirty = true;
|
||||
renderDirtyXY(bx * BS, by * BS);
|
||||
blockP[by * BW + bx] = code; blockV[by * BW + bx] = v;
|
||||
if (isFilter(code)) { blockV[by * BW + bx] = filterVariant(code, v); filterCfg.set(by * BW + bx, filterRec(extra)); filtersDirty = true; }
|
||||
if (isProcCode(code)) procDirty = true;
|
||||
@@ -169,6 +170,7 @@ function removeBlock(bx, by) {
|
||||
rackState.delete(b);
|
||||
if (s) for (const [m, count] of s.q) giveOrDrop(m, count, bot.x, bot.y);
|
||||
}
|
||||
renderDirtyXY(bx * BS, by * BS);
|
||||
blockP[b] = 0; blockV[b] = 0;
|
||||
const cleared = blockCellList(bx, by);
|
||||
if (isSlope(code) || isSlopeBelt(code)) {
|
||||
|
||||
@@ -12,6 +12,7 @@ function setCell(i, m, t, fl) {
|
||||
if (t - AMB[y] > 20 || AMB[y] - t > 20 || (MELT[m] !== 0 && AMB[y] >= MELT[m]) || (m === WATER && AMB[y] < -0.5)) wakeHeatXY(x, y);
|
||||
}
|
||||
function swapCells(i, j) {
|
||||
renderDirtyIdx(i); renderDirtyIdx(j);
|
||||
let a = mat[i]; mat[i] = mat[j]; mat[j] = a;
|
||||
const f = temp[i]; temp[i] = temp[j]; temp[j] = f;
|
||||
a = life[i]; life[i] = life[j]; life[j] = a;
|
||||
@@ -149,6 +150,7 @@ function liquid(i, x, y, m) {
|
||||
function gas(i, x, y, m) {
|
||||
// Even blocked gas must keep aging or condensing until it is gone.
|
||||
awake[(y >> 5) * CW + (x >> 5)] = 3;
|
||||
renderDirtyXY(x, y); // Gas can age without moving.
|
||||
if (m === FIRE) {
|
||||
const l = life[i];
|
||||
if (l <= 1) { mat[i] = EMPTY; life[i] = 0; wakeXY(x, y); return; }
|
||||
|
||||
@@ -114,6 +114,7 @@ function carryBeltsAndFilters(belts, filters, d, bm, carrying) {
|
||||
}
|
||||
// Change what a filter does; grains inside it that no longer pass are pushed out on top.
|
||||
function setFilter(b, cfg) {
|
||||
renderDirtyXY((b % BW) * BS, ((b / BW) | 0) * BS);
|
||||
if (!isFilter(blockP[b])) return false;
|
||||
const rec = filterRec(cfg);
|
||||
filterCfg.set(b, rec);
|
||||
|
||||
+1
-1
@@ -151,7 +151,7 @@ function pulseTick() {
|
||||
const thin = open > 1 && !THIN_BONUS_OFF ? 1 + (open - 1) * THIN_BONUS : 1;
|
||||
if (h > tier.hard) { e *= 0.35; continue; }
|
||||
const q = digp[i] + e * thin * tier.speed * PULSE_K / (1 + h * h * 0.6);
|
||||
if (q >= 255) { shatterCell(i, x, y, m); if (!p.tune) crumbleAround(x, y); } else digp[i] = q;
|
||||
if (q >= 255) { shatterCell(i, x, y, m); if (!p.tune) crumbleAround(x, y); } else { digp[i] = q; renderDirtyIdx(i); }
|
||||
e *= p.tune ? 0.96 : p.damp;
|
||||
}
|
||||
p.en[k] = e; p.s[k] = sd;
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@ function fogAlloc() { FW = Math.ceil(W / FOG); FH = Math.ceil(H / FOG); fog = ne
|
||||
function fogMark(x, y, v) {
|
||||
if (x < 0 || y < 0 || x >= W || y >= H) return;
|
||||
const i = Math.floor(y / FOG) * FW + Math.floor(x / FOG);
|
||||
if (v > fog[i]) fog[i] = v;
|
||||
if (v > fog[i]) { fog[i] = v; renderDirtyXY(x, y); }
|
||||
}
|
||||
function fogSeen(x, y) {
|
||||
if (x < 0 || y < 0 || x >= W || y >= H) return 255;
|
||||
@@ -39,7 +39,7 @@ function fogLook(cx, cy) {
|
||||
const dx = (fx + 0.5) * FOG - cx, dy = (fy + 0.5) * FOG - cy, d2 = dx * dx + dy * dy, i = fy * FW + fx;
|
||||
if (d2 >= R2) continue;
|
||||
const v = d2 <= in2 ? 255 : (FOG_SENSE - Math.sqrt(d2)) * 255 / FOG_SENSE_FADE;
|
||||
if (v > fog[i]) fog[i] = v;
|
||||
if (v > fog[i]) { fog[i] = v; renderDirtyXY(fx * FOG, fy * FOG); }
|
||||
}
|
||||
}
|
||||
function fogRevealAround(x, y, r) {
|
||||
@@ -49,4 +49,4 @@ function fogRevealAround(x, y, r) {
|
||||
fogMark(xx, yy, r >= 6 && d2 <= (r - 6) ** 2 ? 255 : Math.min(255, (r - Math.sqrt(d2)) * 255 / 6));
|
||||
}
|
||||
}
|
||||
function fogRevealAll() { fog.fill(255); }
|
||||
function fogRevealAll() { fog.fill(255); renderDirty.fill(1); }
|
||||
|
||||
@@ -65,6 +65,11 @@ function heatPass() {
|
||||
function heatChunk(c, sc) {
|
||||
const cx = c % CW, cy = (c / CW) | 0;
|
||||
const x0 = cx << 5, y0 = cy << 5, x1 = Math.min(W, x0 + 32), y1 = Math.min(H, y0 + 32);
|
||||
renderDirty[c] = 1;
|
||||
if (cx > 0) renderDirty[c - 1] = 1;
|
||||
if (cx + 1 < CW) renderDirty[c + 1] = 1;
|
||||
if (cy > 0) renderDirty[c - CW] = 1;
|
||||
if (cy + 1 < CH) renderDirty[c + CW] = 1;
|
||||
roofDirty[cx] = 1;
|
||||
const leftCold = cx > 0 && !hot[c - 1], upCold = cy > 0 && !hot[c - CW];
|
||||
let maxDev = 0, burning = false;
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ function netSat(i) { if (POWER_FREE) return 1; const n = nets[netId[i]]; return
|
||||
// Move loose grains out of the given cells, to the nearest open cell above
|
||||
// the footprint in the same column (they then fall back on top). Gas just vanishes.
|
||||
function evictCells(cells, topY) {
|
||||
for (const i of cells) bgm[i] = 0;
|
||||
for (const i of cells) { bgm[i] = 0; renderDirtyIdx(i); }
|
||||
for (const i of cells) {
|
||||
const m = mat[i], ph = PH[m];
|
||||
if (m === EMPTY || ph === PH_SOLID) continue;
|
||||
|
||||
+4
-2
@@ -4,6 +4,7 @@
|
||||
// no longer reaches the ground comes down: every trunk cell drops as wood chunks.
|
||||
function cutTree(i, x, y, tier, amt = 1) {
|
||||
const b = bgm[i];
|
||||
renderDirtyIdx(i);
|
||||
const p = digp[i] + amt * tier.speed * 70 / (1 + HARD[b]);
|
||||
if (p < 255) { digp[i] = p; return false; }
|
||||
digp[i] = 0; bgm[i] = 0;
|
||||
@@ -104,6 +105,7 @@ function plantTick() {
|
||||
if (y <= 6 || y >= H - 1) continue;
|
||||
const i = y * W + x;
|
||||
if (mat[i] !== ACORN || (flags[i] & F_BURN)) continue;
|
||||
renderDirtyIdx(i);
|
||||
if (!SOIL[mat[i + W]] || temp[i] < 2 || temp[i] > 45) { life[i] = 0; continue; }
|
||||
let room = true;
|
||||
for (let k = 1; k <= 6 && room; k++) { const j = i - k * W; if (PH[mat[j]] !== PH_EMPTY && PH[mat[j]] !== PH_GAS || bgm[j] === WOOD) room = false; }
|
||||
@@ -120,7 +122,7 @@ function setTreeCell(x, y, m) {
|
||||
const i = y * W + x;
|
||||
if (mat[i] !== EMPTY && PH[mat[i]] !== PH_GAS) return;
|
||||
if (m === LEAVES && bgm[i]) return;
|
||||
bgm[i] = m;
|
||||
bgm[i] = m; renderDirtyIdx(i);
|
||||
if (treeDraw) treeDraw.push(i);
|
||||
}
|
||||
function saplingTick() {
|
||||
@@ -134,7 +136,7 @@ function saplingTick() {
|
||||
sp.age++;
|
||||
const s = Math.min(1, sp.age / 180);
|
||||
// wipe the previous stage, then draw this one
|
||||
if (sp.cells) for (const i of sp.cells) if (bgm[i] === LEAVES || (bgm[i] === WOOD && i !== base - W)) bgm[i] = 0;
|
||||
if (sp.cells) for (const i of sp.cells) if (bgm[i] === LEAVES || (bgm[i] === WOOD && i !== base - W)) { bgm[i] = 0; renderDirtyIdx(i); }
|
||||
treeDraw = sp.cells = [];
|
||||
const th = Math.max(1, Math.round(sp.th * s)), wide = s > 0.5 ? 2 : 1;
|
||||
for (let j = 1; j <= th; j++) for (let dx = 0; dx < wide; dx++) setTreeCell(sp.x + dx, sp.y - j, WOOD);
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ function toolDig(cx, cy, r) {
|
||||
if (isWall(b) && !blockIntegrity(((y / BS) | 0) * BW + ((x / BS) | 0))) removeBlock((x / BS) | 0, (y / BS) | 0);
|
||||
dug++;
|
||||
if ((dug & 3) === 0) emit({ t: 'debris', x, y, m });
|
||||
} else digp[i] = p;
|
||||
} else { digp[i] = p; renderDirtyIdx(i); }
|
||||
});
|
||||
stats.dug += dug;
|
||||
let status = '';
|
||||
|
||||
@@ -13,6 +13,7 @@ let ents = []; // loose objects (bars)
|
||||
let bgm; // background layer: trees. Liquids, powders and gases pass in front of it.
|
||||
let flyers = []; // grains in flight, blown out of the bot
|
||||
let ghostBlocks = new Map(), ghostMolds = [];
|
||||
let renderDirty; // GPU world textures, one invalidation bit per 32x32 chunk.
|
||||
let awake, hot, hotLvl, powderArea, roofDirty;
|
||||
let loadWakeOrder = null, loadWakePending = null, loadWakeIndex = 0;
|
||||
let tick = 0, tickStamp = 1;
|
||||
@@ -53,6 +54,7 @@ function alloc(w, h) {
|
||||
surfY = new Int16Array(w); roofY = new Int16Array(w); dirtY = new Int16Array(w); graniteY = new Int16Array(w); basaltY = new Int16Array(w); magmaY = new Int16Array(w);
|
||||
AMB = new Float32Array(h);
|
||||
loadWakeOrder = loadWakePending = null; loadWakeIndex = 0;
|
||||
renderDirty = new Uint8Array(NCH).fill(1);
|
||||
cleatChunks = new Uint8Array(NCH);
|
||||
awake = new Uint8Array(NCH); hot = new Uint8Array(NCH); hotLvl = new Uint8Array(NCH);
|
||||
roofDirty = new Uint8Array(CW).fill(1);
|
||||
@@ -90,7 +92,10 @@ function fillNoise() {
|
||||
}
|
||||
|
||||
// ---------- Waking ----------
|
||||
function renderDirtyXY(x, y) { renderDirty[(y >> 5) * CW + (x >> 5)] = 1; }
|
||||
function renderDirtyIdx(i) { renderDirtyXY(i % W, (i / W) | 0); }
|
||||
function wakeXY(x, y) {
|
||||
renderDirtyXY(x, y);
|
||||
const cx = x >> 5, cy = y >> 5, c = cy * CW + cx;
|
||||
roofDirty[cx] = 1;
|
||||
awake[c] = 3;
|
||||
@@ -99,6 +104,7 @@ function wakeXY(x, y) {
|
||||
if (ly === 0) { if (cy > 0) awake[c - CW] = 3; } else if (ly === 31 && cy < CH - 1) awake[c + CW] = 3;
|
||||
}
|
||||
function wakeHeatXY(x, y) {
|
||||
renderDirtyXY(x, y);
|
||||
const c = (y >> 5) * CW + (x >> 5);
|
||||
roofDirty[x >> 5] = 1;
|
||||
hot[c] = 6; hotLvl[c] = 2;
|
||||
|
||||
Reference in New Issue
Block a user