From 232baa6ffc260e707d12e72a60c8346aad72605d Mon Sep 17 00:00:00 2001 From: Gregg Tavares Date: Fri, 3 Jan 2025 23:03:54 -0800 Subject: [PATCH] Use a temp device to get the default limits. Originally I was using the limits in capability_info.ts but passing unknown limits, like maxStorageBuffersInFragmentStage, to an adapter that doesn't know that limit ends up causing an error. So, instead, create temp adapter and from that a temp device with no limits requested to query the limits. We can't query until the first request since getGPU is not async. --- src/common/util/navigator_gpu.ts | 53 +++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/common/util/navigator_gpu.ts b/src/common/util/navigator_gpu.ts index 9f137dd933c..cef5d4ddb4d 100644 --- a/src/common/util/navigator_gpu.ts +++ b/src/common/util/navigator_gpu.ts @@ -1,5 +1,4 @@ // eslint-disable-next-line import/no-restricted-paths -import { getDefaultLimitsForAdapter } from '../../webgpu/capability_info.js'; import { TestCaseRecorder } from '../framework/fixture.js'; import { globalTestConfig } from '../framework/test_config.js'; @@ -34,6 +33,7 @@ export function setGPUProvider(provider: GPUProvider) { } let impl: GPU | undefined = undefined; +let s_defaultLimits: Record | undefined = undefined; let defaultRequestAdapterOptions: GPURequestAdapterOptions | undefined; @@ -52,6 +52,14 @@ export function getDefaultRequestAdapterOptions() { return defaultRequestAdapterOptions; } +function copyLimits(objLike: GPUSupportedLimits) { + const obj: Record = {}; + for (const key in objLike) { + obj[key] = (objLike as unknown as Record)[key]; + } + return obj; +} + /** * Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations). * Throws an exception if not found. @@ -65,15 +73,28 @@ export function getGPU(recorder: TestCaseRecorder | null): GPU { if (globalTestConfig.enforceDefaultLimits) { // eslint-disable-next-line @typescript-eslint/unbound-method - const oldFn = impl.requestAdapter; + const origRequestAdapterFn = impl.requestAdapter; + // eslint-disable-next-line @typescript-eslint/unbound-method + const origRequestDeviceFn = GPUAdapter.prototype.requestDevice; + impl.requestAdapter = async function (options?: GPURequestAdapterOptions) { - const adapter = await oldFn.call(this, { ...defaultRequestAdapterOptions, ...options }); + if (!s_defaultLimits) { + const tempAdapter = await origRequestAdapterFn.call(this, { + ...defaultRequestAdapterOptions, + ...options, + }); + // eslint-disable-next-line no-restricted-syntax + const tempDevice = await tempAdapter?.requestDevice(); + s_defaultLimits = copyLimits(tempDevice!.limits); + tempDevice?.destroy(); + } + const adapter = await origRequestAdapterFn.call(this, { + ...defaultRequestAdapterOptions, + ...options, + }); if (adapter) { const limits = Object.fromEntries( - Object.entries(getDefaultLimitsForAdapter(adapter)).map(([key, { default: v }]) => [ - key, - v, - ]) + Object.entries(s_defaultLimits).map(([key, v]) => [key, v]) ); Object.defineProperty(adapter, 'limits', { @@ -87,17 +108,15 @@ export function getGPU(recorder: TestCaseRecorder | null): GPU { const enforceDefaultLimits = (adapter: GPUAdapter, desc: GPUDeviceDescriptor | undefined) => { if (desc?.requiredLimits) { - const limits = getDefaultLimitsForAdapter(adapter); for (const [key, value] of Object.entries(desc.requiredLimits)) { - const info = limits[key as keyof ReturnType]; - if (info && value !== undefined) { - const [beyondLimit, condition] = - info.class === 'maximum' - ? [value > info.default, 'greater'] - : [value < info.default, 'less']; + const limit = s_defaultLimits![key]; + if (limit !== undefined && value !== undefined) { + const [beyondLimit, condition] = key.startsWith('max') + ? [value > limit, 'greater'] + : [value < limit, 'less']; if (beyondLimit) { throw new DOMException( - `requestedLimit ${value} for ${key} is ${condition} than adapter limit ${info.default}`, + `requestedLimit ${value} for ${key} is ${condition} than adapter limit ${limit}`, 'OperationError' ); } @@ -106,14 +125,12 @@ export function getGPU(recorder: TestCaseRecorder | null): GPU { } }; - // eslint-disable-next-line @typescript-eslint/unbound-method - const origFn = GPUAdapter.prototype.requestDevice; GPUAdapter.prototype.requestDevice = async function ( this: GPUAdapter, desc?: GPUDeviceDescriptor | undefined ) { enforceDefaultLimits(this, desc); - return await origFn.call(this, desc); + return await origRequestDeviceFn.call(this, desc); }; }