Files
micarrsim/packages/webgpu-kernels/compute.js
T

112 lines
3.9 KiB
JavaScript

import { computeDasMap } from "../acoustics-core/das.js";
import { computeGccPhatMap } from "../acoustics-core/gcc-phat.js";
import {
isDasAlgorithm,
requireLocalizationAlgorithmDefinition,
} from "../acoustics-core/localization-algorithms.js";
import { computeDasWebGpu } from "./das.js";
import { isRecoverableWebGpuError } from "./device.js";
function createAbortError(signal) {
if (signal?.reason instanceof Error && signal.reason.name === "AbortError") return signal.reason;
const error = new Error("DAS 계산이 취소되었습니다.");
error.name = "AbortError";
return error;
}
function throwIfAborted(signal) {
if (signal?.aborted) throw createAbortError(signal);
}
/**
* Execute the requested DAS backend. `auto` falls back only for typed WebGPU
* operational errors; invalid inputs and AbortError remain visible to callers.
*/
async function computeDasBackend(options, dependencies = {}) {
const computeGpu = dependencies.computeGpu ?? computeDasWebGpu;
const computeCpu = dependencies.computeCpu ?? computeDasMap;
const requestedBackend = options?.experiment?.beamforming?.backend ?? "auto";
if (requestedBackend === "cpu") return computeCpu(options);
if (requestedBackend === "webgpu") return computeGpu(options);
if (requestedBackend !== "auto") {
throw new TypeError(`지원하지 않는 DAS backend입니다: ${requestedBackend}`);
}
try {
return await computeGpu(options);
} catch (gpuError) {
if (!isRecoverableWebGpuError(gpuError)) throw gpuError;
throwIfAborted(options.signal);
options.onFallback?.({ from: "webgpu", to: "cpu", error: gpuError });
throwIfAborted(options.signal);
try {
const cpuResult = await computeCpu(options);
return {
...cpuResult,
requestedBackend: "auto",
fallback: {
from: "webgpu",
code: gpuError.code,
message: gpuError.message,
},
};
} catch (cpuError) {
if (cpuError?.name === "AbortError") throw cpuError;
throwIfAborted(options.signal);
throw new AggregateError(
[gpuError, cpuError],
`WebGPU 실행 실패 후 CPU fallback도 실패했습니다: ${cpuError.message}`,
);
}
}
}
/**
* Execute a DAS-family algorithm with the requested CPU/WebGPU policy.
* Conventional DAS callers can keep using this backwards-compatible entry.
*/
export async function computeDasWithBackend(options, dependencies = {}) {
const algorithm = requireLocalizationAlgorithmDefinition(
options?.experiment?.beamforming?.algorithm,
);
if (!isDasAlgorithm(algorithm.id)) {
throw new RangeError(
`computeDasWithBackend does not implement ${algorithm.id}.`,
);
}
return computeDasBackend(options, dependencies);
}
/**
* Execute any registered localization algorithm. GCC-PHAT currently uses its
* portable CPU implementation for both `auto` and `cpu`; an explicit WebGPU
* request is rejected instead of silently changing the requested backend.
*/
export async function computeLocalizationWithBackend(options, dependencies = {}) {
const algorithm = requireLocalizationAlgorithmDefinition(
options?.experiment?.beamforming?.algorithm,
);
if (isDasAlgorithm(algorithm.id)) {
return computeDasBackend(options, dependencies);
}
if (algorithm.id !== "gcc-phat") {
throw new RangeError("No backend dispatcher is registered for " + algorithm.id + ".");
}
const requestedBackend = options?.experiment?.beamforming?.backend ?? "auto";
if (requestedBackend === "webgpu") {
throw new TypeError(
`${algorithm.label} currently supports only the CPU backend; choose auto or cpu.`,
);
}
if (requestedBackend !== "auto" && requestedBackend !== "cpu") {
throw new TypeError(`unsupported localization backend: ${requestedBackend}`);
}
const computeCpu =
dependencies.computeGccCpu ??
dependencies.computeCpu ??
computeGccPhatMap;
return computeCpu(options);
}