-
Notifications
You must be signed in to change notification settings - Fork 670
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
2 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
352 changes: 352 additions & 0 deletions
352
sdk/tests/conformance2/rendering/builtin-vert-attribs.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |