diff --git a/src/common/framework/fixture.ts b/src/common/framework/fixture.ts index 25e8f506b952..b7253bc54721 100644 --- a/src/common/framework/fixture.ts +++ b/src/common/framework/fixture.ts @@ -78,8 +78,9 @@ export class Fixture { } /** @internal */ - constructor(sharedState: S, rec: TestCaseRecorder, params: TestParams) { - this._sharedState = sharedState; + constructor(sharedState: SubcaseBatchState, rec: TestCaseRecorder, params: TestParams) { + // sharedState will always come from our own MakeSharedState, so it's safe to cast in practice. + this._sharedState = sharedState as S; this.rec = rec; this._params = params; } @@ -427,12 +428,7 @@ export type SubcaseBatchStateFromFixture = F extends Fixture ? S : n * parameter. */ export type FixtureClass = { - new (sharedState: SubcaseBatchStateFromFixture, log: TestCaseRecorder, params: TestParams): F; - MakeSharedState(recorder: TestCaseRecorder, params: TestParams): SubcaseBatchStateFromFixture; -}; -export type FixtureClassInterface = { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - new (...args: any[]): F; + new (sharedState: SubcaseBatchState, log: TestCaseRecorder, params: TestParams): F; MakeSharedState(recorder: TestCaseRecorder, params: TestParams): SubcaseBatchStateFromFixture; }; export type FixtureClassWithMixin = FC extends FixtureClass diff --git a/src/webgpu/api/operation/resource_init/check_texture/texture_zero_init_test.ts b/src/webgpu/api/operation/resource_init/check_texture/texture_zero_init_test.ts index b383c2e02019..d134a3786189 100644 --- a/src/webgpu/api/operation/resource_init/check_texture/texture_zero_init_test.ts +++ b/src/webgpu/api/operation/resource_init/check_texture/texture_zero_init_test.ts @@ -1,4 +1,8 @@ -import { TestCaseRecorder, TestParams } from '../../../../../common/framework/fixture.js'; +import { + SubcaseBatchState, + TestCaseRecorder, + TestParams, +} from '../../../../../common/framework/fixture.js'; import { kUnitCaseParamsBuilder, ParamTypeOf, @@ -22,7 +26,7 @@ import { isTextureFormatColorRenderable, isTextureFormatPossiblyUsableAsColorRenderAttachment, } from '../../../../format_info.js'; -import { AllFeaturesMaxLimitsGPUTest, GPUTestSubcaseBatchState } from '../../../../gpu_test.js'; +import { AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js'; import { virtualMipSize } from '../../../../util/texture/base.js'; import { createTextureUploadBuffer } from '../../../../util/texture/layout.js'; import { BeginEndRange, SubresourceRange } from '../../../../util/texture/subresource.js'; @@ -187,7 +191,7 @@ export class TextureZeroInitTest extends AllFeaturesMaxLimitsGPUTest { readonly stateToTexelComponents: { [k in InitializedState]: PerTexelComponent }; private p: TextureZeroParams; - constructor(sharedState: GPUTestSubcaseBatchState, rec: TestCaseRecorder, params: TestParams) { + constructor(sharedState: SubcaseBatchState, rec: TestCaseRecorder, params: TestParams) { super(sharedState, rec, params); this.p = params as TextureZeroParams; diff --git a/src/webgpu/api/validation/image_copy/layout_related.spec.ts b/src/webgpu/api/validation/image_copy/layout_related.spec.ts index 0aef942548c6..9587318c3801 100644 --- a/src/webgpu/api/validation/image_copy/layout_related.spec.ts +++ b/src/webgpu/api/validation/image_copy/layout_related.spec.ts @@ -181,6 +181,9 @@ Test the computation of requiredBytesInCopy by computing the minimum data size f return [p._offsetMultiplier * getBlockInfoForSizedTextureFormat(p.format).bytesPerBlock]; }) ) + .beforeAllSubcases(t => { + t.skipIfTextureFormatNotSupported(t.params.format); + }) .fn(t => { const { offset, diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index cb3da556a2da..12d537e58ddc 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -1,7 +1,6 @@ import { Fixture, FixtureClass, - FixtureClassInterface, FixtureClassWithMixin, SubcaseBatchState, TestCaseRecorder, @@ -19,6 +18,7 @@ import { TypedArrayBufferView, TypedArrayBufferViewConstructor, unreachable, + skipTestCase, } from '../common/util/util.js'; import { kLimits, kQueryTypeInfo, WGSLLanguageFeature } from './capability_info.js'; @@ -185,7 +185,7 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { descriptor: DeviceSelectionDescriptor, descriptorModifier?: DescriptorModifier ): void { - assert(this.provider === undefined, "Can't selectDeviceOrSkipTestCase() multiple times"); + assert(this.provider === undefined, "Can't change device parameters after getting a device"); this.provider = devicePool.acquire( this.recorder, initUncanonicalizedDeviceDescriptor(descriptor), @@ -328,7 +328,9 @@ export class GPUTestSubcaseBatchState extends SubcaseBatchState { * This class is a Fixture + a getter that returns a GPUDevice * as well as helpers that use that device. */ -export class GPUTestBase extends Fixture { +export class GPUTestBase< + S extends GPUTestSubcaseBatchState = GPUTestSubcaseBatchState, +> extends Fixture { public static override MakeSharedState( recorder: TestCaseRecorder, params: TestParams @@ -484,23 +486,7 @@ export class GPUTestBase extends Fixture { /** * Skips test if any format is not supported. */ - skipIfTextureFormatNotSupported(...formats: (GPUTextureFormat | undefined)[]) { - for (const format of formats) { - if (!format) { - continue; - } - if (format === 'bgra8unorm-srgb') { - if (isCompatibilityDevice(this.device)) { - this.skip(`texture format '${format}' is not supported`); - } - } - const feature = getRequiredFeatureForTextureFormat(format); - this.skipIf( - !!feature && !this.device.features.has(feature), - `texture format '${format}' requires feature: '${feature}'` - ); - } - } + skipIfTextureFormatNotSupported = skipIfTextureFormatNotSupported_unbound.bind(this); skipIfTextureFormatAndViewDimensionNotCompatible( format: GPUTextureFormat, @@ -1378,7 +1364,9 @@ export class GPUTestBase extends Fixture { /** * Fixture for WebGPU tests that uses a DeviceProvider */ -export class GPUTest extends GPUTestBase { +export class GPUTest< + S extends GPUTestSubcaseBatchState = GPUTestSubcaseBatchState, +> extends GPUTestBase { // Should never be undefined in a test. If it is, init() must not have run/finished. private provider: DeviceProvider | undefined; private mismatchedProvider: DeviceProvider | undefined; @@ -1580,7 +1568,7 @@ export function RequiredLimitsTestMixin>( requiredLimitsHelper: RequiredLimitsHelper ): FixtureClassWithMixin { class RequiredLimitsImpl - extends (Base as FixtureClassInterface) + extends (Base as FixtureClass) implements RequiredLimitsTestMixinType { // @@ -1599,12 +1587,20 @@ export function RequiredLimitsTestMixin>( * Used by AllFeaturesMaxLimitsGPUTest to request a device with all limits and features of the adapter. */ export class AllFeaturesMaxLimitsGPUTestSubcaseBatchState extends GPUTestSubcaseBatchState { + public device: GPUDevice = undefined!; // Will be set in init() + constructor( protected override readonly recorder: TestCaseRecorder, public override readonly params: TestParams ) { super(recorder, params); } + + override async init(): Promise { + // AllFeaturesMaxLimits can get the device at init() rather than waiting for postInit(). + this.device = (await this.acquireProvider()).device; + } + override requestDeviceWithRequiredParametersOrSkip( descriptor: DeviceSelectionDescriptor, descriptorModifier?: DescriptorModifier @@ -1654,10 +1650,28 @@ export class AllFeaturesMaxLimitsGPUTestSubcaseBatchState extends GPUTestSubcase } /** - * Use skipIfDeviceDoesNotHaveFeature or skipIf(device.limits.maxXXX < requiredXXX) etc... + * Skips test if any format is not supported. */ - selectMismatchedDeviceOrSkipTestCase(descriptor: DeviceSelectionDescriptor): void { - unreachable('this function should not be called in AllFeaturesMaxLimitsGPUTest'); + skipIfTextureFormatNotSupported = skipIfTextureFormatNotSupported_unbound.bind(this); +} + +function skipIfTextureFormatNotSupported_unbound( + this: { device: GPUDevice }, + ...formats: (GPUTextureFormat | undefined)[] +) { + for (const format of formats) { + if (!format) { + continue; + } + if (format === 'bgra8unorm-srgb') { + if (isCompatibilityDevice(this.device)) { + skipTestCase(`texture format '${format}' is not supported`); + } + } + const feature = getRequiredFeatureForTextureFormat(format); + if (!!feature && !this.device.features.has(feature)) { + skipTestCase(`texture format '${format}' requires feature: '${feature}'`); + } } } @@ -1690,11 +1704,8 @@ export class UniqueFeaturesOrLimitsGPUTest extends GPUTest {} * You could enable it manually but that spreads enabling to every test instead of being * centralized in one place, here. */ -export class AllFeaturesMaxLimitsGPUTest extends GPUTest { - public static override MakeSharedState( - recorder: TestCaseRecorder, - params: TestParams - ): GPUTestSubcaseBatchState { +export class AllFeaturesMaxLimitsGPUTest extends GPUTest { + public static override MakeSharedState(recorder: TestCaseRecorder, params: TestParams) { return new AllFeaturesMaxLimitsGPUTestSubcaseBatchState(recorder, params); } }