Less strict webgpu reqs
This commit is contained in:
parent
a73914d0ef
commit
39e19a3c64
2 changed files with 104 additions and 32 deletions
|
|
@ -121,12 +121,15 @@ html > body {
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
max-width: calc(100% - var(--normal-margin) * 2);
|
||||
margin: var(--normal-margin);
|
||||
z-index: 5;
|
||||
|
||||
pre {
|
||||
font-size: 20px;
|
||||
color: red;
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,30 @@ const REQUESTED_LIMIT_NAMES = [
|
|||
'maxComputeWorkgroupsPerDimension',
|
||||
] as const satisfies ReadonlyArray<keyof GPUSupportedLimits>;
|
||||
|
||||
const getRequiredLimits = (
|
||||
interface AdapterRequestAttempt {
|
||||
label: string;
|
||||
options?: GPURequestAdapterOptions;
|
||||
}
|
||||
|
||||
const ADAPTER_REQUEST_ATTEMPTS: ReadonlyArray<AdapterRequestAttempt> = [
|
||||
{
|
||||
label: 'compatibility-default',
|
||||
options: { featureLevel: 'compatibility' },
|
||||
},
|
||||
{
|
||||
label: 'core-default',
|
||||
},
|
||||
{
|
||||
label: 'compatibility-high-performance',
|
||||
options: { featureLevel: 'compatibility', powerPreference: 'high-performance' },
|
||||
},
|
||||
{
|
||||
label: 'core-high-performance',
|
||||
options: { powerPreference: 'high-performance' },
|
||||
},
|
||||
] as const;
|
||||
|
||||
const getRelevantLimits = (
|
||||
limits: GPUSupportedLimits
|
||||
): Record<(typeof REQUESTED_LIMIT_NAMES)[number], number> =>
|
||||
Object.fromEntries(REQUESTED_LIMIT_NAMES.map((name) => [name, limits[name]])) as Record<
|
||||
|
|
@ -42,25 +65,64 @@ const getAdapterInfo = (adapter: GPUAdapter): Record<string, unknown> => {
|
|||
}
|
||||
};
|
||||
|
||||
const getRequiredFeatures = (adapter: GPUAdapter): Array<GPUFeatureName> => {
|
||||
const requiredFeatures: Array<GPUFeatureName> = [];
|
||||
|
||||
if (adapter.features.has('core-features-and-limits')) {
|
||||
requiredFeatures.push('core-features-and-limits');
|
||||
}
|
||||
|
||||
return requiredFeatures;
|
||||
};
|
||||
|
||||
type AdapterRequestOutcome = 'adapter' | 'unavailable' | 'error';
|
||||
|
||||
const describeAdapterRequest = (
|
||||
attempt: AdapterRequestAttempt,
|
||||
outcome: AdapterRequestOutcome,
|
||||
causeMessage?: string
|
||||
): Record<string, unknown> => ({
|
||||
label: attempt.label,
|
||||
featureLevel: attempt.options?.featureLevel ?? 'core',
|
||||
powerPreference: attempt.options?.powerPreference ?? 'default',
|
||||
outcome,
|
||||
...(causeMessage === undefined ? {} : { causeMessage }),
|
||||
});
|
||||
|
||||
const requestAdapter = async (
|
||||
gpu: GPU,
|
||||
options?: GPURequestAdapterOptions
|
||||
): Promise<GPUAdapter | null> => {
|
||||
gpu: GPU
|
||||
): Promise<{
|
||||
adapter: GPUAdapter | null;
|
||||
attempts: Array<Record<string, unknown>>;
|
||||
}> => {
|
||||
const attempts: Array<Record<string, unknown>> = [];
|
||||
|
||||
for (const attempt of ADAPTER_REQUEST_ATTEMPTS) {
|
||||
try {
|
||||
return await gpu.requestAdapter(options);
|
||||
const adapter = await gpu.requestAdapter(attempt.options);
|
||||
attempts.push(describeAdapterRequest(attempt, adapter ? 'adapter' : 'unavailable'));
|
||||
|
||||
if (adapter) {
|
||||
return { adapter, attempts };
|
||||
}
|
||||
} 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',
|
||||
},
|
||||
attempts.push(describeAdapterRequest(attempt, 'error', getErrorMessage(error)));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return { adapter: null, attempts };
|
||||
};
|
||||
|
||||
const formatAdapterAttemptSummary = (
|
||||
attempts: Array<Record<string, unknown>>
|
||||
): string => {
|
||||
if (attempts.length === 0) {
|
||||
return 'No adapter requests were attempted.';
|
||||
}
|
||||
|
||||
return `Adapter attempts: ${attempts
|
||||
.map((attempt) => `${attempt.label}: ${attempt.outcome}`)
|
||||
.join('; ')}`;
|
||||
};
|
||||
|
||||
export const initializeGpu = async (): Promise<GPUDevice> => {
|
||||
|
|
@ -81,35 +143,43 @@ export const initializeGpu = async (): Promise<GPUDevice> => {
|
|||
});
|
||||
}
|
||||
|
||||
const adapter =
|
||||
(await requestAdapter(gpu, {
|
||||
powerPreference: 'high-performance',
|
||||
})) ?? (await requestAdapter(gpu));
|
||||
const { adapter, attempts } = await requestAdapter(gpu);
|
||||
ErrorHandler.addMetadata('webgpuAdapterRequest', {
|
||||
attempts,
|
||||
selectedAttempt: attempts[attempts.length - 1]?.label ?? null,
|
||||
});
|
||||
|
||||
if (!adapter) {
|
||||
throw new RuntimeError(
|
||||
ErrorCode.WEBGPU_ADAPTER_UNAVAILABLE,
|
||||
'WebGPU is available, but this browser could not provide a compatible GPU adapter.'
|
||||
[
|
||||
'WebGPU is available, but this browser could not provide a compatible GPU adapter.',
|
||||
formatAdapterAttemptSummary(attempts),
|
||||
].join('\n'),
|
||||
{
|
||||
details: {
|
||||
attempts,
|
||||
hasNavigatorGpu: true,
|
||||
isSecureContext: window.isSecureContext,
|
||||
platform: navigator.platform,
|
||||
userAgent: navigator.userAgent,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const requiredLimits = getRequiredLimits(adapter.limits);
|
||||
const requiredFeatures: Array<GPUFeatureName> = [];
|
||||
if (adapter.features.has('timestamp-query')) {
|
||||
requiredFeatures.push('timestamp-query');
|
||||
}
|
||||
const requiredFeatures = getRequiredFeatures(adapter);
|
||||
ErrorHandler.addMetadata('webgpuAdapter', {
|
||||
features: Array.from(adapter.features).sort(),
|
||||
info: getAdapterInfo(adapter),
|
||||
requiredFeatures,
|
||||
requiredLimits,
|
||||
relevantLimits: getRelevantLimits(adapter.limits),
|
||||
});
|
||||
|
||||
let gpuDevice: GPUDevice;
|
||||
try {
|
||||
gpuDevice = await adapter.requestDevice({
|
||||
requiredFeatures,
|
||||
requiredLimits,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new RuntimeError(
|
||||
|
|
@ -120,7 +190,6 @@ export const initializeGpu = async (): Promise<GPUDevice> => {
|
|||
details: {
|
||||
causeMessage: getErrorMessage(error),
|
||||
requiredFeatures,
|
||||
requiredLimits,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue