Refactoring start
This commit is contained in:
parent
6588930911
commit
b1acdff594
19 changed files with 528 additions and 97 deletions
|
|
@ -1,33 +1,150 @@
|
|||
import { ErrorHandler, Severity } from '../error-handler';
|
||||
import {
|
||||
ErrorCode,
|
||||
ErrorHandler,
|
||||
getErrorMessage,
|
||||
RuntimeError,
|
||||
Severity,
|
||||
} from '../error-handler';
|
||||
|
||||
const WEBGPU_BROWSER_SUPPORT_MESSAGE =
|
||||
'Fleeting Garden needs WebGPU. Try the latest Chrome, Edge, or another browser with WebGPU enabled.';
|
||||
|
||||
const REQUESTED_LIMIT_NAMES = [
|
||||
'maxBufferSize',
|
||||
'maxStorageBufferBindingSize',
|
||||
'maxComputeWorkgroupsPerDimension',
|
||||
] as const satisfies ReadonlyArray<keyof GPUSupportedLimits>;
|
||||
|
||||
const getRequiredLimits = (
|
||||
limits: GPUSupportedLimits
|
||||
): Record<(typeof REQUESTED_LIMIT_NAMES)[number], number> =>
|
||||
Object.fromEntries(REQUESTED_LIMIT_NAMES.map((name) => [name, limits[name]])) as Record<
|
||||
(typeof REQUESTED_LIMIT_NAMES)[number],
|
||||
number
|
||||
>;
|
||||
|
||||
const getAdapterInfo = (adapter: GPUAdapter): Record<string, unknown> => {
|
||||
try {
|
||||
const info = adapter.info;
|
||||
return {
|
||||
architecture: info.architecture,
|
||||
description: info.description,
|
||||
device: info.device,
|
||||
isFallbackAdapter: info.isFallbackAdapter,
|
||||
subgroupMaxSize: info.subgroupMaxSize,
|
||||
subgroupMinSize: info.subgroupMinSize,
|
||||
vendor: info.vendor,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
unavailableReason: getErrorMessage(error),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const requestAdapter = async (
|
||||
gpu: GPU,
|
||||
options?: GPURequestAdapterOptions
|
||||
): Promise<GPUAdapter | null> => {
|
||||
try {
|
||||
return await gpu.requestAdapter(options);
|
||||
} catch (error) {
|
||||
throw new RuntimeError(
|
||||
ErrorCode.WEBGPU_ADAPTER_UNAVAILABLE,
|
||||
'Could not request a WebGPU adapter.',
|
||||
{
|
||||
cause: error,
|
||||
details: {
|
||||
causeMessage: getErrorMessage(error),
|
||||
powerPreference: options?.powerPreference ?? 'default',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const initializeGpu = async (): Promise<GPUDevice> => {
|
||||
if (window.isSecureContext === false) {
|
||||
throw new RuntimeError(
|
||||
ErrorCode.WEBGPU_INSECURE_CONTEXT,
|
||||
'WebGPU requires a secure context. Open Fleeting Garden over HTTPS or from localhost.'
|
||||
);
|
||||
}
|
||||
|
||||
const gpu = navigator.gpu;
|
||||
if (!gpu) {
|
||||
throw new Error('WebGPU is not supported in your browser');
|
||||
throw new RuntimeError(ErrorCode.WEBGPU_UNSUPPORTED, WEBGPU_BROWSER_SUPPORT_MESSAGE, {
|
||||
details: {
|
||||
hasNavigatorGpu: false,
|
||||
isSecureContext: window.isSecureContext,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const adapter = await gpu.requestAdapter({
|
||||
powerPreference: 'high-performance',
|
||||
});
|
||||
const adapter =
|
||||
(await requestAdapter(gpu, {
|
||||
powerPreference: 'high-performance',
|
||||
})) ?? (await requestAdapter(gpu));
|
||||
|
||||
if (!adapter) {
|
||||
throw new Error('Could not request adatper');
|
||||
throw new RuntimeError(
|
||||
ErrorCode.WEBGPU_ADAPTER_UNAVAILABLE,
|
||||
'WebGPU is available, but this browser could not provide a compatible GPU adapter.'
|
||||
);
|
||||
}
|
||||
|
||||
ErrorHandler.addMetadata('features', adapter.features);
|
||||
ErrorHandler.addMetadata('limits', adapter.limits);
|
||||
|
||||
const gpuDevice = await adapter.requestDevice({
|
||||
requiredLimits: {
|
||||
maxBufferSize: adapter.limits.maxBufferSize,
|
||||
maxStorageBufferBindingSize: adapter.limits.maxStorageBufferBindingSize,
|
||||
maxComputeWorkgroupsPerDimension: adapter.limits.maxComputeWorkgroupsPerDimension,
|
||||
},
|
||||
const requiredLimits = getRequiredLimits(adapter.limits);
|
||||
ErrorHandler.addMetadata('webgpuAdapter', {
|
||||
features: Array.from(adapter.features).sort(),
|
||||
info: getAdapterInfo(adapter),
|
||||
requiredLimits,
|
||||
});
|
||||
|
||||
let gpuDevice: GPUDevice;
|
||||
try {
|
||||
gpuDevice = await adapter.requestDevice({
|
||||
requiredLimits,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new RuntimeError(
|
||||
ErrorCode.WEBGPU_DEVICE_UNAVAILABLE,
|
||||
'Could not create a WebGPU device for this adapter.',
|
||||
{
|
||||
cause: error,
|
||||
details: {
|
||||
causeMessage: getErrorMessage(error),
|
||||
requiredLimits,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!gpuDevice) {
|
||||
throw new RuntimeError(
|
||||
ErrorCode.WEBGPU_DEVICE_UNAVAILABLE,
|
||||
'The browser returned an empty WebGPU device.'
|
||||
);
|
||||
}
|
||||
|
||||
gpuDevice.addEventListener('uncapturederror', (event: GPUUncapturedErrorEvent) =>
|
||||
ErrorHandler.addError(Severity.ERROR, event.error.message)
|
||||
ErrorHandler.addException(event.error, {
|
||||
code: ErrorCode.WEBGPU_UNCAPTURED_ERROR,
|
||||
severity: Severity.ERROR,
|
||||
})
|
||||
);
|
||||
|
||||
gpuDevice.lost.then((info) => {
|
||||
if (info.reason === 'destroyed') {
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorHandler.addError(Severity.ERROR, info.message || 'The WebGPU device was lost.', {
|
||||
code: ErrorCode.WEBGPU_DEVICE_LOST,
|
||||
details: {
|
||||
reason: info.reason,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return gpuDevice;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue