/ concept-collection / acoustic-scattering-2d
Sign in
concept-collection / acoustic-scattering-2d
acoustic-scattering-2d / src / device.ts
45 lines · 2.0 KBBlameHistoryRaw
1/**
2 * The GPU device, requested the same way everywhere (app, tests, scripts).
3 *
4 * One limit matters here. A fused kernel binds one storage buffer per distinct
5 * grid field its line reads, plus its output and the parameter block, and a
6 * leapfrog update reads a lot of fields at once — the two pressure histories,
7 * the clock, the coordinates, the sound speed, the absorption, the Laplacian.
8 * WebGPU only guarantees 8 storage buffers per compute stage, so we ask for
9 * whatever the adapter will give up to 16. When that is not enough the planner
10 * splits the kernel instead of failing (see `fitToBudget` in plan.ts), so this
11 * is a performance request rather than a requirement.
12 */
13export const MAX_STORAGE_BUFFERS = 16;
15/** The adapter the device came from, kept so its limits and info stay
16 * available for as long as the device is in use. */
17let heldAdapter: GPUAdapter | null = null;
19export async function requestAcousticDevice(): Promise<GPUDevice> {
20 if (!navigator.gpu) {
21 throw new Error(
22 'this browser has no WebGPU. Chrome and Edge 113+, Safari 26+, and ' +
23 'Firefox 141+ on Windows have it; on Linux Firefox and Chrome may need ' +
24 'it enabled explicitly.',
25 );
26 }
27 const adapter = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' });
28 if (!adapter) throw new Error('WebGPU found no adapter on this machine.');
29 heldAdapter = adapter;
30 const want = Math.min(
31 MAX_STORAGE_BUFFERS,
32 adapter.limits.maxStorageBuffersPerShaderStage ?? 8,
33 );
34 return adapter.requestDevice({
35 requiredLimits: { maxStorageBuffersPerShaderStage: want },
36 });
39/** The adapter the current device came from, if there is one. */
40export const currentAdapter = (): GPUAdapter | null => heldAdapter;
42/** Grid fields one kernel may read, given the device's binding limit: every
43 * binding but the output and the parameter block. */
44export const kernelOperandBudget = (device: GPUDevice): number =>
45 Math.max(2, (device.limits.maxStorageBuffersPerShaderStage ?? 8) - 2);
moveopenescclose