WebGPU pixel look: GPU colours the world, CPU only rare cells (t88 phase 2a)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
// Whole-world allocation, direct strided uploads; no cell packing or dirty tracking.
|
||||
function gpuTexture(w, h, format = 'rgba8unorm', storage = false) {
|
||||
return GPU.device.createTexture({ size: [w, h], format, usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | (storage ? GPUTextureUsage.STORAGE_BINDING : 0) });
|
||||
}
|
||||
function gpuWrite(texture, data, stride, x, y, w, h) {
|
||||
if (w <= 0 || h <= 0) return;
|
||||
const bytes = data.BYTES_PER_ELEMENT;
|
||||
GPU.device.queue.writeTexture({ texture, origin: [x, y] }, data, { offset: (y * stride + x) * bytes, bytesPerRow: stride * bytes }, [w, h]);
|
||||
}
|
||||
function gpuBuffer(data) {
|
||||
const b = GPU.device.createBuffer({ size: data.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
|
||||
GPU.device.queue.writeBuffer(b, 0, data); return b;
|
||||
}
|
||||
function gpuWorldTextures() {
|
||||
if (GPU.world === mat && GPU.sky === SKY_R) return;
|
||||
if (GPU.worldTextures) for (const t of GPU.worldTextures) t.destroy();
|
||||
if (GPU.lut) GPU.lut.destroy(); if (GPU.land) GPU.land.destroy();
|
||||
GPU.world = mat; GPU.sky = SKY_R;
|
||||
GPU.worldTextures = Array.from({ length: 8 }, (_, i) => gpuTexture(i === 7 ? FW : W, i === 7 ? FH : H, 'r8uint'));
|
||||
GPU.temp = gpuTexture(W, H, 'r32float'); GPU.worldTextures.push(GPU.temp);
|
||||
gpuWrite(GPU.worldTextures[6], noiseB, W, 0, 0, W, H);
|
||||
const tables = new Float32Array((NMAT * 3 + 700 + 551 * 3) * 4);
|
||||
for (let m = 0; m < NMAT; m++) {
|
||||
tables.set([COLR[m], COLG[m], COLB[m], CVAR[m], PH[m], ALPHA[m], 0, 0, TREE_R[m], TREE_G[m], TREE_B[m], 0], m * 12);
|
||||
}
|
||||
for (let i = 0; i < 700; i++) tables.set([GLOW_R[i], GLOW_G[i], GLOW_B[i], GLOW_W[i]], (NMAT * 3 + i) * 4);
|
||||
for (const [j, palette] of Object.values(THERM).entries()) for (let i = 0; i < 551; i++) tables.set([palette[i * 3], palette[i * 3 + 1], palette[i * 3 + 2], 0], (NMAT * 3 + 700 + j * 551 + i) * 4);
|
||||
GPU.lut = gpuBuffer(tables);
|
||||
GPU.landData = new Float32Array((W + H) * 4);
|
||||
GPU.land = gpuBuffer(GPU.landData);
|
||||
GPU.bindColour = null;
|
||||
}
|
||||
function gpuViewTextures() {
|
||||
const key = `${R.bw}/${R.bh}/${R.lw}/${R.lh}`;
|
||||
if (GPU.viewKey === key) return;
|
||||
GPU.viewKey = key;
|
||||
if (GPU.viewTextures) for (const t of GPU.viewTextures) t.destroy();
|
||||
const bw = R.bw, bh = R.bh, gw = R.glowC.width, gh = R.glowC.height;
|
||||
GPU.override = gpuTexture(bw, bh); GPU.overrideGlow = gpuTexture(gw, gh);
|
||||
GPU.colourTex = gpuTexture(bw, bh, 'rgba8unorm', true);
|
||||
GPU.glowTex = gpuTexture(gw, gh, 'rgba8unorm', true);
|
||||
GPU.blurX = gpuTexture(gw, gh, 'rgba8unorm', true); GPU.blurY = gpuTexture(gw, gh, 'rgba8unorm', true);
|
||||
GPU.lightTex = gpuTexture(Math.max(1, R.lw), Math.max(1, R.lh));
|
||||
GPU.viewTextures = [GPU.override, GPU.overrideGlow, GPU.colourTex, GPU.glowTex, GPU.blurX, GPU.blurY, GPU.lightTex];
|
||||
GPU.bindColour = null;
|
||||
}
|
||||
function gpuBindings() {
|
||||
if (GPU.bindColour) return;
|
||||
const d = GPU.device, buffer = b => ({ buffer: b }), view = t => t.createView();
|
||||
const group = (pipeline, resources) => d.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: resources.map((resource, binding) => ({ binding, resource })) });
|
||||
GPU.bindColour = group(GPU.colour, [buffer(GPU.uniform), buffer(GPU.lut), buffer(GPU.land), ...GPU.worldTextures.map(view), view(GPU.override), view(GPU.overrideGlow), view(GPU.colourTex), view(GPU.glowTex)]);
|
||||
GPU.bindDraw = group(GPU.draw, [buffer(GPU.uniform), view(GPU.colourTex), view(GPU.lightTex), view(GPU.blurY), GPU.sampler]);
|
||||
if (GPU.blurUniforms) for (const b of GPU.blurUniforms) b.destroy();
|
||||
GPU.blurUniforms = [0, 1].map(() => d.createBuffer({ size: 16, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }));
|
||||
GPU.bindBlur = [group(GPU.blur, [view(GPU.glowTex), view(GPU.blurX), buffer(GPU.blurUniforms[0])]), group(GPU.blur, [view(GPU.blurX), view(GPU.blurY), buffer(GPU.blurUniforms[1])])];
|
||||
}
|
||||
function gpuUpload(lighting, glowOn) {
|
||||
gpuWorldTextures(); gpuViewTextures(); gpuBindings();
|
||||
const x = Math.max(0, R.vx0 - 2), y = Math.max(0, R.vy0 - 2);
|
||||
const w = Math.min(W, R.vx0 + R.bw + 2) - x, h = Math.min(H, R.vy0 + R.bh + 2) - y;
|
||||
const arrays = [mat, flags, life, part, bgm, digp];
|
||||
arrays.forEach((a, i) => gpuWrite(GPU.worldTextures[i], a, W, x, y, w, h));
|
||||
gpuWrite(GPU.temp, temp, W, x, y, w, h);
|
||||
const fx = Math.max(0, Math.floor(x / FOG)), fy = Math.max(0, Math.floor(y / FOG));
|
||||
gpuWrite(GPU.worldTextures[7], fog, FW, fx, fy, Math.min(FW, Math.ceil((x + w) / FOG) + 1) - fx, Math.min(FH, Math.ceil((y + h) / FOG) + 1) - fy);
|
||||
// Small per-row/column tables may change in scene tools independently of world allocation.
|
||||
const land = GPU.landData;
|
||||
for (let i = 0; i < W; i++) { const k = i * 4; land[k] = surfY[i]; land[k + 1] = dirtY[i]; land[k + 2] = graniteY[i]; land[k + 3] = basaltY[i]; }
|
||||
for (let i = 0; i < H; i++) { const k = (W + i) * 4; land[k] = SKY_R[i]; land[k + 1] = SKY_G[i]; land[k + 2] = SKY_B[i]; land[k + 3] = AMB[i]; }
|
||||
const q = GPU.device.queue;
|
||||
q.writeBuffer(GPU.land, 0, land);
|
||||
gpuWrite(GPU.override, R.px, R.bw, 0, 0, R.bw, R.bh);
|
||||
gpuWrite(GPU.overrideGlow, R.glowPx, R.glowC.width, 0, 0, R.glowC.width, R.glowC.height);
|
||||
if (lighting) gpuWrite(GPU.lightTex, R.lightPx, R.lw, 0, 0, R.lw, R.lh);
|
||||
const zp = R.zp, ox = Math.round(-(R.camX - R.vx0) * zp), oy = Math.round(-(R.camY - R.vy0) * zp);
|
||||
GPU.params.set([R.vx0, R.vy0, R.bw, R.bh, W, H, FW, FH,
|
||||
+SET.fog, R.rain, SET.grain, R.frame, +R.lens, Math.max(0, Object.keys(THERM).indexOf(SET.thermalPalette)), +SET.incandescence, +glowOn,
|
||||
ox, oy, zp, 0, R.lx0, R.ly0, R.lres, +lighting,
|
||||
R.canvas.width, R.canvas.height, SET.glow === 'high' ? 0.9 : 0.5, 0, R.camX, R.camY, 0, 0]);
|
||||
q.writeBuffer(GPU.uniform, 0, GPU.params);
|
||||
const sigma = Math.max(2, zp * (SET.glow === 'high' ? 3.2 : 2.2)) / (2 * zp);
|
||||
q.writeBuffer(GPU.blurUniforms[0], 0, new Float32Array([1, 0, sigma, 0]));
|
||||
q.writeBuffer(GPU.blurUniforms[1], 0, new Float32Array([0, 1, sigma, 0]));
|
||||
}
|
||||
Reference in New Issue
Block a user