Skip to content

Commit 44d19ed

Browse files
committed
Validate setImmediates API in different encoder types
This PR adding validation tests to cover setImmediates API in different encoder types (compute pass, render pass, render bundle) by covering: *Interpretation: - Passing a TypedArray the data offset and size is not given in elements. * Alignment: - rangeOffset is not a multiple of 4 bytes. - content size, converted to bytes, is not a multiple of 4 bytes. * Arithmetic overflow - rangeOffset + contentSize is overflow * Bounds: - dataOffset + size (in bytes) exceeds the content data size. - rangeOffset + size (in bytes) exceeds the maxImmdiateSize.
1 parent e7cad01 commit 44d19ed

File tree

3 files changed

+204
-8
lines changed

3 files changed

+204
-8
lines changed

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"@types/w3c-image-capture": "^1.0.10",
5151
"@typescript-eslint/eslint-plugin": "^6.9.1",
5252
"@typescript-eslint/parser": "^6.9.1",
53-
"@webgpu/types": "^0.1.66",
53+
"@webgpu/types": "^0.1.67",
5454
"ansi-colors": "4.1.3",
5555
"babel-plugin-add-header-comment": "^1.0.3",
5656
"babel-plugin-const-enum": "^1.2.0",
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
export const description = `
2+
setImmediates validation tests.
3+
4+
Test different encoder types (compute pass, render pass, render bundle):
5+
*Interpretation:
6+
- Passing a TypedArray the data offset and size is not given in elements.
7+
* Alignment:
8+
- rangeOffset is not a multiple of 4 bytes.
9+
- content size, converted to bytes, is not a multiple of 4 bytes.
10+
* Arithmetic overflow
11+
- rangeOffset + contentSize is overflow
12+
* Bounds:
13+
- dataOffset + size (in bytes) exceeds the content data size.
14+
- rangeOffset + size (in bytes) exceeds the maxImmdiateSize.
15+
`;
16+
17+
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
18+
import {
19+
kTypedArrayBufferViewConstructors,
20+
TypedArrayBufferViewConstructor,
21+
} from '../../../../../common/util/util.js';
22+
import { Float16Array } from '../../../../../external/petamoriken/float16/float16.js';
23+
import { AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js';
24+
import { kProgrammableEncoderTypes } from '../../../../util/command_buffer_maker.js';
25+
import { kMaxSafeMultipleOf8 } from '../../../../util/math.js';
26+
27+
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
28+
29+
g.test('interpretation')
30+
.desc('Tests that contentSize interpreted to element size with TypedArray.')
31+
.paramsSubcasesOnly(u =>
32+
u //
33+
.combine('encoderType', kProgrammableEncoderTypes)
34+
.combine('_success', [true, false])
35+
)
36+
.fn(t => {
37+
const { encoderType, _success } = t.params;
38+
39+
function runTest(arrayBufferType: TypedArrayBufferViewConstructor) {
40+
const kMinAlignmentBytes = 4;
41+
const elementSize = arrayBufferType.BYTES_PER_ELEMENT;
42+
const maxImmediateSize = t.device.limits.maxImmediateSize; // using this limit for tests
43+
const validDataSize = _success
44+
? Math.floor((maxImmediateSize - kMinAlignmentBytes) / elementSize)
45+
: maxImmediateSize - kMinAlignmentBytes;
46+
const validOffset = _success
47+
? Math.ceil(kMinAlignmentBytes / elementSize)
48+
: kMinAlignmentBytes;
49+
50+
const { encoder, validateFinish } = t.createEncoder(encoderType);
51+
const data = new arrayBufferType(maxImmediateSize);
52+
53+
encoder.setImmediates(/* rangeOffset */ 0, data, /* dataOffset */ validOffset, validDataSize);
54+
55+
validateFinish(_success || elementSize === 1);
56+
}
57+
58+
for (const arrayType of kTypedArrayBufferViewConstructors) {
59+
if (arrayType === Float16Array) {
60+
// Skip Float16Array since it is supplied by an external module, so there isn't an overload for it.
61+
continue;
62+
}
63+
runTest(arrayType);
64+
}
65+
});
66+
67+
g.test('alignment')
68+
.desc('Tests that rangeOffset and contentSize aligned must align to 4 bytes.')
69+
.paramsSubcasesOnly(u =>
70+
u //
71+
.combine('encoderType', kProgrammableEncoderTypes)
72+
.combineWithParams([
73+
// control case
74+
{ offset: 4, contentByteSize: 4, _offsetValid: true, _contentValid: true },
75+
// offset is not align to 4 bytes
76+
{ offset: 1, contentByteSize: 4, _offsetValid: false, _contentValid: true },
77+
// contentSize is not align to 4 bytes
78+
{ offset: 4, contentByteSize: 5, _offsetValid: true, _contentValid: false },
79+
] as const)
80+
)
81+
.fn(t => {
82+
const { encoderType, offset, contentByteSize, _offsetValid, _contentValid } = t.params;
83+
const data = new Uint8Array(contentByteSize);
84+
85+
const { encoder, validateFinish } = t.createEncoder(encoderType);
86+
87+
const doSetImmediates = () => {
88+
encoder.setImmediates(offset, data, 0, contentByteSize);
89+
};
90+
91+
if (_contentValid) {
92+
doSetImmediates();
93+
} else {
94+
t.shouldThrow('RangeError', doSetImmediates);
95+
}
96+
97+
validateFinish(_offsetValid);
98+
});
99+
100+
g.test('overflow')
101+
.desc('Tests that rangeOffset + contentSize exceed MAX_INTEGER.')
102+
.paramsSubcasesOnly(u =>
103+
u //
104+
.combine('encoderType', kProgrammableEncoderTypes)
105+
.combineWithParams([
106+
// control case
107+
{ offset: 4, contentByteSize: 4, _rangeValid: true, _contentValid: true },
108+
// rangeOffset + contentSize is overflow
109+
{
110+
offset: 4,
111+
contentByteSize: kMaxSafeMultipleOf8,
112+
_rangeValid: true,
113+
_contentValid: false,
114+
},
115+
] as const)
116+
)
117+
.fn(t => {
118+
const { encoderType, offset, contentByteSize, _rangeValid, _contentValid } = t.params;
119+
const data = new Uint8Array(t.device.limits.maxImmediateSize);
120+
121+
const { encoder, validateFinish } = t.createEncoder(encoderType);
122+
123+
const doSetImmediates = () => {
124+
encoder.setImmediates(offset, data, 0, contentByteSize);
125+
};
126+
127+
if (_contentValid) {
128+
doSetImmediates();
129+
} else {
130+
t.shouldThrow('RangeError', doSetImmediates);
131+
}
132+
133+
validateFinish(_rangeValid);
134+
});
135+
136+
g.test('out_of_bounds')
137+
.desc(
138+
'Tests that rangeOffset + contentSize is greater than maxImmediateSize and contentSize is larger than data size.'
139+
)
140+
.paramsSubcasesOnly(u =>
141+
u //
142+
.combine('encoderType', kProgrammableEncoderTypes)
143+
.combineWithParams([
144+
// control case
145+
{
146+
rangeRemainSpace: 4,
147+
dataByteSize: 32,
148+
immediateContentByteSize: 4,
149+
_rangeValid: true,
150+
_contentValid: true,
151+
},
152+
// offset + contentByteSize large than maxImmediateSize
153+
{
154+
rangeRemainSpace: 0,
155+
dataByteSize: 32,
156+
immediateContentByteSize: 4,
157+
_rangeValid: false,
158+
_contentValid: true,
159+
},
160+
// contentSize is larger than data size
161+
{
162+
rangeRemainSpace: 8,
163+
dataByteSize: 4,
164+
immediateContentByteSize: 8,
165+
_rangeValid: true,
166+
_contentValid: false,
167+
},
168+
] as const)
169+
)
170+
.fn(t => {
171+
const {
172+
encoderType,
173+
rangeRemainSpace,
174+
dataByteSize,
175+
immediateContentByteSize,
176+
_rangeValid,
177+
_contentValid,
178+
} = t.params;
179+
const maxImmediates = t.device.limits.maxImmediateSize;
180+
const rangeOffset = maxImmediates - rangeRemainSpace;
181+
const data = new Uint8Array(dataByteSize);
182+
183+
const { encoder, validateFinish } = t.createEncoder(encoderType);
184+
185+
const doSetImmediates = () => {
186+
encoder.setImmediates(rangeOffset, data, 0, immediateContentByteSize);
187+
};
188+
189+
if (_contentValid) {
190+
doSetImmediates();
191+
} else {
192+
t.shouldThrow('RangeError', doSetImmediates);
193+
}
194+
195+
validateFinish(_rangeValid);
196+
});

0 commit comments

Comments
 (0)