Skip to content

Commit d5b0dae

Browse files
committed
Fix maxBindGroups test so it can attempt more resources
Before it was just binding uniform buffers but if maxBindGroups is more than maxUniformBuffersPerShaderStage the test would fail. Now it uses all possible resources types. One per bind group.
1 parent 379339d commit d5b0dae

File tree

1 file changed

+137
-15
lines changed

1 file changed

+137
-15
lines changed

src/webgpu/api/validation/capability_checks/limits/maxBindGroups.spec.ts

Lines changed: 137 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { range } from '../../../../../common/util/util.js';
1+
import { assert } from '../../../../../common/util/util.js';
22

33
import {
44
kCreatePipelineTypes,
@@ -10,30 +10,152 @@ import {
1010
const limit = 'maxBindGroups';
1111
export const { g, description } = makeLimitTestGroup(limit);
1212

13+
type BindingLayout = {
14+
buffer?: GPUBufferBindingLayout;
15+
sampler?: GPUSamplerBindingLayout;
16+
texture?: GPUTextureBindingLayout;
17+
storageTexture?: GPUStorageTextureBindingLayout;
18+
externalTexture?: GPUExternalTextureBindingLayout;
19+
};
20+
21+
type LimitToBindingLayout = {
22+
name: keyof GPUSupportedLimits;
23+
entry: BindingLayout;
24+
};
25+
26+
const kLimitToBindingLayout: LimitToBindingLayout[] = [
27+
{
28+
name: 'maxSampledTexturesPerShaderStage',
29+
entry: {
30+
texture: {},
31+
},
32+
},
33+
{
34+
name: 'maxSamplersPerShaderStage',
35+
entry: {
36+
sampler: {},
37+
},
38+
},
39+
{
40+
name: 'maxUniformBuffersPerShaderStage',
41+
entry: {
42+
buffer: {},
43+
},
44+
},
45+
{
46+
name: 'maxStorageBuffersPerShaderStage',
47+
entry: {
48+
buffer: {
49+
type: 'read-only-storage',
50+
},
51+
},
52+
},
53+
{
54+
name: 'maxStorageTexturesPerShaderStage',
55+
entry: {
56+
storageTexture: {
57+
access: 'write-only',
58+
format: 'rgba8unorm',
59+
viewDimension: '2d',
60+
},
61+
},
62+
},
63+
];
64+
65+
/**
66+
* Yields all possible binding layout entries for a stage.
67+
*/
68+
function* getBindingLayoutEntriesForStage(device: GPUDevice) {
69+
for (const { name, entry } of kLimitToBindingLayout) {
70+
const limit = device.limits[name] as number;
71+
for (let i = 0; i < limit; ++i) {
72+
yield entry;
73+
}
74+
}
75+
}
76+
77+
/**
78+
* Yields all of the possible BindingLayoutEntryAndVisibility entries for a render pipeline
79+
*/
80+
function* getBindingLayoutEntriesForRenderPipeline(
81+
device: GPUDevice
82+
): Generator<GPUBindGroupLayoutEntry> {
83+
const visibilities = [GPUShaderStage.VERTEX, GPUShaderStage.FRAGMENT];
84+
for (const visibility of visibilities) {
85+
for (const bindEntryResourceType of getBindingLayoutEntriesForStage(device)) {
86+
const entry: GPUBindGroupLayoutEntry = {
87+
binding: 0,
88+
visibility,
89+
...bindEntryResourceType,
90+
};
91+
yield entry;
92+
}
93+
}
94+
}
95+
96+
/**
97+
* Returns the total possible bindings per render pipeline
98+
*/
99+
function getTotalPossibleBindingsPerRenderPipeline(device: GPUDevice) {
100+
const totalPossibleBindingsPerStage =
101+
device.limits.maxSampledTexturesPerShaderStage +
102+
device.limits.maxSamplersPerShaderStage +
103+
device.limits.maxUniformBuffersPerShaderStage +
104+
device.limits.maxStorageBuffersPerShaderStage +
105+
device.limits.maxStorageTexturesPerShaderStage;
106+
return totalPossibleBindingsPerStage * 2;
107+
}
108+
109+
/**
110+
* Yields count GPUBindGroupLayoutEntries
111+
*/
112+
function* getBindingLayoutEntries(
113+
device: GPUDevice,
114+
count: number
115+
): Generator<GPUBindGroupLayoutEntry> {
116+
assert(count < getTotalPossibleBindingsPerRenderPipeline(device));
117+
const iter = getBindingLayoutEntriesForRenderPipeline(device);
118+
for (; count > 0; --count) {
119+
yield iter.next().value;
120+
}
121+
}
122+
13123
g.test('createPipelineLayout,at_over')
14124
.desc(`Test using createPipelineLayout at and over ${limit} limit`)
15125
.params(kMaximumLimitBaseParams)
16126
.fn(async t => {
17127
const { limitTest, testValueName } = t.params;
128+
18129
await t.testDeviceWithRequestedMaximumLimits(
19130
limitTest,
20131
testValueName,
21-
async ({ device, testValue, shouldError }) => {
22-
const bindGroupLayouts = range(testValue, _i =>
23-
device.createBindGroupLayout({
24-
entries: [
25-
{
26-
binding: 0,
27-
visibility: GPUShaderStage.VERTEX,
28-
buffer: {},
29-
},
30-
],
31-
})
132+
async ({ device, testValue, shouldError, actualLimit }) => {
133+
const totalPossibleBindingsPerPipeline = getTotalPossibleBindingsPerRenderPipeline(device);
134+
// Not sure what to do if we ever hit this but I think it's better to assert than silently skip.
135+
assert(
136+
testValue < totalPossibleBindingsPerPipeline,
137+
`not enough possible bindings(${totalPossibleBindingsPerPipeline}) to test ${testValue} bindGroups`
32138
);
33139

34-
await t.expectValidationError(() => {
35-
device.createPipelineLayout({ bindGroupLayouts });
36-
}, shouldError);
140+
const bindingDescriptions: string[] = [];
141+
const bindGroupLayouts = [...getBindingLayoutEntries(device, testValue)].map(entry => {
142+
bindingDescriptions.push(
143+
`${JSON.stringify(entry)} // group(${bindingDescriptions.length})`
144+
);
145+
return device.createBindGroupLayout({
146+
entries: [entry],
147+
});
148+
});
149+
150+
await t.expectValidationError(
151+
() => {
152+
device.createPipelineLayout({ bindGroupLayouts });
153+
},
154+
shouldError,
155+
`testing ${testValue} bindGroups on maxBindGroups = ${actualLimit} with \n${bindingDescriptions.join(
156+
'\n'
157+
)}`
158+
);
37159
}
38160
);
39161
});

0 commit comments

Comments
 (0)