Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compat: refactor resource_usages tests for 0 storage buffer/textures #4126

Merged
merged 1 commit into from
Dec 31, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Buffer Usages Validation Tests in Render Pass and Compute Pass.

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { assert, unreachable } from '../../../../../common/util/util.js';
import { GPUTestBase, MaxLimitsTestMixin } from '../../../../gpu_test.js';
import { ValidationTest } from '../../validation_test.js';

const kBoundBufferSize = 256;
Expand All @@ -27,15 +28,18 @@ export const kAllBufferUsages: BufferUsage[] = [
'indexedIndirect',
];

function resourceVisibilityToVisibility(resourceVisibility: 'compute' | 'fragment') {
return resourceVisibility === 'compute' ? GPUShaderStage.COMPUTE : GPUShaderStage.FRAGMENT;
}

export class BufferResourceUsageTest extends ValidationTest {
createBindGroupLayoutForTest(
type: 'uniform' | 'storage' | 'read-only-storage',
resourceVisibility: 'compute' | 'fragment'
): GPUBindGroupLayout {
const bindGroupLayoutEntry: GPUBindGroupLayoutEntry = {
binding: 0,
visibility:
resourceVisibility === 'compute' ? GPUShaderStage.COMPUTE : GPUShaderStage.FRAGMENT,
visibility: resourceVisibilityToVisibility(resourceVisibility),
buffer: {
type,
},
Expand Down Expand Up @@ -138,7 +142,45 @@ function IsBufferUsageInBindGroup(bufferUsage: BufferUsage): boolean {
}
}

export const g = makeTestGroup(BufferResourceUsageTest);
function skipIfStorageBuffersNotAvailableInStages(
t: GPUTestBase,
visibility: number,
numRequired: number
) {
if (t.isCompatibility) {
t.skipIf(
(visibility & GPUShaderStage.FRAGMENT) !== 0 &&
!(t.device.limits.maxStorageBuffersInFragmentStage! >= numRequired),
`maxStorageBuffersInFragmentStage${t.device.limits.maxStorageBuffersInFragmentStage} < ${numRequired}`
);
t.skipIf(
(visibility & GPUShaderStage.VERTEX) !== 0 &&
!(t.device.limits.maxStorageBuffersInVertexStage! >= numRequired),
`maxStorageBuffersInVertexStage${t.device.limits.maxStorageBuffersInVertexStage} < ${numRequired}`
);
}
}

/**
* Skips test if usage is a storage buffer and there are not numRequired
* storage buffers supported for the given visibility.
*/
export function skipIfStorageBuffersUsedAndNotAvailableInStages(
t: GPUTestBase,
usage: BufferUsage | 'copy-src' | 'copy-dst',
visibility: 'fragment' | 'compute',
numRequired: number
) {
if (usage === 'storage' || usage === 'read-only-storage') {
skipIfStorageBuffersNotAvailableInStages(
t,
resourceVisibilityToVisibility(visibility),
numRequired
);
}
}

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

g.test('subresources,buffer_usage_in_one_compute_pass_with_no_dispatch')
.desc(
Expand All @@ -158,6 +200,19 @@ bindGroup, dynamicOffsets), do not contribute directly to a usage scope.`
)
.fn(t => {
const { usage0, usage1, visibility0, visibility1, hasOverlap } = t.params;
const numStorageBuffersNeededInFragmentStage = 1;
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage0,
visibility0,
numStorageBuffersNeededInFragmentStage
);
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage1,
visibility1,
numStorageBuffersNeededInFragmentStage
);

const buffer = t.createBufferWithState('valid', {
size: kBoundBufferSize * 2,
Expand Down Expand Up @@ -255,6 +310,19 @@ have tests covered (https://github.com/gpuweb/cts/issues/2232)
visibility1,
hasOverlap,
} = t.params;
const numStorageBuffersNeededInFragmentStage = 1;
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage0,
visibility0,
numStorageBuffersNeededInFragmentStage
);
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage1,
visibility1,
numStorageBuffersNeededInFragmentStage
);

const buffer = t.createBufferWithState('valid', {
size: kBoundBufferSize * 2,
Expand Down Expand Up @@ -476,6 +544,20 @@ there is no draw call in the render pass.
.fn(t => {
const { usage0, usage1, hasOverlap, visibility0, visibility1 } = t.params;

const numStorageBuffersNeededInFragmentStage = 1;
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage0,
visibility0,
numStorageBuffersNeededInFragmentStage
);
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage1,
visibility1,
numStorageBuffersNeededInFragmentStage
);

const UseBufferOnRenderPassEncoder = (
buffer: GPUBuffer,
offset: number,
Expand Down Expand Up @@ -627,6 +709,21 @@ have tests covered (https://github.com/gpuweb/cts/issues/2232)
visibility1,
hasOverlap,
} = t.params;

const numStorageBuffersNeededInFragmentStage = 1;
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage0,
visibility0,
numStorageBuffersNeededInFragmentStage
);
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage1,
visibility1,
numStorageBuffersNeededInFragmentStage
);

const buffer = t.createBufferWithState('valid', {
size: kBoundBufferSize * 2,
usage:
Expand Down Expand Up @@ -849,6 +946,21 @@ different render pass encoders belong to different usage scopes.`
GPUBufferUsage.INDEX |
GPUBufferUsage.INDIRECT,
});

