Skip to content

Commit

Permalink
Add AllFeaturesMaxLimitsGPUTest (#4184)
Browse files Browse the repository at this point in the history
This is  test that requests all features and maximum limits. This should be the default
test for the majority of tests, otherwise optional features will not be tested.
The exceptions are only tests that explicitly test the absence of a feature or
specific limits such as the tests under validation/capability_checks.

As a concrete example to demonstrate the issue, texture format `rg11b10ufloat` is
optionally renderable and can optionally be used multisampled. Any test that tests
texture formats should test this format, skipping only if the feature is missing.
So, the default should be that the test tests `kAllTextureFormats` with the appropriate
filters from format_info.ts or the various helpers. This way, `rg11b10ufloat` will
included in the test and fail if not appropriately filtered. If instead you were
to use GPUTest then `rg11b10ufloat` would just be skipped as its never enabled.
You could enable it manually but that spreads enabling to every test instead of being
centralized in one place, here.

Honestly, I'd prefer to rename `GPUTest` to `SpecialGPUTest` or possibly even
`DeprecatedGPUTest` it clear not to use it and where it needs to be fixed and then
name this `GPUTest`. But, we can do that later. For now, I just effectively replaced
`MaxLimitsTestMixin` with `AllFeaturesMaxLimitsGPUTest` in the places where it could
easily be replaced.

The remaining places bring up an issue. All of them are based on `ValidationTest`.
I think the same rules apply. For example, if we're validating that textures fail
validation for certain reasons we should be checking formats that are optional
also pass the same validation rules. The simplest wasy to do that would be
to make `ValidationTest` inherit from `AllFeaturesMaxLimitsGPUTest`.

Unforunately, the capability_checks/* are all based off `ValidationTest`
so they need to be moved to something else before we can make that change.
  • Loading branch information
greggman authored Feb 11, 2025
1 parent 294a0e3 commit a71a20e
Show file tree
Hide file tree
Showing 22 changed files with 123 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { unreachable } from '../../../../../common/util/util.js';
import { GPUTest, GPUTestBase } from '../../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTestBase } from '../../../../gpu_test.js';
import { EncoderType } from '../../../../util/command_buffer_maker.js';

interface BindGroupIndices {
Expand All @@ -12,7 +12,7 @@ type CreateEncoderType = ReturnType<
typeof GPUTestBase.prototype.createEncoder<'compute pass' | 'render pass' | 'render bundle'>
>['encoder'];

export class ProgrammableStateTest extends GPUTest {
export class ProgrammableStateTest extends AllFeaturesMaxLimitsGPUTest {
private commonBindGroupLayouts: Map<string, GPUBindGroupLayout> = new Map();

skipIfNeedsStorageBuffersInFragmentStageAndHaveNone(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ times in different orders) for setBindGroup and setPipeline.

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { GPUConst } from '../../../../constants.js';
import { MaxLimitsTestMixin } from '../../../../gpu_test.js';
import { kProgrammableEncoderTypes } from '../../../../util/command_buffer_maker.js';

import { ProgrammableStateTest } from './programmable_state_test.js';

export const g = makeTestGroup(MaxLimitsTestMixin(ProgrammableStateTest));
export const g = makeTestGroup(ProgrammableStateTest);

const kBufferUsage =
GPUConst.BufferUsage.COPY_SRC |
Expand Down
4 changes: 2 additions & 2 deletions src/webgpu/api/operation/limits/max_combined_limits.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ implementation allows, all of them are useable.
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { range } from '../../../../common/util/util.js';
import { kTextureFormatInfo } from '../../../format_info.js';
import { GPUTest, MaxLimitsTestMixin, TextureTestMixin } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, TextureTestMixin } from '../../../gpu_test.js';
import { TexelView } from '../../../util/texture/texel_view.js';

export const g = makeTestGroup(TextureTestMixin(MaxLimitsTestMixin(GPUTest)));
export const g = makeTestGroup(TextureTestMixin(AllFeaturesMaxLimitsGPUTest));

g.test('max_storage_buffer_texture_frag_outputs')
.desc(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert, unreachable } from '../../../../../common/util/util.js';
import { GPUTest } from '../../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js';
import { checkElementsEqualEither } from '../../../../util/check_contents.js';
import { OperationContext, OperationContextHelper } from '../operation_context_helper.js';

Expand Down Expand Up @@ -150,7 +150,7 @@ const kDummyVertexShader = `

// Note: If it would be useful to have any of these helpers be separate from the fixture,
// they can be refactored into standalone functions.
export class BufferSyncTest extends GPUTest {
export class BufferSyncTest extends AllFeaturesMaxLimitsGPUTest {
// Vertex and index buffers used in read render pass
vertexBuffer?: GPUBuffer;
indexBuffer?: GPUBuffer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ TODO: Tests with more than one buffer to try to stress implementations a little
`;

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { MaxLimitsTestMixin } from '../../../../gpu_test.js';
import {
kOperationBoundaries,
kBoundaryInfo,
Expand All @@ -35,7 +34,7 @@ const kSrcValue = 0;
// The op value is what the read/write operation write into the target buffer.
const kOpValue = 1;

export const g = makeTestGroup(MaxLimitsTestMixin(BufferSyncTest));
export const g = makeTestGroup(BufferSyncTest);

g.test('rw')
.desc(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ Wait on another fence, then call expectContents to verify the dst buffer value.
`;

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { MaxLimitsTestMixin } from '../../../../gpu_test.js';
import {
kOperationBoundaries,
kBoundaryInfo,
Expand All @@ -33,7 +32,7 @@ const kSrcValue = 0;
// The op value is what the read/write operation write into the target buffer.
const kOpValue = 1;

export const g = makeTestGroup(MaxLimitsTestMixin(BufferSyncTest));
export const g = makeTestGroup(BufferSyncTest);

g.test('rw')
.desc(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Memory Synchronization Tests for Texture: read before write, read after write, a
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { assert, memcpy, unreachable } from '../../../../../common/util/util.js';
import { EncodableTextureFormat } from '../../../../format_info.js';
import { GPUTest, MaxLimitsTestMixin } from '../../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../../../gpu_test.js';
import { align } from '../../../../util/math.js';
import { getTextureCopyLayout } from '../../../../util/texture/layout.js';
import {
Expand All @@ -31,7 +31,7 @@ import {
kOpInfo,
} from './texture_sync_test.js';

export const g = makeTestGroup(MaxLimitsTestMixin(GPUTest));
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);

const fullscreenQuadWGSL = `
struct VertexOutput {
Expand Down
4 changes: 2 additions & 2 deletions src/webgpu/api/operation/rendering/depth_clip_clamp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ depth ranges as well.
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert } from '../../../../common/util/util.js';
import { kDepthStencilFormats, kTextureFormatInfo } from '../../../format_info.js';
import { GPUTest, MaxLimitsTestMixin } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';
import {
checkElementsBetween,
checkElementsPassPredicate,
CheckElementsSupplementalTableRows,
} from '../../../util/check_contents.js';

export const g = makeTestGroup(MaxLimitsTestMixin(GPUTest));
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);

g.test('depth_clamp_and_clip')
.desc(
Expand Down
4 changes: 2 additions & 2 deletions src/webgpu/api/operation/rendering/draw.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
TypedArrayBufferView,
TypedArrayBufferViewConstructor,
} from '../../../../common/util/util.js';
import { GPUTest, MaxLimitsTestMixin, TextureTestMixin } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, TextureTestMixin } from '../../../gpu_test.js';
import { PerPixelComparison } from '../../../util/texture/texture_ok.js';

class DrawTest extends TextureTestMixin(MaxLimitsTestMixin(GPUTest)) {
class DrawTest extends TextureTestMixin(AllFeaturesMaxLimitsGPUTest) {
checkTriangleDraw(opts: {
firstIndex: number | undefined;
count: number;
Expand Down
4 changes: 2 additions & 2 deletions src/webgpu/api/operation/sampling/sampler_texture.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ Tests samplers with textures.

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert, range } from '../../../../common/util/util.js';
import { GPUTest, MaxLimitsTestMixin, TextureTestMixin } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, TextureTestMixin } from '../../../gpu_test.js';
import { TexelView } from '../../../util/texture/texel_view.js';

export const g = makeTestGroup(TextureTestMixin(MaxLimitsTestMixin(GPUTest)));
export const g = makeTestGroup(TextureTestMixin(AllFeaturesMaxLimitsGPUTest));

g.test('sample_texture_combos')
.desc(
Expand Down
6 changes: 3 additions & 3 deletions src/webgpu/api/operation/storage_texture/read_only.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
kColorTextureFormats,
kTextureFormatInfo,
} from '../../../format_info.js';
import { GPUTest, MaxLimitsTestMixin } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';
import { kValidShaderStages, TValidShaderStage } from '../../../util/shader.js';

function ComponentCount(format: ColorTextureFormat): number {
Expand Down Expand Up @@ -46,7 +46,7 @@ function ComponentCount(format: ColorTextureFormat): number {
}
}

class F extends GPUTest {
class F extends AllFeaturesMaxLimitsGPUTest {
initTextureAndGetExpectedOutputBufferData(
storageTexture: GPUTexture,
format: ColorTextureFormat
Expand Down Expand Up @@ -532,7 +532,7 @@ class F extends GPUTest {
}
}

export const g = makeTestGroup(MaxLimitsTestMixin(F));
export const g = makeTestGroup(F);

g.test('basic')
.desc(
Expand Down
6 changes: 3 additions & 3 deletions src/webgpu/api/operation/storage_texture/read_write.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert, unreachable } from '../../../../common/util/util.js';
import { kTextureDimensions } from '../../../capability_info.js';
import { kColorTextureFormats, kTextureFormatInfo } from '../../../format_info.js';
import { GPUTest, MaxLimitsTestMixin } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';
import { align } from '../../../util/math.js';

const kShaderStagesForReadWriteStorageTexture = ['fragment', 'compute'] as const;
type ShaderStageForReadWriteStorageTexture =
(typeof kShaderStagesForReadWriteStorageTexture)[number];

class F extends GPUTest {
class F extends AllFeaturesMaxLimitsGPUTest {
getInitialData(storageTexture: GPUTexture): ArrayBuffer {
const format = storageTexture.format;
const bytesPerBlock = kTextureFormatInfo[format].bytesPerBlock;
Expand Down Expand Up @@ -298,7 +298,7 @@ class F extends GPUTest {
}
}

export const g = makeTestGroup(MaxLimitsTestMixin(F));
export const g = makeTestGroup(F);

g.test('basic')
.desc(
Expand Down
4 changes: 2 additions & 2 deletions src/webgpu/api/operation/texture_view/write.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import {
kTextureFormatInfo,
RegularTextureFormat,
} from '../../../format_info.js';
import { GPUTest, MaxLimitsTestMixin, TextureTestMixin } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTest, TextureTestMixin } from '../../../gpu_test.js';
import { kFullscreenQuadVertexShaderCode } from '../../../util/shader.js';
import { TexelView } from '../../../util/texture/texel_view.js';

export const g = makeTestGroup(TextureTestMixin(MaxLimitsTestMixin(GPUTest)));
export const g = makeTestGroup(TextureTestMixin(AllFeaturesMaxLimitsGPUTest));

const kTextureViewWriteMethods = [
'storage-write-fragment',
Expand Down
6 changes: 3 additions & 3 deletions src/webgpu/api/operation/vertex_state/correctness.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
unreachable,
} from '../../../../common/util/util.js';
import { kVertexFormatInfo, kVertexFormats } from '../../../capability_info.js';
import { GPUTest, MaxLimitsTestMixin } from '../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';
import { float32ToFloat16Bits, normalizedIntegerAsFloat } from '../../../util/conversion.js';
import { align, clamp } from '../../../util/math.js';

Expand Down Expand Up @@ -83,7 +83,7 @@ type TestData = {
vertexData: ArrayBuffer;
};

class VertexStateTest extends GPUTest {
class VertexStateTest extends AllFeaturesMaxLimitsGPUTest {
// Generate for VS + FS (entrypoints vsMain / fsMain) that for each attribute will check that its
// value corresponds to what's expected (as provided by a uniform buffer per attribute) and then
// renders each vertex at position (vertexIndex, instanceindex) with either 1 (success) or
Expand Down Expand Up @@ -639,7 +639,7 @@ struct VSOutputs {
}
}

export const g = makeTestGroup(MaxLimitsTestMixin(VertexStateTest));
export const g = makeTestGroup(VertexStateTest);

g.test('vertex_format_to_shader_format_conversion')
.desc(
Expand Down
78 changes: 78 additions & 0 deletions src/webgpu/gpu_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,25 @@ function applyLimitsToDescriptor(
return descWithMaxLimits;
}

function getAdapterFeaturesAsDeviceRequiredFeatures(adapter: GPUAdapter): Iterable<GPUFeatureName> {
return adapter.features as Iterable<GPUFeatureName>;
}

function applyFeaturesToDescriptor(
adapter: GPUAdapter,
desc: CanonicalDeviceDescriptor | undefined,
getRequiredFeatures: (adapter: GPUAdapter) => Iterable<GPUFeatureName>
) {
const existingRequiredFeatures = (desc && desc?.requiredFeatures) ?? [];
const descWithRequiredFeatures: CanonicalDeviceDescriptor = {
requiredLimits: {},
defaultQueue: {},
...desc,
requiredFeatures: [...existingRequiredFeatures, ...getRequiredFeatures(adapter)],
};
return descWithRequiredFeatures;
}

/**
* Used by RequiredLimitsTestMixin to allow you to request specific limits
*
Expand Down Expand Up @@ -1474,6 +1493,65 @@ export function MaxLimitsTestMixin<F extends FixtureClass<GPUTestBase>>(Base: F)
});
}

/**
* Used by AllFeaturesMaxLimitsGPUTest to request a device with all limits and features of the adapter.
*/
export class AllFeaturesMaxLimitsGPUTestSubcaseBatchState extends GPUTestSubcaseBatchState {
constructor(
protected override readonly recorder: TestCaseRecorder,
public override readonly params: TestParams
) {
super(recorder, params);
}
override requestDeviceWithRequiredParametersOrSkip(
descriptor: DeviceSelectionDescriptor,
descriptorModifier?: DescriptorModifier
): void {
const mod: DescriptorModifier = {
descriptorModifier(adapter: GPUAdapter, desc: CanonicalDeviceDescriptor | undefined) {
desc = descriptorModifier?.descriptorModifier
? descriptorModifier.descriptorModifier(adapter, desc)
: desc;
desc = applyLimitsToDescriptor(adapter, desc, getAdapterLimitsAsDeviceRequiredLimits);
desc = applyFeaturesToDescriptor(adapter, desc, getAdapterFeaturesAsDeviceRequiredFeatures);
return desc;
},
keyModifier(baseKey: string) {
return `${baseKey}:AllFeaturesMaxLimits`;
},
};
super.requestDeviceWithRequiredParametersOrSkip(
initUncanonicalizedDeviceDescriptor(descriptor),
mod
);
}
}

/**
* A test that requests all features and maximum limits. This should be the default
* test for the majority of tests, otherwise optional features will not be tested.
* The exceptions are only tests that explicitly test the absence of a feature or
* specific limits such as the tests under validation/capability_checks.
*
* As a concrete example to demonstrate the issue, texture format `rg11b10ufloat` is
* optionally renderable and can optionally be used multisampled. Any test that tests
* texture formats should test this format, skipping only if the feature is missing.
* So, the default should be that the test tests `kAllTextureFormats` with the appropriate
* filters from format_info.ts or the various helpers. This way, `rg11b10ufloat` will
* included in the test and fail if not appropriately filtered. If instead you were
* to use GPUTest then `rg11b10ufloat` would just be skipped as its never enabled.
* 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 {
return new AllFeaturesMaxLimitsGPUTestSubcaseBatchState(recorder, params);
}
}

/**
* Texture expectation mixin can be applied on top of GPUTest to add texture
* related expectation helpers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@ import {
sampleTypeForFormatAndAspect,
textureDimensionAndFormatCompatible,
} from '../../../../../format_info.js';
import { MaxLimitsTestMixin } from '../../../../../gpu_test.js';
import { align } from '../../../../../util/math.js';
import { kShaderStages, ShaderStage } from '../../../../validation/decl/util.js';

import { WGSLTextureQueryTest } from './texture_utils.js';

export const g = makeTestGroup(MaxLimitsTestMixin(WGSLTextureQueryTest));
export const g = makeTestGroup(WGSLTextureQueryTest);

/// The maximum number of texture mipmap levels to test.
/// Keep this small to reduce memory and test permutations.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
kTextureFormatInfo,
textureDimensionAndFormatCompatible,
} from '../../../../../format_info.js';
import { GPUTest, MaxLimitsTestMixin } from '../../../../../gpu_test.js';
import { AllFeaturesMaxLimitsGPUTest, GPUTest } from '../../../../../gpu_test.js';
import { maxMipLevelCount, virtualMipSize } from '../../../../../util/texture/base.js';
import { TexelFormats } from '../../../../types.js';

Expand Down Expand Up @@ -79,7 +79,7 @@ function skipIfStorageTexturesNotSupportedInStage(t: GPUTest, stage: ShortShader
}
}

export const g = makeTestGroup(MaxLimitsTestMixin(GPUTest));
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);

g.test('sampled_1d')
.specURL('https://www.w3.org/TR/WGSL/#textureload')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ Returns the number of layers (elements) of an array texture.

import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { kTextureFormatInfo } from '../../../../../format_info.js';
import { MaxLimitsTestMixin } from '../../../../../gpu_test.js';
import { TexelFormats } from '../../../../types.js';
import { kShaderStages } from '../../../../validation/decl/util.js';

Expand Down Expand Up @@ -35,7 +34,7 @@ function getLayerSettingsAndExpected({
};
}

export const g = makeTestGroup(MaxLimitsTestMixin(WGSLTextureQueryTest));
export const g = makeTestGroup(WGSLTextureQueryTest);

g.test('sampled')
.specURL('https://www.w3.org/TR/WGSL/#texturenumlayers')
Expand Down
Loading

0 comments on commit a71a20e

Please sign in to comment.