Add hasFeature function and disallow features.has (#4506)

The problem with `features.has` is that it takes
a string isntead of a `GPUFeatureName`. To prevent
typos when checking for features, add a lint warning
for `features.has(feature)` and suggest using
`hasFeature(features, feature)`. `hasFeature`
takes a `GPUFeatureName` and so will warn if the
string pass is not a valid feature.
This commit is contained in:
Greggman 2025-11-19 11:29:39 +09:00 committed by GitHub
parent 5a1dcdc857
commit 3314e41086
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 193 additions and 162 deletions

View file

@ -58,6 +58,11 @@
"message": "Use requestDeviceTracked() instead of requestDevice().",
// We don't seem to need direct calls to requestDevice() at all so we can just disallow all of them.
"selector": "CallExpression > MemberExpression > Identifier[name=\"requestDevice\"]"
},
{
"message": "Use hasFeature() instead of features.has().",
// features.has takes any string. We want only valid feature names.
"selector": "CallExpression[callee.property.name='has'][callee.object.name='features'],CallExpression[callee.property.name='has'][callee.object.property.name='features']"
}
],

View file

@ -1,4 +1,4 @@
import { assert } from '../util/util.js';
import { assert, hasFeature } from '../util/util.js';
export type TestConfig = {
/**
@ -90,7 +90,7 @@ export const globalTestConfig: TestConfig = {
// is trying to test that compatibility devices have the correct validation.
export function isCompatibilityDevice(device: GPUDevice) {
if (globalTestConfig.compatibility) {
assert(!device.features.has('core-features-and-limits'));
assert(!hasFeature(device.features, 'core-features-and-limits'));
}
return globalTestConfig.compatibility;
}

View file

@ -2,7 +2,7 @@
import { TestCaseRecorder } from '../framework/fixture.js';
import { globalTestConfig } from '../framework/test_config.js';
import { ErrorWithExtra, assert, objectEquals } from './util.js';
import { ErrorWithExtra, assert, hasFeature, objectEquals } from './util.js';
/**
* Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations).
@ -164,7 +164,9 @@ export function getGPU(recorder: TestCaseRecorder | null): GPU {
Object.defineProperty(adapter, 'features', {
enumerable: false,
value: new Set(
adapter.features.has('core-features-and-limits') ? ['core-features-and-limits'] : []
hasFeature(adapter.features, 'core-features-and-limits')
? ['core-features-and-limits']
: []
),
});
}
@ -177,6 +179,7 @@ export function getGPU(recorder: TestCaseRecorder | null): GPU {
for (const [feature] of desc.requiredFeatures) {
// Note: This adapter has had its features property over-ridden and will only return
// have nothing or 'core-features-and-limits'.
// eslint-disable-next-line no-restricted-syntax
if (!adapter.features.has(feature)) {
throw new TypeError(`requested feature ${feature} does not exist on adapter`);
}

View file

@ -496,3 +496,12 @@ export function filterUniqueValueTestVariants(valueTestVariants: ValueTestVarian
export function makeValueTestVariant(base: number, variant: ValueTestVariant) {
return base * variant.mult + variant.add;
}
/**
* Use instead of features.has because feature's has takes any string
* and we want to prevent typos.
*/
export function hasFeature(features: GPUSupportedFeatures, feature: GPUFeatureName) {
// eslint-disable-next-line no-restricted-syntax
return features.has(feature);
}

220
src/resources/cache/hashes.json generated vendored
View file

@ -1,112 +1,112 @@
{
"webgpu/shader/execution/binary/af_addition.bin": "b1b71eea",
"webgpu/shader/execution/binary/af_logical.bin": "bca2200a",
"webgpu/shader/execution/binary/af_division.bin": "cfda1ac2",
"webgpu/shader/execution/binary/af_matrix_addition.bin": "4473c45a",
"webgpu/shader/execution/binary/af_matrix_subtraction.bin": "e945ec79",
"webgpu/shader/execution/binary/af_multiplication.bin": "eb6c672",
"webgpu/shader/execution/binary/af_remainder.bin": "e971fba9",
"webgpu/shader/execution/binary/af_subtraction.bin": "72293f91",
"webgpu/shader/execution/binary/f16_addition.bin": "ea5505aa",
"webgpu/shader/execution/binary/f16_logical.bin": "7cf1347f",
"webgpu/shader/execution/binary/f16_division.bin": "c05f4feb",
"webgpu/shader/execution/binary/f16_matrix_addition.bin": "429f8589",
"webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "5ce1d443",
"webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "ff07ce52",
"webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "8693f3e",
"webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "1a4cc717",
"webgpu/shader/execution/binary/f16_multiplication.bin": "604b8407",
"webgpu/shader/execution/binary/f16_remainder.bin": "bc510eb0",
"webgpu/shader/execution/binary/f16_subtraction.bin": "81718e04",
"webgpu/shader/execution/binary/f32_addition.bin": "a04733a4",
"webgpu/shader/execution/binary/f32_logical.bin": "83d3ec3d",
"webgpu/shader/execution/binary/f32_division.bin": "d5c79df7",
"webgpu/shader/execution/binary/f32_matrix_addition.bin": "803f74e9",
"webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "2e1e2af9",
"webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "f60c6997",
"webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "bf68cb89",
"webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "9a6401bb",
"webgpu/shader/execution/binary/f32_multiplication.bin": "f1e1ef9a",
"webgpu/shader/execution/binary/f32_remainder.bin": "da5afd13",
"webgpu/shader/execution/binary/f32_subtraction.bin": "830b567f",
"webgpu/shader/execution/binary/i32_arithmetic.bin": "c18da799",
"webgpu/shader/execution/binary/i32_comparison.bin": "848ceb5c",
"webgpu/shader/execution/binary/u32_arithmetic.bin": "dc0b2ed9",
"webgpu/shader/execution/binary/u32_comparison.bin": "2bae879f",
"webgpu/shader/execution/abs.bin": "561a28e0",
"webgpu/shader/execution/acos.bin": "8cc074ad",
"webgpu/shader/execution/acosh.bin": "8a510d97",
"webgpu/shader/execution/asin.bin": "14d5064",
"webgpu/shader/execution/asinh.bin": "1e00159",
"webgpu/shader/execution/atan.bin": "7a1fc6e9",
"webgpu/shader/execution/atan2.bin": "3a2974c9",
"webgpu/shader/execution/atanh.bin": "8d6dab0b",
"webgpu/shader/execution/bitcast.bin": "e307a369",
"webgpu/shader/execution/ceil.bin": "d02bdaad",
"webgpu/shader/execution/clamp.bin": "fcbe35ac",
"webgpu/shader/execution/cos.bin": "ccb1c23f",
"webgpu/shader/execution/cosh.bin": "a0a406ad",
"webgpu/shader/execution/cross.bin": "2a875fef",
"webgpu/shader/execution/degrees.bin": "50bb915a",
"webgpu/shader/execution/determinant.bin": "3e6249da",
"webgpu/shader/execution/distance.bin": "a29c5aa",
"webgpu/shader/execution/dot.bin": "466765d5",
"webgpu/shader/execution/exp.bin": "c2640231",
"webgpu/shader/execution/exp2.bin": "274b093f",
"webgpu/shader/execution/faceForward.bin": "4492d11d",
"webgpu/shader/execution/floor.bin": "d41f1c95",
"webgpu/shader/execution/fma.bin": "2fb1c38f",
"webgpu/shader/execution/fract.bin": "c9080e98",
"webgpu/shader/execution/frexp.bin": "b675bee4",
"webgpu/shader/execution/inverseSqrt.bin": "8b23d93a",
"webgpu/shader/execution/ldexp.bin": "ee536142",
"webgpu/shader/execution/length.bin": "b3384a6",
"webgpu/shader/execution/log.bin": "bd3a55f0",
"webgpu/shader/execution/log2.bin": "ff3794ac",
"webgpu/shader/execution/max.bin": "fce16c8a",
"webgpu/shader/execution/min.bin": "ff191ba4",
"webgpu/shader/execution/mix.bin": "4c4935e9",
"webgpu/shader/execution/modf.bin": "da3ae106",
"webgpu/shader/execution/normalize.bin": "35a5ec27",
"webgpu/shader/execution/pack2x16float.bin": "fec8b248",
"webgpu/shader/execution/pow.bin": "ee753258",
"webgpu/shader/execution/quantizeToF16.bin": "2c4ffde2",
"webgpu/shader/execution/radians.bin": "1fdd20d3",
"webgpu/shader/execution/reflect.bin": "ca02fe50",
"webgpu/shader/execution/refract.bin": "bdb6610",
"webgpu/shader/execution/round.bin": "b8be95b4",
"webgpu/shader/execution/saturate.bin": "74f21503",
"webgpu/shader/execution/sign.bin": "533d2185",
"webgpu/shader/execution/sin.bin": "e1c39c1b",
"webgpu/shader/execution/sinh.bin": "61da608d",
"webgpu/shader/execution/smoothstep.bin": "a6dd83ed",
"webgpu/shader/execution/sqrt.bin": "ca8cd266",
"webgpu/shader/execution/step.bin": "34459d7f",
"webgpu/shader/execution/tan.bin": "b5513d60",
"webgpu/shader/execution/tanh.bin": "6f67098a",
"webgpu/shader/execution/transpose.bin": "1dd8473f",
"webgpu/shader/execution/trunc.bin": "6df96e09",
"webgpu/shader/execution/unpack2x16float.bin": "1c7505ba",
"webgpu/shader/execution/unpack2x16snorm.bin": "ade8ed16",
"webgpu/shader/execution/unpack2x16unorm.bin": "d7b00631",
"webgpu/shader/execution/unpack4x8snorm.bin": "a21a1f80",
"webgpu/shader/execution/unpack4x8unorm.bin": "edb0945d",
"webgpu/shader/execution/unary/af_arithmetic.bin": "5f835ed1",
"webgpu/shader/execution/unary/af_assignment.bin": "ede88653",
"webgpu/shader/execution/unary/bool_conversion.bin": "1d58a0ac",
"webgpu/shader/execution/unary/f16_arithmetic.bin": "f01f7be8",
"webgpu/shader/execution/unary/f16_conversion.bin": "798114db",
"webgpu/shader/execution/unary/f32_arithmetic.bin": "dd24b472",
"webgpu/shader/execution/unary/f32_conversion.bin": "38c55dc0",
"webgpu/shader/execution/unary/i32_arithmetic.bin": "65d2d245",
"webgpu/shader/execution/unary/i32_conversion.bin": "6059380d",
"webgpu/shader/execution/unary/u32_conversion.bin": "9918d0a7",
"webgpu/shader/execution/unary/ai_assignment.bin": "587d799a",
"webgpu/shader/execution/binary/ai_arithmetic.bin": "3c8862bd",
"webgpu/shader/execution/unary/ai_arithmetic.bin": "d46137bc",
"webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "ba58dd07",
"webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "5fe0a0e3",
"webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "9f8ed988",
"webgpu/shader/execution/derivatives.bin": "7a039a71",
"webgpu/shader/execution/fwidth.bin": "30b40017"
"webgpu/shader/execution/binary/af_addition.bin": "da003be4",
"webgpu/shader/execution/binary/af_logical.bin": "14ad43ca",
"webgpu/shader/execution/binary/af_division.bin": "1296892b",
"webgpu/shader/execution/binary/af_matrix_addition.bin": "1317ab16",
"webgpu/shader/execution/binary/af_matrix_subtraction.bin": "2853ad41",
"webgpu/shader/execution/binary/af_multiplication.bin": "5ee6bde6",
"webgpu/shader/execution/binary/af_remainder.bin": "366f8d30",
"webgpu/shader/execution/binary/af_subtraction.bin": "3c19e6fb",
"webgpu/shader/execution/binary/f16_addition.bin": "319185c0",
"webgpu/shader/execution/binary/f16_logical.bin": "94a83cb4",
"webgpu/shader/execution/binary/f16_division.bin": "635bbc5f",
"webgpu/shader/execution/binary/f16_matrix_addition.bin": "30070be5",
"webgpu/shader/execution/binary/f16_matrix_matrix_multiplication.bin": "7e0e00db",
"webgpu/shader/execution/binary/f16_matrix_scalar_multiplication.bin": "d538842d",
"webgpu/shader/execution/binary/f16_matrix_subtraction.bin": "4b5ce37c",
"webgpu/shader/execution/binary/f16_matrix_vector_multiplication.bin": "7a4445de",
"webgpu/shader/execution/binary/f16_multiplication.bin": "b2c70232",
"webgpu/shader/execution/binary/f16_remainder.bin": "d138c173",
"webgpu/shader/execution/binary/f16_subtraction.bin": "e2515ca5",
"webgpu/shader/execution/binary/f32_addition.bin": "60286efc",
"webgpu/shader/execution/binary/f32_logical.bin": "a578e65b",
"webgpu/shader/execution/binary/f32_division.bin": "36d196b1",
"webgpu/shader/execution/binary/f32_matrix_addition.bin": "a11d772e",
"webgpu/shader/execution/binary/f32_matrix_matrix_multiplication.bin": "d578a451",
"webgpu/shader/execution/binary/f32_matrix_scalar_multiplication.bin": "a0259221",
"webgpu/shader/execution/binary/f32_matrix_subtraction.bin": "9eb43464",
"webgpu/shader/execution/binary/f32_matrix_vector_multiplication.bin": "939f2e70",
"webgpu/shader/execution/binary/f32_multiplication.bin": "d34ce65d",
"webgpu/shader/execution/binary/f32_remainder.bin": "6fa6b892",
"webgpu/shader/execution/binary/f32_subtraction.bin": "ddb575f5",
"webgpu/shader/execution/binary/i32_arithmetic.bin": "5bc8c900",
"webgpu/shader/execution/binary/i32_comparison.bin": "9165629b",
"webgpu/shader/execution/binary/u32_arithmetic.bin": "fc8c6726",
"webgpu/shader/execution/binary/u32_comparison.bin": "598d0773",
"webgpu/shader/execution/abs.bin": "f4ea4f10",
"webgpu/shader/execution/acos.bin": "f7ccbfca",
"webgpu/shader/execution/acosh.bin": "11315a9f",
"webgpu/shader/execution/asin.bin": "7349a03b",
"webgpu/shader/execution/asinh.bin": "fdebffc6",
"webgpu/shader/execution/atan.bin": "723ae9a5",
"webgpu/shader/execution/atan2.bin": "57241996",
"webgpu/shader/execution/atanh.bin": "e72a7c31",
"webgpu/shader/execution/bitcast.bin": "3de97de",
"webgpu/shader/execution/ceil.bin": "29fd5fae",
"webgpu/shader/execution/clamp.bin": "8d0eafdb",
"webgpu/shader/execution/cos.bin": "eac17036",
"webgpu/shader/execution/cosh.bin": "575d952c",
"webgpu/shader/execution/cross.bin": "34b3306c",
"webgpu/shader/execution/degrees.bin": "e95cfd56",
"webgpu/shader/execution/determinant.bin": "cbf20e3c",
"webgpu/shader/execution/distance.bin": "1c119356",
"webgpu/shader/execution/dot.bin": "4da666a3",
"webgpu/shader/execution/exp.bin": "69ee596e",
"webgpu/shader/execution/exp2.bin": "1b0d4107",
"webgpu/shader/execution/faceForward.bin": "d9e806e",
"webgpu/shader/execution/floor.bin": "d04aaaed",
"webgpu/shader/execution/fma.bin": "ea6f3d9d",
"webgpu/shader/execution/fract.bin": "6041dbd3",
"webgpu/shader/execution/frexp.bin": "4f14eae",
"webgpu/shader/execution/inverseSqrt.bin": "a31d31ec",
"webgpu/shader/execution/ldexp.bin": "bc8d5e62",
"webgpu/shader/execution/length.bin": "52fc048a",
"webgpu/shader/execution/log.bin": "bdb94ae0",
"webgpu/shader/execution/log2.bin": "b92a3bca",
"webgpu/shader/execution/max.bin": "436f8f21",
"webgpu/shader/execution/min.bin": "21cb02b4",
"webgpu/shader/execution/mix.bin": "ef3d6b1b",
"webgpu/shader/execution/modf.bin": "55ff0437",
"webgpu/shader/execution/normalize.bin": "fd315393",
"webgpu/shader/execution/pack2x16float.bin": "8443853d",
"webgpu/shader/execution/pow.bin": "e1f4e61",
"webgpu/shader/execution/quantizeToF16.bin": "564caff5",
"webgpu/shader/execution/radians.bin": "3d50fa1a",
"webgpu/shader/execution/reflect.bin": "de3cdc23",
"webgpu/shader/execution/refract.bin": "a6b7cf0c",
"webgpu/shader/execution/round.bin": "5735993c",
"webgpu/shader/execution/saturate.bin": "58cb0de5",
"webgpu/shader/execution/sign.bin": "b59874a7",
"webgpu/shader/execution/sin.bin": "59c3422",
"webgpu/shader/execution/sinh.bin": "427c6297",
"webgpu/shader/execution/smoothstep.bin": "b0d9c07e",
"webgpu/shader/execution/sqrt.bin": "f207f4ac",
"webgpu/shader/execution/step.bin": "18a574a0",
"webgpu/shader/execution/tan.bin": "40504049",
"webgpu/shader/execution/tanh.bin": "7c1b22bd",
"webgpu/shader/execution/transpose.bin": "af20793c",
"webgpu/shader/execution/trunc.bin": "2d9f23e5",
"webgpu/shader/execution/unpack2x16float.bin": "de3c9cb4",
"webgpu/shader/execution/unpack2x16snorm.bin": "2cfc1791",
"webgpu/shader/execution/unpack2x16unorm.bin": "85ff09e",
"webgpu/shader/execution/unpack4x8snorm.bin": "e4c5b1f4",
"webgpu/shader/execution/unpack4x8unorm.bin": "1fd43872",
"webgpu/shader/execution/unary/af_arithmetic.bin": "daccee7d",
"webgpu/shader/execution/unary/af_assignment.bin": "fb1a84b1",
"webgpu/shader/execution/unary/bool_conversion.bin": "65b450a7",
"webgpu/shader/execution/unary/f16_arithmetic.bin": "79ee24a3",
"webgpu/shader/execution/unary/f16_conversion.bin": "920adf7",
"webgpu/shader/execution/unary/f32_arithmetic.bin": "9496759c",
"webgpu/shader/execution/unary/f32_conversion.bin": "dddb7940",
"webgpu/shader/execution/unary/i32_arithmetic.bin": "e9c53cb5",
"webgpu/shader/execution/unary/i32_conversion.bin": "87b5fd7",
"webgpu/shader/execution/unary/u32_conversion.bin": "70718f67",
"webgpu/shader/execution/unary/ai_assignment.bin": "4fad3c69",
"webgpu/shader/execution/binary/ai_arithmetic.bin": "a00c8197",
"webgpu/shader/execution/unary/ai_arithmetic.bin": "3b481d45",
"webgpu/shader/execution/binary/af_matrix_matrix_multiplication.bin": "47b72af2",
"webgpu/shader/execution/binary/af_matrix_scalar_multiplication.bin": "1a5c6719",
"webgpu/shader/execution/binary/af_matrix_vector_multiplication.bin": "1b8f17cf",
"webgpu/shader/execution/derivatives.bin": "6d72e1fb",
"webgpu/shader/execution/fwidth.bin": "c00ddf59"
}

View file

@ -6,7 +6,7 @@ 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, objectEquals } from '../../../../common/util/util.js';
import { assert, hasFeature, objectEquals } from '../../../../common/util/util.js';
import { isPowerOfTwo } from '../../../util/math.js';
export const g = makeTestGroup(Fixture);
@ -158,7 +158,7 @@ If they exist, they must both exist and be powers of two, and
// 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 (adapter.features.has('subgroups')) {
if (hasFeature(adapter.features, 'subgroups')) {
t.expect(
subgroupMinSize !== undefined,
'GPUAdapterInfo.subgroupMinSize must exist when subgroups supported'

View file

@ -8,7 +8,7 @@ potentially limited native resources.
import { Fixture } from '../../../../common/framework/fixture.js';
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { getGPU } from '../../../../common/util/navigator_gpu.js';
import { assert, assertReject, typedEntries } from '../../../../common/util/util.js';
import { assert, assertReject, hasFeature, typedEntries } from '../../../../common/util/util.js';
import {
getDefaultLimitsForCTS,
kFeatureNames,
@ -44,7 +44,7 @@ g.test('default')
if (device.features.size === 1) {
t.expect(
device.features.has('core-features-and-limits'),
hasFeature(device.features, 'core-features-and-limits'),
'Default device should not have any features other than "core-features-and-limits"'
);
} else {
@ -204,9 +204,9 @@ g.test('features,known')
assert(adapter !== null);
const promise = t.requestDeviceTracked(adapter, { requiredFeatures: [feature] });
if (adapter.features.has(feature)) {
if (hasFeature(adapter.features, feature)) {
const device = await promise;
t.expect(device.features.has(feature), 'Device should include the required feature');
t.expect(hasFeature(device.features, feature), 'Device should include the required feature');
} else {
t.shouldReject('TypeError', promise);
}
@ -496,12 +496,12 @@ g.test('always_returns_device')
const device = await t.requestDeviceTracked(adapter);
assert(device instanceof GPUDevice, 'requestDevice must return a device or throw');
if (featureLevel === 'core' && adapter.features.has('core-features-and-limits')) {
if (featureLevel === 'core' && hasFeature(adapter.features, 'core-features-and-limits')) {
// Check if the device supports core, when featureLevel is core and adapter supports core.
// This check is to make sure something lower-level is not forcing compatibility mode.
t.expect(
device.features.has('core-features-and-limits'),
hasFeature(device.features, 'core-features-and-limits'),
'must not get a Compatibility adapter if not requested'
);
}

View file

@ -103,6 +103,7 @@ changes to WebGPU do not break sites requesting everything.
// Test that all the adapter features are on the device.
for (const feature of t.adapter.features) {
// eslint-disable-next-line no-restricted-syntax
t.expect(t.device.features.has(feature), `device has feature: ${feature}`);
}
});

View file

@ -4,7 +4,7 @@ Tests for capability checking for features enabling optional texture formats.
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { getGPU } from '../../../../../common/util/navigator_gpu.js';
import { assert } from '../../../../../common/util/util.js';
import { assert, hasFeature } from '../../../../../common/util/util.js';
import { kCanvasTextureFormats } from '../../../../capability_info.js';
import {
kASTCCompressedTextureFormats,
@ -551,11 +551,11 @@ g.test('check_capability_guarantees')
const features = adapter.features;
const supportsBC = features.has('texture-compression-bc');
const supportsBCSliced3D = features.has('texture-compression-bc-sliced-3d');
const supportsASTC = features.has('texture-compression-astc');
const supportsASTCSliced3D = features.has('texture-compression-astc-sliced-3d');
const supportsETC2 = features.has('texture-compression-etc2');
const supportsBC = hasFeature(features, 'texture-compression-bc');
const supportsBCSliced3D = hasFeature(features, 'texture-compression-bc-sliced-3d');
const supportsASTC = hasFeature(features, 'texture-compression-astc');
const supportsASTCSliced3D = hasFeature(features, 'texture-compression-astc-sliced-3d');
const supportsETC2 = hasFeature(features, 'texture-compression-etc2');
t.expect(
supportsBC || (supportsETC2 && supportsASTC),

View file

@ -13,6 +13,7 @@ when the feature is not enabled. This includes:
`;
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { hasFeature } from '../../../../../common/util/util.js';
import {
kTextureFormatTier1AllowsRenderAttachmentBlendableMultisample,
kTextureFormatTier1ThrowsWhenNotEnabled,
@ -32,7 +33,7 @@ g.test('enables_rg11b10ufloat_renderable')
)
.beforeAllSubcases(t => t.selectDeviceOrSkipTestCase('texture-formats-tier1'))
.fn(t => {
t.expect(() => t.device.features.has('rg11b10ufloat-renderable'));
t.expect(() => hasFeature(t.device.features, 'rg11b10ufloat-renderable'));
});
g.test('texture_usage,render_attachment')

View file

@ -5,10 +5,11 @@ Test that enabling texture-formats-tier2 also enables rg11b10ufloat-renderable a
Tests that abilities enabled by 'texture-formats-tier2' correctly generate validation errors
when the feature is not enabled. This includes:
- read-write stoorage access formats gaining this capability.
- read-write storage access formats gaining this capability.
`;
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { hasFeature } from '../../../../../common/util/util.js';
import { kTextureFormatsTier2EnablesStorageReadWrite } from '../../../../format_info.js';
import { UniqueFeaturesOrLimitsGPUTest } from '../../../../gpu_test.js';
import * as vtu from '../../validation_test_utils.js';
@ -23,8 +24,8 @@ g.test('enables_rg11b10ufloat_renderable_and_texture_formats_tier1')
)
.beforeAllSubcases(t => t.selectDeviceOrSkipTestCase('texture-formats-tier2'))
.fn(t => {
t.expect(() => t.device.features.has('rg11b10ufloat-renderable'));
t.expect(() => t.device.features.has('texture-formats-tier1'));
t.expect(() => hasFeature(t.device.features, 'rg11b10ufloat-renderable'));
t.expect(() => hasFeature(t.device.features, 'texture-formats-tier1'));
});
g.test('bind_group_layout,storage_binding_read_write_access')

View file

@ -1,5 +1,5 @@
import { keysOf } from '../../../../../common/util/data_tables.js';
import { assert } from '../../../../../common/util/util.js';
import { assert, hasFeature } from '../../../../../common/util/util.js';
import { align, roundDown } from '../../../../util/math.js';
import {
@ -152,7 +152,7 @@ g.test('createComputePipeline,at_over')
const { limitTest, testValueName, async, wgslType } = t.params;
const { defaultLimit, adapterLimit: maximumLimit } = t;
const hasF16 = t.adapter.features.has('shader-f16');
const hasF16 = hasFeature(t.adapter.features, 'shader-f16');
if (!hasF16 && wgslType in wgslF16Types) {
return;
}

View file

@ -3,6 +3,7 @@ This test dedicatedly tests validation of GPUPrimitiveState of createRenderPipel
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { hasFeature } from '../../../../common/util/util.js';
import { kPrimitiveTopology, kIndexFormat } from '../../../capability_info.js';
import * as vtu from '../validation_test_utils.js';
@ -38,6 +39,6 @@ g.test('unclipped_depth')
const descriptor = t.getDescriptor({ primitive: { unclippedDepth } });
const _success = !unclippedDepth || t.device.features.has('depth-clip-control');
const _success = !unclippedDepth || hasFeature(t.device.features, 'depth-clip-control');
vtu.doCreateRenderPipelineTest(t, isAsync, _success, descriptor);
});

View file

@ -12,7 +12,7 @@ import {
valueof,
} from '../common/util/data_tables.js';
import { assertTypeTrue, TypeEqual } from '../common/util/types.js';
import { unreachable } from '../common/util/util.js';
import { hasFeature, unreachable } from '../common/util/util.js';
import { GPUConst, kMaxUnsignedLongValue, kMaxUnsignedLongLongValue } from './constants.js';
@ -839,7 +839,9 @@ export function getDefaultLimitsForCTS() {
}
export function getDefaultLimitsForDevice(device: GPUDevice) {
const featureLevel = device.features.has('core-features-and-limits') ? 'core' : 'compatibility';
const featureLevel = hasFeature(device.features, 'core-features-and-limits')
? 'core'
: 'compatibility';
return getDefaultLimits(featureLevel);
}

View file

@ -1,6 +1,6 @@
import { isCompatibilityDevice } from '../common/framework/test_config.js';
import { keysOf } from '../common/util/data_tables.js';
import { assert, unreachable } from '../common/util/util.js';
import { assert, unreachable, hasFeature } from '../common/util/util.js';
import { align, roundDown } from './util/math.js';
import { getTextureDimensionFromView } from './util/texture/base.js';
@ -2068,8 +2068,10 @@ export function textureDimensionAndFormatCompatibleForDevice(
): boolean {
if (
dimension === '3d' &&
((isBCTextureFormat(format) && device.features.has('texture-compression-bc-sliced-3d')) ||
(isASTCTextureFormat(format) && device.features.has('texture-compression-astc-sliced-3d')))
((isBCTextureFormat(format) &&
hasFeature(device.features, 'texture-compression-bc-sliced-3d')) ||
(isASTCTextureFormat(format) &&
hasFeature(device.features, 'texture-compression-astc-sliced-3d')))
) {
return true;
}
@ -2357,10 +2359,10 @@ export function isTextureFormatUsableAsRenderAttachment(
format: GPUTextureFormat
) {
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
return hasFeature(device.features, 'rg11b10ufloat-renderable');
}
if (isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)) {
return device.features.has('texture-formats-tier1');
return hasFeature(device.features, 'texture-formats-tier1');
}
return kTextureFormatInfo[format].colorRender || isDepthOrStencilTextureFormat(format);
}
@ -2373,10 +2375,10 @@ export function isTextureFormatColorRenderable(
format: GPUTextureFormat
): boolean {
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
return hasFeature(device.features, 'rg11b10ufloat-renderable');
}
if (isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)) {
return device.features.has('texture-formats-tier1');
return hasFeature(device.features, 'texture-formats-tier1');
}
return !!kAllTextureFormatInfo[format].colorRender;
}
@ -2389,10 +2391,10 @@ export function isTextureFormatBlendable(device: GPUDevice, format: GPUTextureFo
return false;
}
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
return hasFeature(device.features, 'rg11b10ufloat-renderable');
}
if (is32Float(format)) {
return device.features.has('float32-blendable');
return hasFeature(device.features, 'float32-blendable');
}
return !!kAllTextureFormatInfo[format].colorRender?.blend;
}
@ -2528,12 +2530,12 @@ function isTextureFormatUsableAsWriteOnlyStorageTexture(
return false;
}
}
if (format === 'bgra8unorm' && device.features.has('bgra8unorm-storage')) {
if (format === 'bgra8unorm' && hasFeature(device.features, 'bgra8unorm-storage')) {
return true;
}
if (
isTextureFormatTier1EnablesStorageReadOnlyWriteOnly(format) &&
device.features.has('texture-formats-tier1')
hasFeature(device.features, 'texture-formats-tier1')
) {
return true;
}
@ -2610,7 +2612,7 @@ function isTextureFormatUsableAsReadWriteStorageTexture(
format: GPUTextureFormat
): boolean {
if (isTextureFormatTier2EnablesStorageReadWrite(format)) {
return device.features.has('texture-formats-tier2');
return hasFeature(device.features, 'texture-formats-tier2');
}
return !!kTextureFormatInfo[format].color?.readWriteStorage;
}
@ -2666,10 +2668,10 @@ export function isTextureFormatMultisampled(device: GPUDevice, format: GPUTextur
}
}
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
return hasFeature(device.features, 'rg11b10ufloat-renderable');
}
if (isTextureFormatTier1EnablesRenderAttachmentBlendableMultisample(format)) {
return device.features.has('texture-formats-tier1');
return hasFeature(device.features, 'texture-formats-tier1');
}
return kAllTextureFormatInfo[format].multisample;
}
@ -2680,10 +2682,10 @@ export function isTextureFormatMultisampled(device: GPUDevice, format: GPUTextur
*/
export function isTextureFormatResolvable(device: GPUDevice, format: GPUTextureFormat): boolean {
if (format === 'rg11b10ufloat') {
return device.features.has('rg11b10ufloat-renderable');
return hasFeature(device.features, 'rg11b10ufloat-renderable');
}
if (isTextureFormatTier1EnablesResolve(format)) {
return device.features.has('texture-formats-tier1');
return hasFeature(device.features, 'texture-formats-tier1');
}
// You can't resolve a non-multisampled format.
if (!isTextureFormatMultisampled(device, format)) {

View file

@ -19,6 +19,7 @@ import {
TypedArrayBufferView,
TypedArrayBufferViewConstructor,
unreachable,
hasFeature,
} from '../common/util/util.js';
import { kPossibleLimits, kQueryTypeInfo, WGSLLanguageFeature } from './capability_info.js';
@ -460,7 +461,10 @@ export class GPUTestBase extends Fixture<GPUTestSubcaseBatchState> {
* Note: Try to use one of the more specific skipIf tests if possible.
*/
skipIfDeviceDoesNotHaveFeature(feature: GPUFeatureName) {
this.skipIf(!this.device.features.has(feature), `device does not have feature: '${feature}'`);
this.skipIf(
!hasFeature(this.device.features, feature),
`device does not have feature: '${feature}'`
);
}
/**
@ -497,7 +501,7 @@ export class GPUTestBase extends Fixture<GPUTestSubcaseBatchState> {
}
const feature = getRequiredFeatureForTextureFormat(format);
this.skipIf(
!!feature && !this.device.features.has(feature),
!!feature && !hasFeature(this.device.features, feature),
`texture format '${format}' requires feature: '${feature}'`
);
}
@ -603,7 +607,7 @@ export class GPUTestBase extends Fixture<GPUTestSubcaseBatchState> {
this.skipIf(isSintOrUintFormat(format), 'sint/uint formats are not blendable');
if (is32Float(format)) {
this.skipIf(
!this.device.features.has('float32-blendable'),
!hasFeature(this.device.features, 'float32-blendable'),
`texture format '${format}' is not blendable`
);
}
@ -616,7 +620,7 @@ export class GPUTestBase extends Fixture<GPUTestSubcaseBatchState> {
this.skipIf(isSintOrUintFormat(format), 'sint/uint formats are not filterable');
if (is32Float(format)) {
this.skipIf(
!this.device.features.has('float32-filterable'),
!hasFeature(this.device.features, 'float32-filterable'),
`texture format '${format}' is not filterable`
);
}

View file

@ -1,5 +1,5 @@
import { keysOf } from '../../../../../../common/util/data_tables.js';
import { assert, range, unreachable } from '../../../../../../common/util/util.js';
import { assert, range, unreachable, hasFeature } from '../../../../../../common/util/util.js';
import { Float16Array } from '../../../../../../external/petamoriken/float16/float16.js';
import {
ColorTextureFormat,
@ -2438,7 +2438,8 @@ function getComponentsToCheck(
}
const gbaUndefined =
isDepthOrStencilTextureFormat(format) && !device.features.has('texel-component-swizzle');
isDepthOrStencilTextureFormat(format) &&
!hasFeature(device.features, 'texture-component-swizzle');
if (gbaUndefined) {
return kRComponent;
}
@ -2585,7 +2586,7 @@ export async function checkCallResults<T extends Dimensionality>(
isDepthOrStencilTextureFormat(format) &&
isBuiltinGather(call.builtin) &&
call.component! > 0 &&
!t.device.features.has('texture-component-swizzle')
!hasFeature(t.device.features, 'texture-component-swizzle')
) {
continue;
}
@ -5320,7 +5321,7 @@ ${stageWGSL}
: type ?? 'float';
if (isFiltering && sampleType === 'unfilterable-float') {
assert(is32Float(format));
assert(t.device.features.has('float32-filterable'));
assert(hasFeature(t.device.features, 'float32-filterable'));
sampleType = 'float';
}
if (sampleCount > 1 && sampleType === 'float') {

View file

@ -7,6 +7,7 @@ import {
raceWithRejectOnTimeout,
assertReject,
unreachable,
hasFeature,
} from '../../common/util/util.js';
import { getDefaultLimits, kPossibleLimits } from '../capability_info.js';
@ -319,7 +320,7 @@ function supportsFeature(
}
for (const feature of descriptor.requiredFeatures) {
if (!adapter.features.has(feature)) {
if (!hasFeature(adapter.features, feature)) {
return false;
}
}