blob: fa4d7529e1bda3bdbfef01058baa751f05fa9859 [file] [log] [blame]
export const description = `
Tests for GPUAdapterInfo.
`;
import { Fixture } from '../../../../common/framework/fixture.js';
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { keysOf } from '../../../../common/util/data_tables.js';
import { getGPU } from '../../../../common/util/navigator_gpu.js';
import { assert, hasFeature, objectEquals } from '../../../../common/util/util.js';
import { isPowerOfTwo } from '../../../util/math.js';
export const g = makeTestGroup(Fixture);
const normalizedIdentifierRegex = /^$|^[a-z0-9]+(-[a-z0-9]+)*$/;
g.test('adapter_info')
.desc(
`
Test that every member in the GPUAdapter.info except description is properly formatted`
)
.fn(async t => {
const gpu = getGPU(t.rec);
const adapter = await gpu.requestAdapter();
assert(adapter !== null);
const adapterInfo = adapter.info;
assert(adapterInfo instanceof GPUAdapterInfo);
t.expect(
normalizedIdentifierRegex.test(adapterInfo.vendor),
`adapterInfo.vendor should be a normalized identifier. But it's '${adapterInfo.vendor}'`
);
t.expect(
normalizedIdentifierRegex.test(adapterInfo.architecture),
`adapterInfo.architecture should be a normalized identifier. But it's '${adapterInfo.architecture}'`
);
t.expect(
normalizedIdentifierRegex.test(adapterInfo.device),
`adapterInfo.device should be a normalized identifier. But it's '${adapterInfo.device}'`
);
});
g.test('same_object')
.desc(
`
GPUAdapter.info and GPUDevice.adapterInfo provide the same object each time they're accessed,
but different objects from one another.`
)
.fn(async t => {
const gpu = getGPU(t.rec);
const adapter = await gpu.requestAdapter();
assert(adapter !== null);
assert(adapter.info instanceof GPUAdapterInfo);
const adapterInfo1 = adapter.info;
const adapterInfo2 = adapter.info;
t.expect(adapterInfo1 === adapterInfo2, 'adapter.info should obey [SameObject]');
const device = await t.requestDeviceTracked(adapter);
assert(device !== null);
assert(device.adapterInfo instanceof GPUAdapterInfo);
const deviceAdapterInfo1 = device.adapterInfo;
const deviceAdapterInfo2 = device.adapterInfo;
t.expect(
deviceAdapterInfo1 === deviceAdapterInfo2,
'device.adapterInfo should obey [SameObject]'
);
t.expect(
adapter.info !== device.adapterInfo,
'adapter.info and device.adapterInfo should NOT return the same object'
);
});
g.test('device_matches_adapter')
.desc(
`
Test that GPUDevice.adapterInfo matches GPUAdapter.info. Cases access the members in
different orders to make sure that they are consistent regardless of the access order.`
)
.paramsSubcasesOnly(u =>
u.combine('testDeviceFirst', [true, false]).combine('testMembersFirst', [true, false])
)
.fn(async t => {
const { testDeviceFirst, testMembersFirst } = t.params;
const gpu = getGPU(t.rec);
const adapter = await gpu.requestAdapter();
assert(adapter !== null);
const device = await t.requestDeviceTracked(adapter);
assert(device !== null);
const deviceInfo: unknown[] = [];
const adapterInfo: unknown[] = [];
const kGPUAdapterInfoKeys = keysOf(GPUAdapterInfo.prototype);
if (testMembersFirst) {
if (testDeviceFirst) {
assert(device.adapterInfo instanceof GPUAdapterInfo);
for (const k of kGPUAdapterInfoKeys) {
deviceInfo.push(device.adapterInfo[k]);
}
assert(adapter.info instanceof GPUAdapterInfo);
for (const k of kGPUAdapterInfoKeys) {
adapterInfo.push(adapter.info[k]);
}
} else {
assert(adapter.info instanceof GPUAdapterInfo);
for (const k of kGPUAdapterInfoKeys) {
adapterInfo.push(adapter.info[k]);
}
assert(device.adapterInfo instanceof GPUAdapterInfo);
for (const k of kGPUAdapterInfoKeys) {
deviceInfo.push(device.adapterInfo[k]);
}
}
} else {
if (testDeviceFirst) {
assert(device.adapterInfo instanceof GPUAdapterInfo);
assert(adapter.info instanceof GPUAdapterInfo);
for (const k of kGPUAdapterInfoKeys) {
deviceInfo.push(device.adapterInfo[k]);
adapterInfo.push(adapter.info[k]);
}
} else {
assert(adapter.info instanceof GPUAdapterInfo);
assert(device.adapterInfo instanceof GPUAdapterInfo);
for (const k of kGPUAdapterInfoKeys) {
adapterInfo.push(adapter.info[k]);
deviceInfo.push(device.adapterInfo[k]);
}
}
t.expect(objectEquals(deviceInfo, adapterInfo));
}
});
const kSubgroupMinSizeBound = 4;
const kSubgroupMaxSizeBound = 128;
g.test('subgroup_sizes')
.desc(
`
Verify GPUAdapterInfo.subgroupMinSize, GPUAdapterInfo.subgroupMaxSize.
If the subgroups feature is supported, they must both exist.
If they exist, they must both exist and be powers of two, and
4 <= subgroupMinSize <= subgroupMaxSize <= 128.
`
)
.fn(async t => {
const gpu = getGPU(t.rec);
const adapter = await gpu.requestAdapter();
assert(adapter !== null);
const { subgroupMinSize, subgroupMaxSize } = adapter.info;
// Once 'subgroups' lands, the properties should be defined with default values 4 and 128
// when adapter does not support the feature.
// https://github.com/gpuweb/gpuweb/pull/4963
if (hasFeature(adapter.features, 'subgroups')) {
t.expect(
subgroupMinSize !== undefined,
'GPUAdapterInfo.subgroupMinSize must exist when subgroups supported'
);
t.expect(
subgroupMaxSize !== undefined,
'GPUAdapterInfo.subgroupMaxSize must exist when subgroups supported'
);
}
t.expect(
(subgroupMinSize === undefined) === (subgroupMinSize === undefined),
'GPUAdapterInfo.subgropuMinSize and GPUAdapterInfo.subgroupMaxSize must both be defined, or neither should be'
);
if (subgroupMinSize !== undefined && subgroupMaxSize !== undefined) {
t.expect(isPowerOfTwo(subgroupMinSize));
t.expect(isPowerOfTwo(subgroupMaxSize));
t.expect(kSubgroupMinSizeBound <= subgroupMinSize);
t.expect(subgroupMinSize <= subgroupMaxSize);
t.expect(subgroupMaxSize <= kSubgroupMaxSizeBound);
}
});