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: adjust for 0 vert/frag storage buffers/textures #4088

Merged
merged 3 commits into from
Dec 12, 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 @@ -21,6 +21,8 @@ import {
const kExtraLimits: LimitsRequest = {
maxBindingsPerBindGroup: 'adapterLimit',
maxBindGroups: 'adapterLimit',
maxStorageBuffersInFragmentStage: 'adapterLimit',
maxStorageBuffersInVertexStage: 'adapterLimit',
};

const limit = 'maxStorageBuffersPerShaderStage';
Expand Down Expand Up @@ -165,6 +167,24 @@ g.test('createPipeline,at_over')
`can not test ${testValue} bindings in same group because maxBindingsPerBindGroup = ${device.limits.maxBindingsPerBindGroup}`
);

if (t.isCompatibility) {
t.skipIf(
(bindingCombination === 'fragment' ||
bindingCombination === 'vertexAndFragmentWithPossibleVertexStageOverflow' ||
bindingCombination === 'vertexAndFragmentWithPossibleFragmentStageOverflow') &&
testValue > device.limits.maxStorageBuffersInFragmentStage!,
`can not test ${testValue} bindings as it is more than maxStorageBuffersInFragmentStage(${device.limits.maxStorageBuffersInFragmentStage})`
);

t.skipIf(
(bindingCombination === 'vertex' ||
bindingCombination === 'vertexAndFragmentWithPossibleVertexStageOverflow' ||
bindingCombination === 'vertexAndFragmentWithPossibleFragmentStageOverflow') &&
testValue > device.limits.maxStorageBuffersInVertexStage!,
`can not test ${testValue} bindings as it is more than maxStorageBuffersInVertexStage(${device.limits.maxStorageBuffersInVertexStage})`
);
}

const code = getPerStageWGSLForBindingCombination(
bindingCombination,
order,
Expand Down
43 changes: 42 additions & 1 deletion src/webgpu/api/validation/layout_shader_compat.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ValidBindableResource,
} from '../../capability_info.js';
import { GPUConst } from '../../constants.js';
import { MaxLimitsTestMixin } from '../../gpu_test.js';

import { ValidationTest } from './validation_test.js';

Expand Down Expand Up @@ -156,7 +157,7 @@ const BindingResourceCompatibleWithShaderStages = function (
return true;
};

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

g.test('pipeline_layout_shader_exact_match')
.desc(
Expand Down Expand Up @@ -195,6 +196,46 @@ g.test('pipeline_layout_shader_exact_match')
isBindingStaticallyUsed,
} = t.params;

if (t.isCompatibility) {
const bindingUsedWithVertexStage =
(shaderStageWithBinding & GPUShaderStage.VERTEX) !== 0 ||
(pipelineLayoutVisibility & GPUShaderStage.VERTEX) !== 0;
const bindingUsedWithFragmentStage =
(shaderStageWithBinding & GPUShaderStage.FRAGMENT) !== 0 ||
(pipelineLayoutVisibility & GPUShaderStage.FRAGMENT) !== 0;
const bindingIsStorageBuffer =
bindingInPipelineLayout === 'readonlyStorageBuf' ||
bindingInPipelineLayout === 'storageBuf';
const bindingIsStorageTexture =
bindingInPipelineLayout === 'readonlyStorageTex' ||
bindingInPipelineLayout === 'readwriteStorageTex' ||
bindingInPipelineLayout === 'writeonlyStorageTex';
t.skipIf(
bindingUsedWithVertexStage &&
bindingIsStorageBuffer &&
t.device.limits.maxStorageBuffersInVertexStage === 0,
'Storage buffers can not be used in vertex shaders because maxStorageBuffersInVertexStage === 0'
);
t.skipIf(
bindingUsedWithVertexStage &&
bindingIsStorageTexture &&
t.device.limits.maxStorageTexturesInVertexStage === 0,
'Storage textures can not be used in vertex shaders because maxStorageTexturesInVertexStage === 0'
);
t.skipIf(
bindingUsedWithFragmentStage &&
bindingIsStorageBuffer &&
t.device.limits.maxStorageBuffersInFragmentStage === 0,
'Storage buffers can not be used in fragment shaders because maxStorageBuffersInFragmentStage === 0'
);
t.skipIf(
bindingUsedWithFragmentStage &&
bindingIsStorageTexture &&
t.device.limits.maxStorageTexturesInFragmentStage === 0,
'Storage textures can not be used in fragment shaders because maxStorageTexturesInFragmentStage === 0'
);
}

