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;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
max-width: calc(100% - var(--normal-margin) * 2);
|
||||||
margin: var(--normal-margin);
|
margin: var(--normal-margin);
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: red;
|
color: red;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,30 @@ const REQUESTED_LIMIT_NAMES = [
|
||||||
'maxComputeWorkgroupsPerDimension',
|
'maxComputeWorkgroupsPerDimension',
|
||||||
] as const satisfies ReadonlyArray<keyof GPUSupportedLimits>;
|
] 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
|
limits: GPUSupportedLimits
|
||||||
): Record<(typeof REQUESTED_LIMIT_NAMES)[number], number> =>
|
): Record<(typeof REQUESTED_LIMIT_NAMES)[number], number> =>
|
||||||
Object.fromEntries(REQUESTED_LIMIT_NAMES.map((name) => [name, limits[name]])) as Record<
|
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 (
|
const requestAdapter = async (
|
||||||
gpu: GPU,
|
gpu: GPU
|
||||||
options?: GPURequestAdapterOptions
|
): Promise<{
|
||||||
): Promise<GPUAdapter | null> => {
|
adapter: GPUAdapter | null;
|
||||||
|
attempts: Array<Record<string, unknown>>;
|
||||||
|
}> => {
|
||||||
|
const attempts: Array<Record<string, unknown>> = [];
|
||||||
|
|
||||||
|
for (const attempt of ADAPTER_REQUEST_ATTEMPTS) {
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
throw new RuntimeError(
|
attempts.push(describeAdapterRequest(attempt, 'error', getErrorMessage(error)));
|
||||||
ErrorCode.WEBGPU_ADAPTER_UNAVAILABLE,
|
|
||||||
'Could not request a WebGPU adapter.',
|
|
||||||
{
|
|
||||||
cause: error,
|
|
||||||
details: {
|
|
||||||
causeMessage: getErrorMessage(error),
|
|
||||||
powerPreference: options?.powerPreference ?? 'default',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> => {
|
export const initializeGpu = async (): Promise<GPUDevice> => {
|
||||||
|
|
@ -81,35 +143,43 @@ export const initializeGpu = async (): Promise<GPUDevice> => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter =
|
const { adapter, attempts } = await requestAdapter(gpu);
|
||||||
(await requestAdapter(gpu, {
|
ErrorHandler.addMetadata('webgpuAdapterRequest', {
|
||||||
powerPreference: 'high-performance',
|
attempts,
|
||||||
})) ?? (await requestAdapter(gpu));
|
selectedAttempt: attempts[attempts.length - 1]?.label ?? null,
|
||||||
|
});
|
||||||
|
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
throw new RuntimeError(
|
throw new RuntimeError(
|
||||||
ErrorCode.WEBGPU_ADAPTER_UNAVAILABLE,
|
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 = getRequiredFeatures(adapter);
|
||||||
const requiredFeatures: Array<GPUFeatureName> = [];
|
|
||||||
if (adapter.features.has('timestamp-query')) {
|
|
||||||
requiredFeatures.push('timestamp-query');
|
|
||||||
}
|
|
||||||
ErrorHandler.addMetadata('webgpuAdapter', {
|
ErrorHandler.addMetadata('webgpuAdapter', {
|
||||||
features: Array.from(adapter.features).sort(),
|
features: Array.from(adapter.features).sort(),
|
||||||
info: getAdapterInfo(adapter),
|
info: getAdapterInfo(adapter),
|
||||||
requiredFeatures,
|
requiredFeatures,
|
||||||
requiredLimits,
|
relevantLimits: getRelevantLimits(adapter.limits),
|
||||||
});
|
});
|
||||||
|
|
||||||
let gpuDevice: GPUDevice;
|
let gpuDevice: GPUDevice;
|
||||||
try {
|
try {
|
||||||
gpuDevice = await adapter.requestDevice({
|
gpuDevice = await adapter.requestDevice({
|
||||||
requiredFeatures,
|
requiredFeatures,
|
||||||
requiredLimits,
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new RuntimeError(
|
throw new RuntimeError(
|
||||||
|
|
@ -120,7 +190,6 @@ export const initializeGpu = async (): Promise<GPUDevice> => {
|
||||||
details: {
|
details: {
|
||||||
causeMessage: getErrorMessage(error),
|
causeMessage: getErrorMessage(error),
|
||||||
requiredFeatures,
|
requiredFeatures,
|
||||||
requiredLimits,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue