Skip to content

Commit

Permalink
Add test of gl_VertexID and gl_InstanceID.
Browse files Browse the repository at this point in the history
Findings:
* Some MacOS (Intel?) has `first` affect `gl_InstanceID` in `DrawArrays`.

Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1779800
  • Loading branch information
kdashg committed Sep 8, 2022
1 parent 124b63d commit d86d88d
Show file tree
Hide file tree
Showing 2 changed files with 353 additions and 0 deletions.
1 change: 1 addition & 0 deletions sdk/tests/conformance2/rendering/00_test_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ blitframebuffer-size-overflow.html
--min-version 2.0.1 blitframebuffer-stencil-only.html
blitframebuffer-test.html
--min-version 2.0.1 blitframebuffer-unaffected-by-colormask.html
--min-version 2.0.1 builtin-vert-attribs.html
canvas-resizing-with-pbo-bound.html
--min-version 2.0.1 clearbuffer-sub-source.html
--min-version 2.0.1 clearbufferfv-with-alpha-false.html
Expand Down
352 changes: 352 additions & 0 deletions sdk/tests/conformance2/rendering/builtin-vert-attribs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
<!--
Copyright (c) 2022 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<link rel=stylesheet href="../../resources/js-test-style.css"/>
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
</head>
<body>
<canvas id=e_canvas width=1 height=1 style="width: 100px; height: 100px;"></canvas>
<div id=description></div>
<div id=console></div>
<script>
"use strict";
description('gl_VertexID and gl_InstanceID should behave per spec.');
// gl_VertexID behavior for DrawElements* is clarified in the ES3.1 spec,
// though it was implied previously.
// (gl_VertexID receives the ID fetched from the index buffer)

const wtu = WebGLTestUtils;
const gl = wtu.create3DContext('e_canvas');

const ERRATA = {};
//ERRATA.IGNORE_GL_INSTANCE_ID = true;
// No-workaround MacOS needs this.

//ERRATA.EXPECT_INDEXED_GL_VERTEX_ID_0 = true;
// Every GL driver I've found needs this.
// ANGLE-WebGL-on-D3D and -on-Metal do this properly.
// While admittedly the ES3.0 spec is a little vague, this was explicitly clarified in the ES3.1 spec.
// However, even ES3.1+ drivers seem to just leave gl_VertexID=0 for DrawElements*.

debug(`ERRATA: ${JSON.stringify(ERRATA)}`);

function make_vs_point(vid, iid) {
return `\
#version 300 es
${vid.name == 'gl_VertexID' ? '// ' : ''}layout(location=${vid.loc}) in highp int ${vid.name};
${iid.name == 'gl_InstanceID' ? '// ' :''}layout(location=${iid.loc}) in highp int ${iid.name};
out vec4 v_color;
void main() {
gl_PointSize = 1.0;
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
v_color = vec4(1.0, float(${vid.name}) / 255.0, float(${iid.name}) / 255.0, 1.0);
#if ${(iid.name == 'gl_InstanceID' && ERRATA.IGNORE_GL_INSTANCE_ID)|0}
v_color.b = 0.0;
#endif
}`;
}

function make_vs_tri(vid, iid) {
return `\
#version 300 es
${vid.name == 'gl_VertexID' ? '// ' : ''}layout(location=${vid.loc}) in highp int ${vid.name};
${iid.name == 'gl_InstanceID' ? '// ' :''}layout(location=${iid.loc}) in highp int ${iid.name};
out vec4 v_color;
void main() {
int prim_vert_id = ${vid.name} % 3;
int flat_vert_id = ${vid.name} - prim_vert_id + 2;
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
gl_Position.x = (prim_vert_id == 1) ? 2.0 : -1.0;
gl_Position.y = (prim_vert_id == 2) ? 2.0 : -1.0;
v_color = vec4(1.0, float(flat_vert_id) / 255.0, float(${iid.name}) / 255.0, 1.0);
#if ${(iid.name == 'gl_InstanceID' && ERRATA.IGNORE_GL_INSTANCE_ID)|0}
v_color.b = 0.0;
#endif
}`;
}

const FS = `\
#version 300 es
precision mediump float;
in vec4 v_color;
out vec4 o_color;
void main() {
o_color = v_color;
}
`;


function crossCombine(...args) {
function crossCombine2(listA, listB) {
const listC = [];
for (const a of listA) {
for (const b of listB) {
const c = Object.assign({}, a, b);
listC.push(c);
}
}
return listC;
}

let res = [{}];
while (args.length) {
const next = args.shift();
next[0].defined;
res = crossCombine2(res, next);
}
return res;
}

/// makeCombiner('foo', [5, 3]) -> [{foo: 5}, {foo: 3}]
function makeCombiner(key, vals) {
const ret = [];
for (const val of vals) {
const cur = {};
cur[key] = val;
ret.push(cur);
}
return ret;
}

debug('Draw a point with a shader that takes no attributes and verify it fills the whole canvas.');


let TESTS = [
makeCombiner('vid', [
{name: 'a_VertexID', loc:0},
{name: 'a_VertexID', loc:2}, // Test 2, so that we're not only testing 0.
{name: 'gl_VertexID', loc:-1},
{name: 'gl_VertexID', loc:0}, // Enable a vertex array, despite not using it.
{name: 'gl_VertexID', loc:2}, // Enable a vertex array, despite not using it.
]),
makeCombiner('iid', [
{name: 'a_InstanceID', loc:1},
{name: 'gl_InstanceID', loc:-1},
{name: 'gl_InstanceID', loc:1}, // Enable a vertex array, despite not using it.
]),
makeCombiner('separate_vbufs', [true, false]),
];
//console.log('a', {TESTS});
TESTS = crossCombine(...TESTS);
//console.log('b', {TESTS});


let vdata = new Int32Array(1000);
vdata = vdata.map((v,i) => i);
const vbuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbuf);
gl.bufferData(gl.ARRAY_BUFFER, vdata, gl.STATIC_DRAW);


const vbuf2 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbuf2);
gl.bufferData(gl.ARRAY_BUFFER, vdata, gl.STATIC_DRAW);


let index_data = new Uint32Array(1000);
index_data = index_data.map((x,i) => 10+i);
const index_buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_data, gl.STATIC_DRAW);


gl.disable(gl.DEPTH_TEST);

(async () => {
for (const desc of TESTS) {
await wtu.dispatchPromise(); // Yield, for responsiveness.
debug('');
debug('---------------------');
debug(`desc: ${JSON.stringify(desc)}`);

let fn = (vs) => {
//console.log({vs});
const prog = wtu.setupProgram(gl, [vs, FS]);

{
const WEBGL_debug_shaders = gl.getExtension('WEBGL_debug_shaders');
let i = -1;
for (const s of gl.getAttachedShaders(prog)) {
i += 1;
debug('');
debug(`shader[${i}] getShaderSource() -> `);
debug(gl.getShaderSource(s));
if (WEBGL_debug_shaders) {
debug(`shader[${i}] getTranslatedShaderSource() -> `);
debug(WEBGL_debug_shaders.getTranslatedShaderSource(s));
}
}
}
return prog;
};
const point_prog = fn(make_vs_point(desc.vid, desc.iid));
const tri_prog = fn(make_vs_tri(desc.vid, desc.iid));

// -

gl.bindBuffer(gl.ARRAY_BUFFER, null);
for (let i = 0; i <= 2; i++) {
gl.disableVertexAttribArray(i);
gl.vertexAttribPointer(i, 4, gl.FLOAT, false, 0, 0);
gl.vertexAttribDivisor(i, 0);
}

gl.bindBuffer(gl.ARRAY_BUFFER, vbuf);
let loc = desc.vid.loc;
if (loc != -1) {
gl.enableVertexAttribArray(loc);
gl.vertexAttribIPointer(loc, 1, gl.INT, 0, 0);
};

if (desc.separate_vbufs) {
gl.bindBuffer(gl.ARRAY_BUFFER, vbuf2);
}
loc = desc.iid.loc;
if (loc != -1) {
gl.enableVertexAttribArray(loc);
gl.vertexAttribIPointer(loc, 1, gl.INT, 0, 0);
gl.vertexAttribDivisor(loc, 1);
};

{
const err = gl.getError();
if (err) throw err; // Broken init.
}

// -

fn = (eval_str, expected_arr) => {
if (ERRATA.IGNORE_GL_INSTANCE_ID) {
if (desc.iid.name == 'gl_InstanceID') {
expected_arr = expected_arr.map(x => x);
expected_arr[2] = 0;
}
}
if (ERRATA.EXPECT_INDEXED_GL_VERTEX_ID_0) {
if (desc.vid.name == 'gl_VertexID' && eval_str.includes('Elements')) {
if (eval_str.includes('POINTS')) {
expected_arr = expected_arr.map(x => x);
expected_arr[1] = 0;
} else {
// TRIANGLES will fail to auto-generate a triangle mesh if gl_VertexID is always 0.
expected_arr = [0,0,0,0];
}
}
}

debug('');
//debug(`${eval_str} -> [${expected_arr.join(', ')}]`);
eval(eval_str);

const err = gl.getError();
if (err) throw err; // Broken subtest.

wtu.checkCanvas(gl, expected_arr, eval_str);
}

gl.useProgram(point_prog);

gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawArrays(gl.POINTS, 0, 0)`, [0, 0, 0, 0]);
fn(`gl.drawArrays(gl.POINTS, 0, 1)`, [255, 0, 0, 255]);
fn(`gl.drawArrays(gl.POINTS, 0, 2)`, [255, 1, 0, 255]);
fn(`gl.drawArrays(gl.POINTS, 100, 2)`, [255, 100+2-1, 0, 255]);
fn(`gl.drawArrays(gl.POINTS, 0, 255)`, [255, 254, 0, 255]);
fn(`gl.drawArrays(gl.POINTS, 0, 256)`, [255, 255, 0, 255]);

gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 0, 1)`, [0, 0, 0, 0]);
gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 0)`, [0, 0, 0, 0]);

fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 1)`, [255, 0, 0, 255]);
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 2, 1)`, [255, 1, 0, 255]);
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 2)`, [255, 0, 1, 255]);
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 2, 2)`, [255, 1, 1, 255]);
fn(`gl.drawArraysInstanced(gl.POINTS, 100, 2, 2)`, [255, 100+2-1, 1, 255]);
fn(`gl.drawArraysInstanced(gl.POINTS, 0, 255, 255)`, [255, 254, 254, 255]);

// -

gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawElements(gl.POINTS, 0, gl.UNSIGNED_INT, 4*0)`, [0, 0, 0, 0]);
fn(`gl.drawElements(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0)`, [255, 10+0, 0, 255]);
fn(`gl.drawElements(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0)`, [255, 10+1, 0, 255]);
fn(`gl.drawElements(gl.POINTS, 2, gl.UNSIGNED_INT, 4*100)`, [255, 100+10+1, 0, 255]);
fn(`gl.drawElements(gl.POINTS, 245, gl.UNSIGNED_INT, 4*0)`, [255, 10+244, 0, 255]);
fn(`gl.drawElements(gl.POINTS, 246, gl.UNSIGNED_INT, 4*0)`, [255, 10+245, 0, 255]);

gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawElementsInstanced(gl.POINTS, 0, gl.UNSIGNED_INT, 4*0, 1)`, [0, 0, 0, 0]);
gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 0)`, [0, 0, 0, 0]);

fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+0, 0, 255]);
fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+1, 0, 255]);
fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+0, 1, 255]);
fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+1, 1, 255]);
fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*100, 2)`, [255, 100+10+1, 1, 255]);
fn(`gl.drawElementsInstanced(gl.POINTS, 245, gl.UNSIGNED_INT, 4*0, 255)`, [255, 10+244, 254, 255]);

// -

gl.useProgram(tri_prog);

gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawArrays(gl.TRIANGLES, 0, 0*3)`, [0, 0, 0, 0]);
fn(`gl.drawArrays(gl.TRIANGLES, 0, 1*3)`, [255, 1*3-1, 0, 255]);
fn(`gl.drawArrays(gl.TRIANGLES, 0, 2*3)`, [255, 2*3-1, 0, 255]);
fn(`gl.drawArrays(gl.TRIANGLES, 90, 2*3)`, [255, 90+2*3-1, 0, 255]);

gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 0, 1)`, [0, 0, 0, 0]);
gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 0)`, [0, 0, 0, 0]);

fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 1)`, [255, 1*3-1, 0, 255]);
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 2*3, 1)`, [255, 2*3-1, 0, 255]);
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 2)`, [255, 1*3-1, 1, 255]);
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 2*3, 2)`, [255, 2*3-1, 1, 255]);
fn(`gl.drawArraysInstanced(gl.TRIANGLES, 90, 2*3, 2)`, [255, 90+2*3-1, 1, 255]);

// -

gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawElements(gl.TRIANGLES, 0*3, gl.UNSIGNED_INT, 4*0)`, [0, 0, 0, 0]);
fn(`gl.drawElements(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0)`, [255, 10+1*3-1, 0, 255]);
fn(`gl.drawElements(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0)`, [255, 10+2*3-1, 0, 255]);
fn(`gl.drawElements(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*100)`, [255, 100+10+2*3-1, 0, 255]);

gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 0*3, gl.UNSIGNED_INT, 4*0, 1)`, [0, 0, 0, 0]);
gl.clear(gl.COLOR_BUFFER_BIT);
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 0)`, [0, 0, 0, 0]);

fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+1*3-1, 0, 255]);
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+2*3-1, 0, 255]);
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+1*3-1, 1, 255]);
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+2*3-1, 1, 255]);
fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*100, 2)`, [255, 100+10+2*3-1, 1, 255]);
}

finishTest();
})();

var successfullyParsed = true;
</script>
</body>
</html>

0 comments on commit d86d88d

Please sign in to comment.