Skip to content

Commit

Permalink
Use a temp device to get the default limits.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
greggman committed Jan 7, 2025
1 parent d6587e3 commit a1cd0ca
Showing 1 changed file with 35 additions and 18 deletions.
53 changes: 35 additions & 18 deletions src/common/util/navigator_gpu.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -34,6 +33,7 @@ export function setGPUProvider(provider: GPUProvider) {
}

let impl: GPU | undefined = undefined;
let s_defaultLimits: Record<string, GPUSize64> | undefined = undefined;

let defaultRequestAdapterOptions: GPURequestAdapterOptions | undefined;

Expand All @@ -52,6 +52,14 @@ export function getDefaultRequestAdapterOptions() {
return defaultRequestAdapterOptions;
}

function copyLimits(objLike: GPUSupportedLimits) {
const obj: Record<string, number> = {};
for (const key in objLike) {
obj[key] = (objLike as unknown as Record<string, number>)[key];
}
return obj;
}

/**
* Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations).
* Throws an exception if not found.
Expand All @@ -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', {
Expand All @@ -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<typeof getDefaultLimitsForAdapter>];
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'
);
}
Expand All @@ -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);
};
}

Expand Down

0 comments on commit a1cd0ca

Please sign in to comment.