const numStorageBuffersNeededInFragmentStage = 1;
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage0,
'fragment',
numStorageBuffersNeededInFragmentStage
);
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage1,
'fragment',
numStorageBuffersNeededInFragmentStage
);

const UseBufferOnRenderPassEncoderInDrawCall = (
offset: number,
usage: BufferUsage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ Test other buffer usage validation rules that are not tests in ./in_pass_encoder

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { unreachable } from '../../../../../common/util/util.js';
import { MaxLimitsTestMixin } from '../../../../gpu_test.js';

import { BufferUsage, BufferResourceUsageTest, kAllBufferUsages } from './in_pass_encoder.spec.js';
import {
BufferUsage,
BufferResourceUsageTest,
kAllBufferUsages,
skipIfStorageBuffersUsedAndNotAvailableInStages,
} from './in_pass_encoder.spec.js';

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

const kBufferSize = 256;

Expand Down Expand Up @@ -95,6 +101,9 @@ still contribute directly to the usage scope of the draw call.`
.fn(t => {
const { usage0, usage1 } = t.params;

skipIfStorageBuffersUsedAndNotAvailableInStages(t, usage0, 'fragment', 1);
skipIfStorageBuffersUsedAndNotAvailableInStages(t, usage1, 'fragment', 1);

const kUsages =
GPUBufferUsage.UNIFORM |
GPUBufferUsage.STORAGE |
Expand Down Expand Up @@ -247,7 +256,7 @@ g.test('subresources,buffer_usages_in_copy_and_pass')
'indirect',
'indexedIndirect',
] as const)
.combine('pass', ['render', 'compute'])
.combine('pass', ['render', 'compute'] as const)
.unless(({ usage0, usage1, pass }) => {
const IsCopy = (usage: BufferUsage | 'copy-src' | 'copy-dst') => {
return usage === 'copy-src' || usage === 'copy-dst';
Expand Down Expand Up @@ -282,6 +291,19 @@ g.test('subresources,buffer_usages_in_copy_and_pass')
.fn(t => {
const { usage0, usage1, pass } = t.params;

skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage0,
pass === 'render' ? 'fragment' : 'compute',
1
);
skipIfStorageBuffersUsedAndNotAvailableInStages(
t,
usage1,
pass === 'render' ? 'fragment' : 'compute',
1
);

const kUsages =
GPUBufferUsage.COPY_SRC |
GPUBufferUsage.COPY_DST |
Expand Down Expand Up @@ -328,15 +350,16 @@ g.test('subresources,buffer_usages_in_copy_and_pass')
case 'uniform':
case 'storage':
case 'read-only-storage': {
const bindGroup = t.createBindGroupForTest(buffer, 0, usage, 'fragment');
switch (pass) {
case 'render': {
const bindGroup = t.createBindGroupForTest(buffer, 0, usage, 'fragment');
const renderPassEncoder = t.beginSimpleRenderPass(encoder);
renderPassEncoder.setBindGroup(0, bindGroup);
renderPassEncoder.end();
break;
}
case 'compute': {
const bindGroup = t.createBindGroupForTest(buffer, 0, usage, 'compute');
const computePassEncoder = encoder.beginComputePass();
computePassEncoder.setBindGroup(0, bindGroup);
computePassEncoder.end();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Texture Usages Validation Tests in Same or Different Render Pass Encoders.

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { assert, unreachable } from '../../../../../common/util/util.js';
import { MaxLimitsTestMixin } from '../../../../gpu_test.js';
import { ValidationTest } from '../../validation_test.js';

export type TextureBindingType =
Expand Down Expand Up @@ -92,7 +93,7 @@ class F extends ValidationTest {
}
}

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

const kTextureSize = 16;
const kTextureLevels = 3;
Expand Down Expand Up @@ -207,6 +208,13 @@ g.test('subresources,color_attachment_and_bind_group')
inSamePass,
} = t.params;

t.skipIf(
t.isCompatibility &&
bgUsage !== 'sampled-texture' &&
!(t.device.limits.maxStorageTexturesInFragmentStage! >= 1),
`maxStorageTexturesInFragmentStage(${t.device.limits.maxStorageTexturesInFragmentStage}) < 1`
);

const texture = t.createTextureTracked({
format: 'r32float',
usage:
Expand Down Expand Up @@ -469,6 +477,13 @@ g.test('subresources,multiple_bind_groups')
.fn(t => {
const { bg0Levels, bg0Layers, bg1Levels, bg1Layers, bgUsage0, bgUsage1, inSamePass } = t.params;

t.skipIf(
t.isCompatibility &&
(bgUsage0 !== 'sampled-texture' || bgUsage1 !== 'sampled-texture') &&
!(t.device.limits.maxStorageTexturesInFragmentStage! >= 2),
`maxStorageTexturesInFragmentStage(${t.device.limits.maxStorageTexturesInFragmentStage}) < 2`
);

const texture = t.createTextureTracked({
format: 'r32float',
usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.TEXTURE_BINDING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,29 @@ Texture Usages Validation Tests on All Kinds of WebGPU Subresource Usage Scopes.
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { unreachable } from '../../../../../common/util/util.js';
import { kTextureUsages } from '../../../../capability_info.js';
import { MaxLimitsTestMixin } from '../../../../gpu_test.js';
import { ValidationTest } from '../../validation_test.js';
import {
TextureBindingType,
kTextureBindingTypes,
IsReadOnlyTextureBindingType,
} from '../texture/in_render_common.spec.js';

function skipIfStorageTexturesUsedAndNotAvailableInFragmentStage(
t: ValidationTest,
usage: (typeof kTextureBindingTypes)[number] | 'copy-src' | 'copy-dst' | 'color-attachment',
numRequired: number
) {
t.skipIf(
t.isCompatibility &&
(usage === 'writeonly-storage-texture' ||
usage === 'readonly-storage-texture' ||
usage === 'readwrite-storage-texture') &&
!(t.device.limits.maxStorageTexturesInFragmentStage! > numRequired),
`maxStorageTexturesInFragmentStage${t.device.limits.maxStorageTexturesInFragmentStage} < ${numRequired}`
);
}

class F extends ValidationTest {
createBindGroupLayoutForTest(
textureUsage: TextureBindingType,
Expand Down Expand Up @@ -70,7 +86,7 @@ class F extends ValidationTest {
}
}

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

const kTextureSize = 16;
const kTextureLayers = 3;
Expand Down Expand Up @@ -507,6 +523,9 @@ g.test('subresources,texture_usages_in_copy_and_render_pass')
.fn(t => {
const { usage0, usage1 } = t.params;

skipIfStorageTexturesUsedAndNotAvailableInFragmentStage(t, usage0, 1);
skipIfStorageTexturesUsedAndNotAvailableInFragmentStage(t, usage1, 1);

const texture = t.createTextureTracked({
format: 'r32float',
usage:
Expand Down Expand Up @@ -599,6 +618,8 @@ g.test('subresources,texture_view_usages')
.fn(t => {
const { bindingType, viewUsage } = t.params;

skipIfStorageTexturesUsedAndNotAvailableInFragmentStage(t, bindingType, 1);

const texture = t.createTextureTracked({
format: 'r32float',
usage:
Expand Down
Loading