Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions src/common/framework/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ export class Fixture<S extends SubcaseBatchState = SubcaseBatchState> {
}

/** @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;
}
Expand Down Expand Up @@ -427,12 +428,7 @@ export type SubcaseBatchStateFromFixture<F> = F extends Fixture<infer S> ? S : n
* parameter.
*/
export type FixtureClass<F extends Fixture = Fixture> = {
new (sharedState: SubcaseBatchStateFromFixture<F>, log: TestCaseRecorder, params: TestParams): F;
MakeSharedState(recorder: TestCaseRecorder, params: TestParams): SubcaseBatchStateFromFixture<F>;
};
export type FixtureClassInterface<F extends Fixture = Fixture> = {
/* 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<F>;
};
export type FixtureClassWithMixin<FC, M> = FC extends FixtureClass<infer F>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { TestCaseRecorder, TestParams } from '../../../../../common/framework/fixture.js';
import {
SubcaseBatchState,
TestCaseRecorder,
TestParams,
} from '../../../../../common/framework/fixture.js';
import {
kUnitCaseParamsBuilder,
ParamTypeOf,
Expand All @@ -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';
Expand Down Expand Up @@ -187,7 +191,7 @@ export class TextureZeroInitTest extends AllFeaturesMaxLimitsGPUTest {
readonly stateToTexelComponents: { [k in InitializedState]: PerTexelComponent<number> };

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;

Expand Down
3 changes: 3 additions & 0 deletions src/webgpu/api/validation/image_copy/layout_related.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
71 changes: 41 additions & 30 deletions src/webgpu/gpu_test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
Fixture,
FixtureClass,
FixtureClassInterface,
FixtureClassWithMixin,
SubcaseBatchState,
TestCaseRecorder,
Expand All @@ -19,6 +18,7 @@ import {
TypedArrayBufferView,
TypedArrayBufferViewConstructor,
unreachable,
skipTestCase,
} from '../common/util/util.js';

import { kLimits, kQueryTypeInfo, WGSLLanguageFeature } from './capability_info.js';
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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<GPUTestSubcaseBatchState> {
export class GPUTestBase<
S extends GPUTestSubcaseBatchState = GPUTestSubcaseBatchState,
> extends Fixture<S> {
public static override MakeSharedState(
recorder: TestCaseRecorder,
params: TestParams
Expand Down Expand Up @@ -484,23 +486,7 @@ export class GPUTestBase extends Fixture<GPUTestSubcaseBatchState> {
/**
* 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,
Expand Down Expand Up @@ -1378,7 +1364,9 @@ export class GPUTestBase extends Fixture<GPUTestSubcaseBatchState> {
/**
* Fixture for WebGPU tests that uses a DeviceProvider
*/
export class GPUTest extends GPUTestBase {
export class GPUTest<
S extends GPUTestSubcaseBatchState = GPUTestSubcaseBatchState,
> extends GPUTestBase<S> {
// Should never be undefined in a test. If it is, init() must not have run/finished.
private provider: DeviceProvider | undefined;
private mismatchedProvider: DeviceProvider | undefined;
Expand Down Expand Up @@ -1580,7 +1568,7 @@ export function RequiredLimitsTestMixin<F extends FixtureClass<GPUTestBase>>(
requiredLimitsHelper: RequiredLimitsHelper
): FixtureClassWithMixin<F, RequiredLimitsTestMixinType> {
class RequiredLimitsImpl
extends (Base as FixtureClassInterface<GPUTestBase>)
extends (Base as FixtureClass<GPUTestBase>)
implements RequiredLimitsTestMixinType
{
//
Expand All @@ -1599,12 +1587,20 @@ export function RequiredLimitsTestMixin<F extends FixtureClass<GPUTestBase>>(
* 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<void> {
// AllFeaturesMaxLimits can get the device at init() rather than waiting for postInit().
this.device = (await this.acquireProvider()).device;
}

override requestDeviceWithRequiredParametersOrSkip(
descriptor: DeviceSelectionDescriptor,
descriptorModifier?: DescriptorModifier
Expand Down Expand Up @@ -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}'`);
}
}
}

Expand Down Expand Up @@ -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<AllFeaturesMaxLimitsGPUTestSubcaseBatchState> {
public static override MakeSharedState(recorder: TestCaseRecorder, params: TestParams) {
return new AllFeaturesMaxLimitsGPUTestSubcaseBatchState(recorder, params);
}
}
Loading