WebNN support initial
This commit is contained in:
+5
-3
@@ -444,9 +444,11 @@ export function App() {
|
|||||||
Model: <code>{modelId}</code> · Engine:{" "}
|
Model: <code>{modelId}</code> · Engine:{" "}
|
||||||
{activeDevice === "webgpu"
|
{activeDevice === "webgpu"
|
||||||
? "WebGPU (on-device)"
|
? "WebGPU (on-device)"
|
||||||
: activeDevice === "wasm"
|
: activeDevice === "webnn"
|
||||||
? "WASM (on-device)"
|
? "WebNN (on-device)"
|
||||||
: "on-device — backend selected on first load"}
|
: activeDevice === "wasm"
|
||||||
|
? "WASM (on-device)"
|
||||||
|
: "on-device — backend selected on first load"}
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ interface ModelPickerProps {
|
|||||||
const DEVICE_OPTIONS: readonly { value: DevicePreference; label: string }[] = [
|
const DEVICE_OPTIONS: readonly { value: DevicePreference; label: string }[] = [
|
||||||
{ value: "auto", label: "Auto (WebGPU if available)" },
|
{ value: "auto", label: "Auto (WebGPU if available)" },
|
||||||
{ value: "webgpu", label: "WebGPU" },
|
{ value: "webgpu", label: "WebGPU" },
|
||||||
|
{ value: "webnn", label: "WebNN" },
|
||||||
{ value: "wasm", label: "WASM (CPU)" },
|
{ value: "wasm", label: "WASM (CPU)" },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -47,7 +48,12 @@ export function ModelPicker({
|
|||||||
// the device. Until the worker has reported one, assume WASM unless the
|
// the device. Until the worker has reported one, assume WASM unless the
|
||||||
// user explicitly asked for WebGPU.
|
// user explicitly asked for WebGPU.
|
||||||
const estimateDevice: InferenceDevice =
|
const estimateDevice: InferenceDevice =
|
||||||
activeDevice ?? (devicePreference === "webgpu" ? "webgpu" : "wasm");
|
activeDevice ??
|
||||||
|
(devicePreference === "webgpu"
|
||||||
|
? "webgpu"
|
||||||
|
: devicePreference === "webnn"
|
||||||
|
? "webnn"
|
||||||
|
: "wasm");
|
||||||
|
|
||||||
const groups: { title: string; items: WhisperModel[] }[] = [
|
const groups: { title: string; items: WhisperModel[] }[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
+15
-8
@@ -122,6 +122,12 @@ const WEBGPU_FP16_Q4: DtypeDefault = {
|
|||||||
note: "fp16 encoder + q4 merged decoder — the common working WebGPU setup.",
|
note: "fp16 encoder + q4 merged decoder — the common working WebGPU setup.",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const WEBNN_FP16: DtypeDefault = {
|
||||||
|
dtype: "fp16",
|
||||||
|
verified: false,
|
||||||
|
note: "fp16 is commonly used with WebNN.",
|
||||||
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// The catalog (D-006: curated, not exhaustive)
|
// The catalog (D-006: curated, not exhaustive)
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -138,6 +144,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
// Verified in-browser during M1 (WASM, Transformers.js default q8).
|
// Verified in-browser during M1 (WASM, Transformers.js default q8).
|
||||||
wasm: { ...WASM_Q8, verified: true },
|
wasm: { ...WASM_Q8, verified: true },
|
||||||
webgpu: WEBGPU_FP32,
|
webgpu: WEBGPU_FP32,
|
||||||
|
webnn: WEBNN_FP16,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -147,7 +154,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
multilingual: true,
|
multilingual: true,
|
||||||
encoderParamsM: 8,
|
encoderParamsM: 8,
|
||||||
decoderParamsM: 31,
|
decoderParamsM: 31,
|
||||||
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP32 },
|
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP32, webnn: WEBNN_FP16 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "onnx-community/whisper-base.en",
|
id: "onnx-community/whisper-base.en",
|
||||||
@@ -156,7 +163,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
multilingual: false,
|
multilingual: false,
|
||||||
encoderParamsM: 20,
|
encoderParamsM: 20,
|
||||||
decoderParamsM: 54,
|
decoderParamsM: 54,
|
||||||
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP32 },
|
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP32, webnn: WEBNN_FP16 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "onnx-community/whisper-base",
|
id: "onnx-community/whisper-base",
|
||||||
@@ -165,7 +172,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
multilingual: true,
|
multilingual: true,
|
||||||
encoderParamsM: 20,
|
encoderParamsM: 20,
|
||||||
decoderParamsM: 54,
|
decoderParamsM: 54,
|
||||||
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP32 },
|
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP32, webnn: WEBNN_FP16 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "onnx-community/whisper-small.en",
|
id: "onnx-community/whisper-small.en",
|
||||||
@@ -174,7 +181,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
multilingual: false,
|
multilingual: false,
|
||||||
encoderParamsM: 88,
|
encoderParamsM: 88,
|
||||||
decoderParamsM: 156,
|
decoderParamsM: 156,
|
||||||
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP16_Q4 },
|
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP16_Q4, webnn: WEBNN_FP16 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "onnx-community/whisper-small",
|
id: "onnx-community/whisper-small",
|
||||||
@@ -183,7 +190,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
multilingual: true,
|
multilingual: true,
|
||||||
encoderParamsM: 88,
|
encoderParamsM: 88,
|
||||||
decoderParamsM: 156,
|
decoderParamsM: 156,
|
||||||
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP16_Q4 },
|
defaults: { wasm: WASM_Q8, webgpu: WEBGPU_FP16_Q4, webnn: WEBNN_FP16 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "onnx-community/whisper-medium.en",
|
id: "onnx-community/whisper-medium.en",
|
||||||
@@ -192,7 +199,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
multilingual: false,
|
multilingual: false,
|
||||||
encoderParamsM: 306,
|
encoderParamsM: 306,
|
||||||
decoderParamsM: 463,
|
decoderParamsM: 463,
|
||||||
defaults: { wasm: WASM_Q4, webgpu: WEBGPU_FP16_Q4 },
|
defaults: { wasm: WASM_Q4, webgpu: WEBGPU_FP16_Q4, webnn: WEBNN_FP16 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "onnx-community/whisper-medium",
|
id: "onnx-community/whisper-medium",
|
||||||
@@ -201,7 +208,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
multilingual: true,
|
multilingual: true,
|
||||||
encoderParamsM: 306,
|
encoderParamsM: 306,
|
||||||
decoderParamsM: 463,
|
decoderParamsM: 463,
|
||||||
defaults: { wasm: WASM_Q4, webgpu: WEBGPU_FP16_Q4 },
|
defaults: { wasm: WASM_Q4, webgpu: WEBGPU_FP16_Q4, webnn: WEBNN_FP16 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "onnx-community/whisper-large-v3-turbo",
|
id: "onnx-community/whisper-large-v3-turbo",
|
||||||
@@ -210,7 +217,7 @@ export const MODELS: readonly WhisperModel[] = [
|
|||||||
multilingual: true,
|
multilingual: true,
|
||||||
encoderParamsM: 635,
|
encoderParamsM: 635,
|
||||||
decoderParamsM: 174,
|
decoderParamsM: 174,
|
||||||
defaults: { wasm: WASM_Q4, webgpu: WEBGPU_FP16_Q4 },
|
defaults: { wasm: WASM_Q4, webgpu: WEBGPU_FP16_Q4, webnn: WEBNN_FP16 },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -86,14 +86,22 @@ function forwardProgress(info: ProgressInfo): void {
|
|||||||
// Device selection (D-004): WebGPU when available, automatic WASM fallback
|
// Device selection (D-004): WebGPU when available, automatic WASM fallback
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
interface NavigatorWithGpu {
|
interface NavigatorWithHardware {
|
||||||
gpu?: { requestAdapter(): Promise<unknown | null> };
|
gpu?: { requestAdapter(): Promise<unknown | null> };
|
||||||
|
ml?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function detectDevice(preference: DevicePreference): Promise<InferenceDevice> {
|
async function detectDevice(preference: DevicePreference): Promise<InferenceDevice> {
|
||||||
if (preference === "wasm") return "wasm";
|
if (preference === "wasm") return "wasm";
|
||||||
|
if (preference === "webnn") {
|
||||||
|
try {
|
||||||
|
return (navigator as NavigatorWithHardware).ml ? "webnn" : "wasm";
|
||||||
|
} catch {
|
||||||
|
return "wasm";
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const adapter = await (navigator as NavigatorWithGpu).gpu?.requestAdapter();
|
const adapter = await (navigator as NavigatorWithHardware).gpu?.requestAdapter();
|
||||||
return adapter ? "webgpu" : "wasm";
|
return adapter ? "webgpu" : "wasm";
|
||||||
} catch {
|
} catch {
|
||||||
return "wasm";
|
return "wasm";
|
||||||
@@ -127,7 +135,8 @@ async function createPipeline(
|
|||||||
// If WebGPU was selected but pipeline creation throws (driver quirks,
|
// If WebGPU was selected but pipeline creation throws (driver quirks,
|
||||||
// unsupported ops, out-of-memory), fall back to WASM automatically (D-004).
|
// unsupported ops, out-of-memory), fall back to WASM automatically (D-004).
|
||||||
const attempts: InferenceDevice[] =
|
const attempts: InferenceDevice[] =
|
||||||
detected === "webgpu" ? ["webgpu", "wasm"] : ["wasm"];
|
detected === "webgpu" ? ["webgpu", "wasm"] :
|
||||||
|
detected === "webnn" ? ["webnn", "wasm"] : ["wasm"];
|
||||||
|
|
||||||
let lastError: unknown;
|
let lastError: unknown;
|
||||||
for (const device of attempts) {
|
for (const device of attempts) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Devices the worker can actually run on (D-004). */
|
/** Devices the worker can actually run on (D-004). */
|
||||||
export type InferenceDevice = "webgpu" | "wasm";
|
export type InferenceDevice = "webgpu" | "wasm" | "webnn";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What the UI may request. "auto" (the default) lets the worker probe
|
* What the UI may request. "auto" (the default) lets the worker probe
|
||||||
@@ -147,7 +147,7 @@ function isOptionalString(value: unknown): value is string | undefined {
|
|||||||
return value === undefined || typeof value === "string";
|
return value === undefined || typeof value === "string";
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEVICES: readonly string[] = ["webgpu", "wasm"];
|
const DEVICES: readonly string[] = ["webgpu", "wasm", "webnn"];
|
||||||
const DTYPES: readonly string[] = ["fp32", "fp16", "q8", "q4"];
|
const DTYPES: readonly string[] = ["fp32", "fp16", "q8", "q4"];
|
||||||
|
|
||||||
export function isInferenceDevice(value: unknown): value is InferenceDevice {
|
export function isInferenceDevice(value: unknown): value is InferenceDevice {
|
||||||
|
|||||||
Reference in New Issue
Block a user