Research-Stack/5-Applications/scripts/ascii_dof_quad_buffer.ts
2026-05-11 22:18:31 -05:00

125 lines
3.3 KiB
TypeScript

export type AsciiDofQuadBuffer = {
buffers: GPUBuffer[];
cellCount: number;
byteLength: number;
currentWrite(frameCounter: number): GPUBuffer;
currentRead(frameCounter: number): GPUBuffer;
queued(frameCounter: number): [GPUBuffer, GPUBuffer];
};
declare const GPUBufferUsage: {
COPY_SRC: GPUBufferUsageFlags;
COPY_DST: GPUBufferUsageFlags;
STORAGE: GPUBufferUsageFlags;
UNIFORM: GPUBufferUsageFlags;
};
export type AsciiDofParams = {
sourceWidth: number;
sourceHeight: number;
gridWidth: number;
gridHeight: number;
cellWidth: number;
cellHeight: number;
focus: number;
aperture: number;
cocScale: number;
flags?: number;
};
export const ASCII_DOF_PACKED_CELL_BYTES = 4;
export const ASCII_DOF_PARAM_BYTES = 48;
const Q16_SCALE = 65536;
export function toQ16Unit(value: number): number {
const clamped = Math.min(1, Math.max(0, value));
return Math.min(0x00010000, Math.round(clamped * Q16_SCALE)) >>> 0;
}
export function createAsciiDofQuadBuffer(
device: GPUDevice,
gridWidth: number,
gridHeight: number,
label = "ascii-dof",
): AsciiDofQuadBuffer {
const cellCount = gridWidth * gridHeight;
const byteLength = cellCount * ASCII_DOF_PACKED_CELL_BYTES;
const buffers = Array.from({ length: 4 }, (_, i) =>
device.createBuffer({
label: `${label}-${i}`,
size: byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
}),
);
return {
buffers,
cellCount,
byteLength,
currentWrite(frameCounter: number) {
return buffers[frameCounter & 3];
},
currentRead(frameCounter: number) {
return buffers[(frameCounter + 3) & 3];
},
queued(frameCounter: number) {
return [buffers[(frameCounter + 2) & 3], buffers[(frameCounter + 1) & 3]];
},
};
}
export function createAsciiDofParamBuffer(device: GPUDevice, label = "ascii-dof-params"): GPUBuffer {
return device.createBuffer({
label,
size: ASCII_DOF_PARAM_BYTES,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
}
export function writeAsciiDofParams(
device: GPUDevice,
buffer: GPUBuffer,
params: AsciiDofParams,
): void {
const words = new Uint32Array(12);
words[0] = params.sourceWidth >>> 0;
words[1] = params.sourceHeight >>> 0;
words[2] = params.gridWidth >>> 0;
words[3] = params.gridHeight >>> 0;
words[4] = params.cellWidth >>> 0;
words[5] = params.cellHeight >>> 0;
words[6] = toQ16Unit(params.focus);
words[7] = toQ16Unit(params.aperture);
words[8] = toQ16Unit(params.cocScale);
words[9] = (params.flags ?? 0) & 0xf;
device.queue.writeBuffer(buffer, 0, words);
}
export function createAsciiDofBindGroup(
device: GPUDevice,
layout: GPUBindGroupLayout,
asciiBuffer: GPUBuffer,
depthView: GPUTextureView,
colorView: GPUTextureView,
paramBuffer: GPUBuffer,
): GPUBindGroup {
return device.createBindGroup({
layout,
entries: [
{ binding: 0, resource: { buffer: asciiBuffer } },
{ binding: 1, resource: depthView },
{ binding: 2, resource: colorView },
{ binding: 3, resource: { buffer: paramBuffer } },
],
});
}
export function decodePackedAsciiCell(packed: number) {
return {
depth13: (packed >>> 19) & 0x1fff,
luma7: (packed >>> 12) & 0x7f,
char6: (packed >>> 6) & 0x3f,
flags4: (packed >>> 2) & 0xf,
reserved2: packed & 0x3,
};
}