Skip to content

Commit

Permalink
Compat: refactor resource_usages tests for 0 storage buffer/textures (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
greggman authored Dec 31, 2024
1 parent d65f8cb commit b72bd61
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 9 deletions.
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

0 comments on commit b72bd61

Please sign in to comment.