const layout = t.createPipelineLayout(bindingInPipelineLayout, pipelineLayoutVisibility);
const bindResourceDeclaration = `@group(0) @binding(0) ${t.GetBindableResourceShaderDeclaration(
bindingInShader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Tests for resource compatibility between pipeline layout and shader modules

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { keysOf } from '../../../../common/util/data_tables.js';
import { MaxLimitsTestMixin } from '../../../gpu_test.js';
import {
kAPIResources,
getWGSLShaderForResource,
Expand All @@ -13,7 +14,7 @@ import {

import { CreateRenderPipelineValidationTest } from './common.js';

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

g.test('resource_compatibility')
.desc(
Expand Down Expand Up @@ -53,8 +54,36 @@ g.test('resource_compatibility')
((wgslResource.buffer !== undefined && wgslResource.buffer.type === 'storage') ||
(wgslResource.storageTexture !== undefined &&
wgslResource.storageTexture.access !== 'read-only')),
'Storage buffers and textures cannot be used in vertex shaders'
'Read-Write Storage buffers and textures cannot be used in vertex shaders'
);
if (t.isCompatibility) {
t.skipIf(
t.params.stage === 'vertex' &&
(apiResource.buffer?.type === 'storage' ||
apiResource.buffer?.type === 'read-only-storage') &&
t.device.limits.maxStorageBuffersInVertexStage === 0,
'Storage buffers can not be used in vertex shaders because maxStorageBuffersInVertexStage === 0'
);
t.skipIf(
t.params.stage === 'vertex' &&
apiResource.storageTexture !== undefined &&
t.device.limits.maxStorageTexturesInVertexStage === 0,
'Storage textures can not be used in vertex shaders because maxStorageTexturesInVertexStage === 0'
);
t.skipIf(
t.params.stage === 'fragment' &&
(apiResource.buffer?.type === 'storage' ||
apiResource.buffer?.type === 'read-only-storage') &&
t.device.limits.maxStorageBuffersInFragmentStage === 0,
'Storage buffers can not be used in fragment shaders because maxStorageBuffersInFragmentStage === 0'
);
t.skipIf(
t.params.stage === 'fragment' &&
apiResource.storageTexture !== undefined &&
t.device.limits.maxStorageTexturesInFragmentStage === 0,
'Storage textures can not be used in fragment shaders because maxStorageTexturesInFragmentStage === 0'
);
}
t.skipIfTextureViewDimensionNotSupported(wgslResource.texture?.viewDimension);
const emptyVS = `
@vertex
Expand Down
11 changes: 11 additions & 0 deletions src/webgpu/gpu_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ import {
import { createTextureFromTexelViews } from './util/texture.js';
import { reifyExtent3D, reifyOrigin3D } from './util/unions.js';

// Declarations for WebGPU items we want tests for that are not yet officially part of the spec.
declare global {
// MAINTENANCE_TODO: remove once added to @webgpu/types
interface GPUSupportedLimits {
readonly maxStorageBuffersInFragmentStage?: number;
readonly maxStorageTexturesInFragmentStage?: number;
readonly maxStorageBuffersInVertexStage?: number;
readonly maxStorageTexturesInVertexStage?: number;
}
}

const devicePool = new DevicePool();

// MAINTENANCE_TODO: When DevicePool becomes able to provide multiple devices at once, use the
Expand Down
Loading