Skip to content

Commit 51b8675

Browse files
committed
Add new builtins to maxInterStageShaderVariables test
primitive_index, subgroup_id, subgroup_size were missing from the test.
1 parent f1aca41 commit 51b8675

File tree

1 file changed

+138
-60
lines changed

1 file changed

+138
-60
lines changed
Lines changed: 138 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,145 @@
1-
import { range } from '../../../../../common/util/util.js';
1+
import { Fixture } from '../../../../../common/framework/fixture.js';
2+
import { keysOf } from '../../../../../common/util/data_tables.js';
3+
import { hasFeature, range } from '../../../../../common/util/util.js';
24

35
import { kMaximumLimitBaseParams, makeLimitTestGroup } from './limit_utils.js';
46

7+
const kFragmentInputTypes = {
8+
front_facing: 'bool',
9+
sample_index: 'u32',
10+
sample_mask: 'u32',
11+
primitive_index: 'u32',
12+
subgroup_invocation_id: 'u32',
13+
subgroup_size: 'u32',
14+
} as const;
15+
16+
const kFragmentInputs = keysOf(kFragmentInputTypes);
17+
18+
const kItemsThatCountAgainstLimit = ['point-list', ...kFragmentInputs] as const;
19+
20+
const kExtraItems = [
21+
'sample_mask_out', // special - see below
22+
] as const;
23+
24+
function getCombinations<T>(arr: readonly T[], sizes: number[]): T[][] {
25+
const combos: T[][] = [];
26+
27+
function backtrack(start: number, path: T[], size: number) {
28+
if (path.length === size) {
29+
combos.push(path.slice());
30+
return;
31+
}
32+
33+
for (let i = start; i < arr.length; i++) {
34+
path.push(arr[i]);
35+
backtrack(i + 1, path, size);
36+
path.pop();
37+
}
38+
}
39+
40+
sizes.forEach(size => {
41+
backtrack(0, [], size);
42+
});
43+
44+
return combos;
45+
}
46+
47+
const kTestItems = [...kItemsThatCountAgainstLimit, ...kExtraItems] as const;
48+
const kTestItemCombinations = [
49+
[], // no builtins case
50+
...getCombinations(kTestItems, [1, 2, 3]),
51+
kTestItems, // all case
52+
] as const;
53+
54+
const requiresSubgroupsFeature = (items: Set<(typeof kTestItems)[number]>) =>
55+
items.has('subgroup_invocation_id') || items.has('subgroup_size');
56+
557
function getPipelineDescriptor(
58+
t: Fixture,
659
device: GPUDevice,
760
testValue: number,
8-
pointList: boolean,
9-
frontFacing: boolean,
10-
sampleIndex: boolean,
11-
sampleMaskIn: boolean,
12-
sampleMaskOut: boolean
61+
items: Set<(typeof kTestItems)[number]>
1362
): GPURenderPipelineDescriptor {
14-
const vertexOutputDeductions = pointList ? 1 : 0;
15-
const fragmentInputDeductions = [frontFacing, sampleIndex, sampleMaskIn]
63+
const vertexOutputDeductions = items.has('point-list') ? 1 : 0;
64+
const usedFragInputs = [...items.values()].filter(p => p in kFragmentInputTypes);
65+
const fragmentInputDeductions = usedFragInputs
1666
.map(p => (p ? 1 : 0) as number)
17-
.reduce((acc, p) => acc + p);
67+
.reduce((acc, p) => acc + p, 0);
1868

19-
const vertexOutputVariables = testValue - vertexOutputDeductions;
20-
const fragmentInputVariables = testValue - fragmentInputDeductions;
21-
const numInterStageVariables = Math.min(vertexOutputVariables, fragmentInputVariables);
69+
t.debug(() => `device features: ${[...device.features].join(', ')}`);
70+
71+
const numVertexOutputVariables = testValue - vertexOutputDeductions;
72+
const numFragmentInputVariables = testValue - fragmentInputDeductions;
73+
const numInterStageVariables = Math.min(numVertexOutputVariables, numFragmentInputVariables);
2274

2375
const maxVertexOutputVariables =
2476
device.limits.maxInterStageShaderVariables - vertexOutputDeductions;
2577
const maxFragmentInputVariables =
2678
device.limits.maxInterStageShaderVariables - fragmentInputDeductions;
2779
const maxInterStageVariables = Math.min(maxVertexOutputVariables, maxFragmentInputVariables);
2880

29-
const varyings = `
30-
${range(numInterStageVariables, i => `@location(${i}) v4_${i}: vec4f,`).join('\n')}
31-
`;
81+
const fragInputs = usedFragInputs
82+
.map(
83+
(input, i) =>
84+
` @builtin(${input}) i_${i}: ${
85+
kFragmentInputTypes[input as keyof typeof kFragmentInputTypes]
86+
},`
87+
)
88+
.join('\n');
89+
90+
const varyings = `${range(
91+
numInterStageVariables,
92+
i => ` @location(${i}) v4_${i}: vec4f,`
93+
).join('\n')}`;
3294

3395
const code = `
3496
// test value : ${testValue}
35-
// maxInterStageShaderVariables : ${device.limits.maxInterStageShaderVariables}
36-
// num variables in vertex shader : ${vertexOutputVariables}${pointList ? ' + point-list' : ''}
37-
// num variables in fragment shader : ${fragmentInputVariables}${
38-
frontFacing ? ' + front-facing' : ''
39-
}${sampleIndex ? ' + sample_index' : ''}${sampleMaskIn ? ' + sample_mask' : ''}
97+
// maxInterStageShaderVariables : ${device.limits.maxInterStageShaderVariables}
98+
// num variables in vertex shader : ${numVertexOutputVariables}${
99+
items.has('point-list') ? ' + point-list' : ''
100+
}
101+
// num variables in fragment shader : ${numFragmentInputVariables} + ${usedFragInputs.join(
102+
' + '
103+
)}
40104
// maxInterStageVariables: : ${maxInterStageVariables}
41105
// num used inter stage variables : ${numInterStageVariables}
42106
107+
${items.has('primitive_index') ? 'enable primitive_index;' : ''}
108+
${requiresSubgroupsFeature(items) ? 'enable subgroups;' : ''}
109+
43110
struct VSOut {
44111
@builtin(position) p: vec4f,
45-
${varyings}
112+
${varyings}
46113
}
47114
struct FSIn {
48-
${frontFacing ? '@builtin(front_facing) frontFacing: bool,' : ''}
49-
${sampleIndex ? '@builtin(sample_index) sampleIndex: u32,' : ''}
50-
${sampleMaskIn ? '@builtin(sample_mask) sampleMask: u32,' : ''}
51-
${varyings}
115+
${fragInputs}
116+
${varyings}
52117
}
118+
53119
struct FSOut {
54120
@location(0) color: vec4f,
55-
${sampleMaskOut ? '@builtin(sample_mask) sampleMask: u32,' : ''}
121+
${items.has('sample_mask_out') ? '@builtin(sample_mask) sampleMask: u32,' : ''}
56122
}
123+
57124
@vertex fn vs() -> VSOut {
58125
var o: VSOut;
59126
o.p = vec4f(0);
60127
return o;
61128
}
129+
62130
@fragment fn fs(i: FSIn) -> FSOut {
63131
var o: FSOut;
132+
64133
o.color = vec4f(0);
65134
return o;
66135
}
67136
`;
137+
t.debug(code);
68138
const module = device.createShaderModule({ code });
69139
const pipelineDescriptor: GPURenderPipelineDescriptor = {
70140
layout: 'auto',
71141
primitive: {
72-
topology: pointList ? 'point-list' : 'triangle-list',
142+
topology: items.has('point-list') ? 'point-list' : 'triangle-list',
73143
},
74144
vertex: {
75145
module,
@@ -92,51 +162,59 @@ const limit = 'maxInterStageShaderVariables';
92162
export const { g, description } = makeLimitTestGroup(limit);
93163

94164
g.test('createRenderPipeline,at_over')
95-
.desc(`Test using at and over ${limit} limit in createRenderPipeline(Async)`)
165+
.desc(
166+
`
167+
Test using at and over ${limit} limit in createRenderPipeline(Async)
168+
169+
Note: We test combinations to make sure each entry is counted separately.
170+
and that implementations don't accidentally add only 1 to the count when
171+
2 or more builtins are used. We also include sample_mask as an output
172+
to make sure it does not count against the limit since it has the same
173+
name as sample_mask as an input.
174+
`
175+
)
96176
.params(
97-
kMaximumLimitBaseParams
98-
.combine('async', [false, true])
99-
.combine('pointList', [false, true])
100-
.combine('frontFacing', [false, true])
101-
.combine('sampleIndex', [false, true])
102-
.combine('sampleMaskIn', [false, true])
103-
.combine('sampleMaskOut', [false, true])
177+
kMaximumLimitBaseParams.combine('async', [false, true]).combine('items', kTestItemCombinations)
104178
)
105-
.beforeAllSubcases(t => {
179+
.fn(async t => {
180+
const { limitTest, testValueName, async, items: itemsAsArray } = t.params;
181+
const items = new Set(itemsAsArray);
182+
106183
if (t.isCompatibility) {
107184
t.skipIf(
108-
t.params.sampleMaskIn || t.params.sampleMaskOut,
185+
items.has('sample_mask') || items.has('sample_mask_out'),
109186
'sample_mask not supported in compatibility mode'
110187
);
111-
t.skipIf(t.params.sampleIndex, 'sample_index not supported in compatibility mode');
188+
t.skipIf(items.has('sample_index'), 'sample_index not supported in compatibility mode');
112189
}
113-
})
114-
.fn(async t => {
115-
const {
116-
limitTest,
117-
testValueName,
118-
async,
119-
pointList,
120-
frontFacing,
121-
sampleIndex,
122-
sampleMaskIn,
123-
sampleMaskOut,
124-
} = t.params;
190+
191+
const features: GPUFeatureName[] = [];
192+
193+
if (items.has('primitive_index')) {
194+
if (hasFeature(t.adapter.features, 'primitive-index')) {
195+
features.push('primitive-index');
196+
} else {
197+
t.skip('primitive_index requires primitive-index feature');
198+
}
199+
}
200+
201+
if (requiresSubgroupsFeature(items)) {
202+
if (hasFeature(t.adapter.features, 'subgroups')) {
203+
features.push('subgroups');
204+
} else {
205+
t.skip('subgroup_invocation_id or subgroup_size requires subgroups feature');
206+
}
207+
}
208+
125209
await t.testDeviceWithRequestedMaximumLimits(
126210
limitTest,
127211
testValueName,
128212
async ({ device, testValue, shouldError }) => {
129-
const pipelineDescriptor = getPipelineDescriptor(
130-
device,
131-
testValue,
132-
pointList,
133-
frontFacing,
134-
sampleIndex,
135-
sampleMaskIn,
136-
sampleMaskOut
137-
);
213+
const pipelineDescriptor = getPipelineDescriptor(t, device, testValue, items);
138214

139215
await t.testCreateRenderPipeline(pipelineDescriptor, async, shouldError);
140-
}
216+
},
217+
undefined,
218+
features
141219
);
142220
});

0 commit comments

Comments
 (0)