Skip to content

Commit d86d88d

Browse files
committed
Add test of gl_VertexID and gl_InstanceID.
Findings: * Some MacOS (Intel?) has `first` affect `gl_InstanceID` in `DrawArrays`. Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1779800
1 parent 124b63d commit d86d88d

2 files changed

Lines changed: 353 additions & 0 deletions

File tree

sdk/tests/conformance2/rendering/00_test_list.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ blitframebuffer-size-overflow.html
1111
--min-version 2.0.1 blitframebuffer-stencil-only.html
1212
blitframebuffer-test.html
1313
--min-version 2.0.1 blitframebuffer-unaffected-by-colormask.html
14+
--min-version 2.0.1 builtin-vert-attribs.html
1415
canvas-resizing-with-pbo-bound.html
1516
--min-version 2.0.1 clearbuffer-sub-source.html
1617
--min-version 2.0.1 clearbufferfv-with-alpha-false.html
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
<!--
2+
Copyright (c) 2022 The Khronos Group Inc.
3+
Use of this source code is governed by an MIT-style license that can be
4+
found in the LICENSE.txt file.
5+
-->
6+
7+
<!DOCTYPE html>
8+
<html>
9+
<head>
10+
<meta charset=utf-8>
11+
<link rel=stylesheet href="../../resources/js-test-style.css"/>
12+
<script src="../../js/js-test-pre.js"></script>
13+
<script src="../../js/webgl-test-utils.js"></script>
14+
</head>
15+
<body>
16+
<canvas id=e_canvas width=1 height=1 style="width: 100px; height: 100px;"></canvas>
17+
<div id=description></div>
18+
<div id=console></div>
19+
<script>
20+
"use strict";
21+
description('gl_VertexID and gl_InstanceID should behave per spec.');
22+
// gl_VertexID behavior for DrawElements* is clarified in the ES3.1 spec,
23+
// though it was implied previously.
24+
// (gl_VertexID receives the ID fetched from the index buffer)
25+
26+
const wtu = WebGLTestUtils;
27+
const gl = wtu.create3DContext('e_canvas');
28+
29+
const ERRATA = {};
30+
//ERRATA.IGNORE_GL_INSTANCE_ID = true;
31+
// No-workaround MacOS needs this.
32+
33+
//ERRATA.EXPECT_INDEXED_GL_VERTEX_ID_0 = true;
34+
// Every GL driver I've found needs this.
35+
// ANGLE-WebGL-on-D3D and -on-Metal do this properly.
36+
// While admittedly the ES3.0 spec is a little vague, this was explicitly clarified in the ES3.1 spec.
37+
// However, even ES3.1+ drivers seem to just leave gl_VertexID=0 for DrawElements*.
38+
39+
debug(`ERRATA: ${JSON.stringify(ERRATA)}`);
40+
41+
function make_vs_point(vid, iid) {
42+
return `\
43+
#version 300 es
44+
45+
${vid.name == 'gl_VertexID' ? '// ' : ''}layout(location=${vid.loc}) in highp int ${vid.name};
46+
${iid.name == 'gl_InstanceID' ? '// ' :''}layout(location=${iid.loc}) in highp int ${iid.name};
47+
out vec4 v_color;
48+
49+
void main() {
50+
gl_PointSize = 1.0;
51+
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
52+
v_color = vec4(1.0, float(${vid.name}) / 255.0, float(${iid.name}) / 255.0, 1.0);
53+
#if ${(iid.name == 'gl_InstanceID' && ERRATA.IGNORE_GL_INSTANCE_ID)|0}
54+
v_color.b = 0.0;
55+
#endif
56+
}`;
57+
}
58+
59+
function make_vs_tri(vid, iid) {
60+
return `\
61+
#version 300 es
62+
63+
${vid.name == 'gl_VertexID' ? '// ' : ''}layout(location=${vid.loc}) in highp int ${vid.name};
64+
${iid.name == 'gl_InstanceID' ? '// ' :''}layout(location=${iid.loc}) in highp int ${iid.name};
65+
out vec4 v_color;
66+
67+
void main() {
68+
int prim_vert_id = ${vid.name} % 3;
69+
int flat_vert_id = ${vid.name} - prim_vert_id + 2;
70+
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
71+
gl_Position.x = (prim_vert_id == 1) ? 2.0 : -1.0;
72+
gl_Position.y = (prim_vert_id == 2) ? 2.0 : -1.0;
73+
v_color = vec4(1.0, float(flat_vert_id) / 255.0, float(${iid.name}) / 255.0, 1.0);
74+
#if ${(iid.name == 'gl_InstanceID' && ERRATA.IGNORE_GL_INSTANCE_ID)|0}
75+
v_color.b = 0.0;
76+
#endif
77+
}`;
78+
}
79+
80+
const FS = `\
81+
#version 300 es
82+
precision mediump float;
83+
84+
in vec4 v_color;
85+
out vec4 o_color;
86+
87+
void main() {
88+
o_color = v_color;
89+
}
90+
`;
91+
92+
93+
function crossCombine(...args) {
94+
function crossCombine2(listA, listB) {
95+
const listC = [];
96+
for (const a of listA) {
97+
for (const b of listB) {
98+
const c = Object.assign({}, a, b);
99+
listC.push(c);
100+
}
101+
}
102+
return listC;
103+
}
104+
105+
let res = [{}];
106+
while (args.length) {
107+
const next = args.shift();
108+
next[0].defined;
109+
res = crossCombine2(res, next);
110+
}
111+
return res;
112+
}
113+
114+
/// makeCombiner('foo', [5, 3]) -> [{foo: 5}, {foo: 3}]
115+
function makeCombiner(key, vals) {
116+
const ret = [];
117+
for (const val of vals) {
118+
const cur = {};
119+
cur[key] = val;
120+
ret.push(cur);
121+
}
122+
return ret;
123+
}
124+
125+
debug('Draw a point with a shader that takes no attributes and verify it fills the whole canvas.');
126+
127+
128+
let TESTS = [
129+
makeCombiner('vid', [
130+
{name: 'a_VertexID', loc:0},
131+
{name: 'a_VertexID', loc:2}, // Test 2, so that we're not only testing 0.
132+
{name: 'gl_VertexID', loc:-1},
133+
{name: 'gl_VertexID', loc:0}, // Enable a vertex array, despite not using it.
134+
{name: 'gl_VertexID', loc:2}, // Enable a vertex array, despite not using it.
135+
]),
136+
makeCombiner('iid', [
137+
{name: 'a_InstanceID', loc:1},
138+
{name: 'gl_InstanceID', loc:-1},
139+
{name: 'gl_InstanceID', loc:1}, // Enable a vertex array, despite not using it.
140+
]),
141+
makeCombiner('separate_vbufs', [true, false]),
142+
];
143+
//console.log('a', {TESTS});
144+
TESTS = crossCombine(...TESTS);
145+
//console.log('b', {TESTS});
146+
147+
148+
let vdata = new Int32Array(1000);
149+
vdata = vdata.map((v,i) => i);
150+
const vbuf = gl.createBuffer();
151+
gl.bindBuffer(gl.ARRAY_BUFFER, vbuf);
152+
gl.bufferData(gl.ARRAY_BUFFER, vdata, gl.STATIC_DRAW);
153+
154+
155+
const vbuf2 = gl.createBuffer();
156+
gl.bindBuffer(gl.ARRAY_BUFFER, vbuf2);
157+
gl.bufferData(gl.ARRAY_BUFFER, vdata, gl.STATIC_DRAW);
158+
159+
160+
let index_data = new Uint32Array(1000);
161+
index_data = index_data.map((x,i) => 10+i);
162+
const index_buffer = gl.createBuffer();
163+
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
164+
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_data, gl.STATIC_DRAW);
165+
166+
167+
gl.disable(gl.DEPTH_TEST);
168+
169+
(async () => {
170+
for (const desc of TESTS) {
171+
await wtu.dispatchPromise(); // Yield, for responsiveness.
172+
debug('');
173+
debug('---------------------');
174+
debug(`desc: ${JSON.stringify(desc)}`);
175+
176+
let fn = (vs) => {
177+
//console.log({vs});
178+
const prog = wtu.setupProgram(gl, [vs, FS]);
179+
180+
{
181+
const WEBGL_debug_shaders = gl.getExtension('WEBGL_debug_shaders');
182+
let i = -1;
183+
for (const s of gl.getAttachedShaders(prog)) {
184+
i += 1;
185+
debug('');
186+
debug(`shader[${i}] getShaderSource() -> `);
187+
debug(gl.getShaderSource(s));
188+
if (WEBGL_debug_shaders) {
189+
debug(`shader[${i}] getTranslatedShaderSource() -> `);
190+
debug(WEBGL_debug_shaders.getTranslatedShaderSource(s));
191+
}
192+
}
193+
}
194+
return prog;
195+
};
196+
const point_prog = fn(make_vs_point(desc.vid, desc.iid));
197+
const tri_prog = fn(make_vs_tri(desc.vid, desc.iid));
198+
199+
// -
200+
201+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
202+
for (let i = 0; i <= 2; i++) {
203+
gl.disableVertexAttribArray(i);
204+
gl.vertexAttribPointer(i, 4, gl.FLOAT, false, 0, 0);
205+
gl.vertexAttribDivisor(i, 0);
206+
}
207+
208+
gl.bindBuffer(gl.ARRAY_BUFFER, vbuf);
209+
let loc = desc.vid.loc;
210+
if (loc != -1) {
211+
gl.enableVertexAttribArray(loc);
212+
gl.vertexAttribIPointer(loc, 1, gl.INT, 0, 0);
213+
};
214+
215+
if (desc.separate_vbufs) {
216+
gl.bindBuffer(gl.ARRAY_BUFFER, vbuf2);
217+
}
218+
loc = desc.iid.loc;
219+
if (loc != -1) {
220+
gl.enableVertexAttribArray(loc);
221+
gl.vertexAttribIPointer(loc, 1, gl.INT, 0, 0);
222+
gl.vertexAttribDivisor(loc, 1);
223+
};
224+
225+
{
226+
const err = gl.getError();
227+
if (err) throw err; // Broken init.
228+
}
229+
230+
// -
231+
232+
fn = (eval_str, expected_arr) => {
233+
if (ERRATA.IGNORE_GL_INSTANCE_ID) {
234+
if (desc.iid.name == 'gl_InstanceID') {
235+
expected_arr = expected_arr.map(x => x);
236+
expected_arr[2] = 0;
237+
}
238+
}
239+
if (ERRATA.EXPECT_INDEXED_GL_VERTEX_ID_0) {
240+
if (desc.vid.name == 'gl_VertexID' && eval_str.includes('Elements')) {
241+
if (eval_str.includes('POINTS')) {
242+
expected_arr = expected_arr.map(x => x);
243+
expected_arr[1] = 0;
244+
} else {
245+
// TRIANGLES will fail to auto-generate a triangle mesh if gl_VertexID is always 0.
246+
expected_arr = [0,0,0,0];
247+
}
248+
}
249+
}
250+
251+
debug('');
252+
//debug(`${eval_str} -> [${expected_arr.join(', ')}]`);
253+
eval(eval_str);
254+
255+
const err = gl.getError();
256+
if (err) throw err; // Broken subtest.
257+
258+
wtu.checkCanvas(gl, expected_arr, eval_str);
259+
}
260+
261+
gl.useProgram(point_prog);
262+
263+
gl.clear(gl.COLOR_BUFFER_BIT);
264+
fn(`gl.drawArrays(gl.POINTS, 0, 0)`, [0, 0, 0, 0]);
265+
fn(`gl.drawArrays(gl.POINTS, 0, 1)`, [255, 0, 0, 255]);
266+
fn(`gl.drawArrays(gl.POINTS, 0, 2)`, [255, 1, 0, 255]);
267+
fn(`gl.drawArrays(gl.POINTS, 100, 2)`, [255, 100+2-1, 0, 255]);
268+
fn(`gl.drawArrays(gl.POINTS, 0, 255)`, [255, 254, 0, 255]);
269+
fn(`gl.drawArrays(gl.POINTS, 0, 256)`, [255, 255, 0, 255]);
270+
271+
gl.clear(gl.COLOR_BUFFER_BIT);
272+
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 0, 1)`, [0, 0, 0, 0]);
273+
gl.clear(gl.COLOR_BUFFER_BIT);
274+
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 0)`, [0, 0, 0, 0]);
275+
276+
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 1)`, [255, 0, 0, 255]);
277+
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 2, 1)`, [255, 1, 0, 255]);
278+
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 2)`, [255, 0, 1, 255]);
279+
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 2, 2)`, [255, 1, 1, 255]);
280+
fn(`gl.drawArraysInstanced(gl.POINTS, 100, 2, 2)`, [255, 100+2-1, 1, 255]);
281+
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 255, 255)`, [255, 254, 254, 255]);
282+
283+
// -
284+
285+
gl.clear(gl.COLOR_BUFFER_BIT);
286+
fn(`gl.drawElements(gl.POINTS, 0, gl.UNSIGNED_INT, 4*0)`, [0, 0, 0, 0]);
287+
fn(`gl.drawElements(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0)`, [255, 10+0, 0, 255]);
288+
fn(`gl.drawElements(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0)`, [255, 10+1, 0, 255]);
289+
fn(`gl.drawElements(gl.POINTS, 2, gl.UNSIGNED_INT, 4*100)`, [255, 100+10+1, 0, 255]);
290+
fn(`gl.drawElements(gl.POINTS, 245, gl.UNSIGNED_INT, 4*0)`, [255, 10+244, 0, 255]);
291+
fn(`gl.drawElements(gl.POINTS, 246, gl.UNSIGNED_INT, 4*0)`, [255, 10+245, 0, 255]);
292+
293+
gl.clear(gl.COLOR_BUFFER_BIT);
294+
fn(`gl.drawElementsInstanced(gl.POINTS, 0, gl.UNSIGNED_INT, 4*0, 1)`, [0, 0, 0, 0]);
295+
gl.clear(gl.COLOR_BUFFER_BIT);
296+
fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 0)`, [0, 0, 0, 0]);
297+
298+
fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+0, 0, 255]);
299+
fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+1, 0, 255]);
300+
fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+0, 1, 255]);
301+
fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+1, 1, 255]);
302+
fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*100, 2)`, [255, 100+10+1, 1, 255]);
303+
fn(`gl.drawElementsInstanced(gl.POINTS, 245, gl.UNSIGNED_INT, 4*0, 255)`, [255, 10+244, 254, 255]);
304+
305+
// -
306+
307+
gl.useProgram(tri_prog);
308+
309+
gl.clear(gl.COLOR_BUFFER_BIT);
310+
fn(`gl.drawArrays(gl.TRIANGLES, 0, 0*3)`, [0, 0, 0, 0]);
311+
fn(`gl.drawArrays(gl.TRIANGLES, 0, 1*3)`, [255, 1*3-1, 0, 255]);
312+
fn(`gl.drawArrays(gl.TRIANGLES, 0, 2*3)`, [255, 2*3-1, 0, 255]);
313+
fn(`gl.drawArrays(gl.TRIANGLES, 90, 2*3)`, [255, 90+2*3-1, 0, 255]);
314+
315+
gl.clear(gl.COLOR_BUFFER_BIT);
316+
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 0, 1)`, [0, 0, 0, 0]);
317+
gl.clear(gl.COLOR_BUFFER_BIT);
318+
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 0)`, [0, 0, 0, 0]);
319+
320+
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 1)`, [255, 1*3-1, 0, 255]);
321+
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 2*3, 1)`, [255, 2*3-1, 0, 255]);
322+
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 2)`, [255, 1*3-1, 1, 255]);
323+
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 2*3, 2)`, [255, 2*3-1, 1, 255]);
324+
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 90, 2*3, 2)`, [255, 90+2*3-1, 1, 255]);
325+
326+
// -
327+
328+
gl.clear(gl.COLOR_BUFFER_BIT);
329+
fn(`gl.drawElements(gl.TRIANGLES, 0*3, gl.UNSIGNED_INT, 4*0)`, [0, 0, 0, 0]);
330+
fn(`gl.drawElements(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0)`, [255, 10+1*3-1, 0, 255]);
331+
fn(`gl.drawElements(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0)`, [255, 10+2*3-1, 0, 255]);
332+
fn(`gl.drawElements(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*100)`, [255, 100+10+2*3-1, 0, 255]);
333+
334+
gl.clear(gl.COLOR_BUFFER_BIT);
335+
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 0*3, gl.UNSIGNED_INT, 4*0, 1)`, [0, 0, 0, 0]);
336+
gl.clear(gl.COLOR_BUFFER_BIT);
337+
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 0)`, [0, 0, 0, 0]);
338+
339+
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+1*3-1, 0, 255]);
340+
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+2*3-1, 0, 255]);
341+
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+1*3-1, 1, 255]);
342+
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+2*3-1, 1, 255]);
343+
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*100, 2)`, [255, 100+10+2*3-1, 1, 255]);
344+
}
345+
346+
finishTest();
347+
})();
348+
349+
var successfullyParsed = true;
350+
</script>
351+
</body>
352+
</html>

0 commit comments

Comments
 (0)