424 lines
13 KiB
JavaScript
424 lines
13 KiB
JavaScript
/*
|
|
Copyright (c) 2019 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.
|
|
*/
|
|
GLSLConformanceTester = (function(){
|
|
|
|
var wtu = WebGLTestUtils;
|
|
var defaultVertexShader = [
|
|
"attribute vec4 vPosition;",
|
|
"void main()",
|
|
"{",
|
|
" gl_Position = vPosition;",
|
|
"}"
|
|
].join('\n');
|
|
|
|
var defaultFragmentShader = [
|
|
"precision mediump float;",
|
|
"void main()",
|
|
"{",
|
|
" gl_FragColor = vec4(1.0,0.0,0.0,1.0);",
|
|
"}"
|
|
].join('\n');
|
|
|
|
var defaultESSL3VertexShader = [
|
|
"#version 300 es",
|
|
"in vec4 vPosition;",
|
|
"void main()",
|
|
"{",
|
|
" gl_Position = vPosition;",
|
|
"}"
|
|
].join('\n');
|
|
|
|
var defaultESSL3FragmentShader = [
|
|
"#version 300 es",
|
|
"precision mediump float;",
|
|
"out vec4 my_FragColor;",
|
|
"void main()",
|
|
"{",
|
|
" my_FragColor = vec4(1.0,0.0,0.0,1.0);",
|
|
"}"
|
|
].join('\n');
|
|
|
|
function log(msg) {
|
|
bufferedLogToConsole(msg);
|
|
}
|
|
|
|
var vShaderDB = {};
|
|
var fShaderDB = {};
|
|
|
|
/**
|
|
* The info parameter should contain the following keys. Note that you may leave
|
|
* the parameters for one shader out, in which case the default shader will be
|
|
* used.
|
|
* vShaderSource: the source code for vertex shader
|
|
* vShaderId: id of an element containing vertex shader source code. Used if
|
|
* vShaderSource is not specified.
|
|
* vShaderSuccess: true if vertex shader compilation should
|
|
* succeed.
|
|
* fShaderSource: the source code for fragment shader
|
|
* fShaderId: id of an element containing fragment shader source code. Used if
|
|
* fShaderSource is not specified.
|
|
* fShaderSuccess: true if fragment shader compilation should
|
|
* succeed.
|
|
* linkSuccess: true if link should succeed
|
|
* passMsg: msg to describe success condition.
|
|
* render: if true render to unit quad. Green = success
|
|
* uniforms: an array of objects specifying uniforms to set prior to rendering.
|
|
* Each object should have the following keys:
|
|
* name: uniform variable name in the shader source. Uniform location will
|
|
* be queried based on its name.
|
|
* functionName: name of the function used to set the uniform. For example:
|
|
* 'uniform1i'
|
|
* value: value of the uniform to set.
|
|
*/
|
|
function runOneTest(gl, info) {
|
|
var passMsg = info.passMsg
|
|
debug("");
|
|
debug("test: " + passMsg);
|
|
|
|
var consoleDiv = document.getElementById("console");
|
|
|
|
var vIsDefault = false;
|
|
var fIsDefault = false;
|
|
|
|
if (info.vShaderSource === undefined) {
|
|
if (info.vShaderId) {
|
|
info.vShaderSource = document.getElementById(info.vShaderId).text;
|
|
} else {
|
|
vIsDefault = true;
|
|
}
|
|
}
|
|
if (info.fShaderSource === undefined) {
|
|
if (info.fShaderId) {
|
|
info.fShaderSource = document.getElementById(info.fShaderId).text;
|
|
} else {
|
|
fIsDefault = true;
|
|
}
|
|
}
|
|
|
|
var vLabel = (vIsDefault ? "default" : "test") + " vertex shader";
|
|
var fLabel = (fIsDefault ? "default" : "test") + " fragment shader";
|
|
if (vIsDefault) {
|
|
info.vShaderSource = defaultVertexShader;
|
|
info.vShaderSuccess = true;
|
|
}
|
|
if (fIsDefault) {
|
|
info.fShaderSource = defaultFragmentShader;
|
|
info.fShaderSuccess = true;
|
|
}
|
|
|
|
if (vIsDefault != fIsDefault) {
|
|
// The language version of the default shader is chosen
|
|
// according to the language version of the other shader.
|
|
// We rely on "#version 300 es" being in this usual format.
|
|
// It must be on the first line of the shader according to the spec.
|
|
if (fIsDefault) {
|
|
// If we're using the default fragment shader, we need to make sure that
|
|
// it's language version matches with the vertex shader.
|
|
if (info.vShaderSource.split('\n')[0] == '#version 300 es') {
|
|
info.fShaderSource = defaultESSL3FragmentShader;
|
|
}
|
|
} else {
|
|
// If we're using the default vertex shader, we need to make sure that
|
|
// it's language version matches with the fragment shader.
|
|
if (info.fShaderSource.split('\n')[0] == '#version 300 es') {
|
|
info.vShaderSource = defaultESSL3VertexShader;
|
|
}
|
|
}
|
|
}
|
|
|
|
var vSource = info.vShaderPrep ? info.vShaderPrep(info.vShaderSource) :
|
|
info.vShaderSource;
|
|
|
|
if (!quietMode()) {
|
|
wtu.addShaderSource(consoleDiv, vLabel, vSource);
|
|
}
|
|
|
|
// Reuse identical shaders so we test shared shader.
|
|
var vShader = vShaderDB[vSource];
|
|
if (!vShader) {
|
|
// loadShader, with opt_skipCompileStatus: true.
|
|
vShader = wtu.loadShader(gl, vSource, gl.VERTEX_SHADER, null, null, null, null, true);
|
|
let compiledVShader = vShader;
|
|
if (vShader && !gl.getShaderParameter(vShader, gl.COMPILE_STATUS)) {
|
|
compiledVShader = null;
|
|
}
|
|
if (info.vShaderTest) {
|
|
if (!info.vShaderTest(compiledVShader)) {
|
|
testFailed("[vertex shader test] " + passMsg);
|
|
return;
|
|
}
|
|
}
|
|
// As per GLSL 1.0.17 10.27 we can only check for success on
|
|
// compileShader, not failure.
|
|
if (!info.ignoreResults && info.vShaderSuccess && !compiledVShader) {
|
|
testFailed("[unexpected vertex shader compile status] (expected: " +
|
|
info.vShaderSuccess + ") " + passMsg);
|
|
if (!quietMode() && vShader) {
|
|
const info = gl.getShaderInfoLog(vShader);
|
|
wtu.addShaderSource(consoleDiv, vLabel + " info log", info);
|
|
}
|
|
}
|
|
// Save the shaders so we test shared shader.
|
|
if (compiledVShader) {
|
|
vShaderDB[vSource] = compiledVShader;
|
|
} else {
|
|
vShader = null;
|
|
}
|
|
}
|
|
|
|
var debugShaders = gl.getExtension('WEBGL_debug_shaders');
|
|
if (debugShaders && vShader && !quietMode()) {
|
|
wtu.addShaderSource(consoleDiv, vLabel + " translated for driver",
|
|
debugShaders.getTranslatedShaderSource(vShader));
|
|
}
|
|
|
|
var fSource = info.fShaderPrep ? info.fShaderPrep(info.fShaderSource) :
|
|
info.fShaderSource;
|
|
|
|
if (!quietMode()) {
|
|
wtu.addShaderSource(consoleDiv, fLabel, fSource);
|
|
}
|
|
|
|
// Reuse identical shaders so we test shared shader.
|
|
var fShader = fShaderDB[fSource];
|
|
if (!fShader) {
|
|
// loadShader, with opt_skipCompileStatus: true.
|
|
fShader = wtu.loadShader(gl, fSource, gl.FRAGMENT_SHADER, null, null, null, null, true);
|
|
let compiledFShader = fShader;
|
|
if (fShader && !gl.getShaderParameter(fShader, gl.COMPILE_STATUS)) {
|
|
compiledFShader = null;
|
|
}
|
|
if (info.fShaderTest) {
|
|
if (!info.fShaderTest(compiledFShader)) {
|
|
testFailed("[fragment shader test] " + passMsg);
|
|
return;
|
|
}
|
|
}
|
|
//debug(fShader == null ? "fail" : "succeed");
|
|
// As per GLSL 1.0.17 10.27 we can only check for success on
|
|
// compileShader, not failure.
|
|
if (!info.ignoreResults && info.fShaderSuccess && !compiledFShader) {
|
|
testFailed("[unexpected fragment shader compile status] (expected: " +
|
|
info.fShaderSuccess + ") " + passMsg);
|
|
if (!quietMode() && fShader) {
|
|
const info = gl.getShaderInfoLog(fShader);
|
|
wtu.addShaderSource(consoleDiv, fLabel + " info log", info);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Safe the shaders so we test shared shader.
|
|
if (compiledFShader) {
|
|
fShaderDB[fSource] = compiledFShader;
|
|
} else {
|
|
fShader = null;
|
|
}
|
|
}
|
|
|
|
if (debugShaders && fShader && !quietMode()) {
|
|
wtu.addShaderSource(consoleDiv, fLabel + " translated for driver",
|
|
debugShaders.getTranslatedShaderSource(fShader));
|
|
}
|
|
|
|
if (vShader && fShader) {
|
|
var program = gl.createProgram();
|
|
gl.attachShader(program, vShader);
|
|
gl.attachShader(program, fShader);
|
|
|
|
if (vSource.indexOf("vPosition") >= 0) {
|
|
gl.bindAttribLocation(program, 0, "vPosition");
|
|
}
|
|
if (vSource.indexOf("texCoord0") >= 0) {
|
|
gl.bindAttribLocation(program, 1, "texCoord0");
|
|
}
|
|
gl.linkProgram(program);
|
|
var linked = (gl.getProgramParameter(program, gl.LINK_STATUS) != 0);
|
|
if (!linked) {
|
|
var error = gl.getProgramInfoLog(program);
|
|
log("*** Error linking program '"+program+"':"+error);
|
|
}
|
|
if (!info.ignoreResults && linked != info.linkSuccess) {
|
|
testFailed("[unexpected link status] (expected: " +
|
|
info.linkSuccess + ") " + passMsg);
|
|
return;
|
|
}
|
|
} else {
|
|
if (!info.ignoreResults && info.linkSuccess) {
|
|
testFailed("[link failed] " + passMsg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (parseInt(wtu.getUrlOptions().dumpShaders)) {
|
|
var vInfo = {
|
|
shader: vShader,
|
|
shaderSuccess: info.vShaderSuccess,
|
|
label: vLabel,
|
|
source: vSource
|
|
};
|
|
var fInfo = {
|
|
shader: fShader,
|
|
shaderSuccess: info.fShaderSuccess,
|
|
label: fLabel,
|
|
source: fSource
|
|
};
|
|
wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vInfo, fInfo);
|
|
}
|
|
|
|
if (!info.render) {
|
|
testPassed(passMsg);
|
|
return;
|
|
}
|
|
|
|
gl.useProgram(program);
|
|
|
|
if (info.uniforms !== undefined) {
|
|
for (var i = 0; i < info.uniforms.length; ++i) {
|
|
var uniform = info.uniforms[i];
|
|
var uniformLocation = gl.getUniformLocation(program, uniform.name);
|
|
if (uniformLocation !== null) {
|
|
if (uniform.functionName.includes("Matrix")) {
|
|
gl[uniform.functionName](uniformLocation, false, uniform.value);
|
|
} else {
|
|
gl[uniform.functionName](uniformLocation, uniform.value);
|
|
}
|
|
debug(uniform.name + ' set to ' + uniform.value);
|
|
} else {
|
|
debug('uniform ' + uniform.name + ' had null location and was not set');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info.uniformBlocks !== undefined) {
|
|
for (var i = 0; i < info.uniformBlocks.length; ++i) {
|
|
var uniformBlockIndex = gl.getUniformBlockIndex(program, info.uniformBlocks[i].name);
|
|
if (uniformBlockIndex !== null) {
|
|
gl.uniformBlockBinding(program, uniformBlockIndex, i);
|
|
debug(info.uniformBlocks[i].name + ' (index ' + uniformBlockIndex + ') bound to slot ' + i);
|
|
|
|
var uboValueBuffer = gl.createBuffer();
|
|
gl.bindBufferBase(gl.UNIFORM_BUFFER, i, uboValueBuffer);
|
|
gl.bufferData(gl.UNIFORM_BUFFER, info.uniformBlocks[i].value, info.uniformBlocks[i].usage || gl.STATIC_DRAW);
|
|
} else {
|
|
debug('uniform block' + info.uniformBlocks[i].name + ' had null block index and was not set');
|
|
}
|
|
}
|
|
}
|
|
|
|
wtu.setupUnitQuad(gl);
|
|
wtu.clearAndDrawUnitQuad(gl);
|
|
|
|
var div = document.createElement("div");
|
|
div.className = "testimages";
|
|
wtu.insertImage(div, "result", wtu.makeImageFromCanvas(gl.canvas));
|
|
div.appendChild(document.createElement('br'));
|
|
consoleDiv.appendChild(div);
|
|
|
|
var tolerance = 0;
|
|
if (info.renderTolerance !== undefined) {
|
|
tolerance = info.renderTolerance;
|
|
}
|
|
if (info.renderColor !== undefined) {
|
|
wtu.checkCanvas(gl, info.renderColor, "should be expected color " + info.renderColor, tolerance);
|
|
} else {
|
|
wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green", tolerance);
|
|
}
|
|
}
|
|
|
|
function runTests(shaderInfos, opt_contextVersion) {
|
|
var wtu = WebGLTestUtils;
|
|
var canvas = document.createElement('canvas');
|
|
canvas.width = 32;
|
|
canvas.height = 32;
|
|
var gl = wtu.create3DContext(canvas, undefined, opt_contextVersion);
|
|
if (!gl) {
|
|
testFailed("context does not exist");
|
|
finishTest();
|
|
return;
|
|
}
|
|
|
|
for (var i = 0; i < shaderInfos.length; i++) {
|
|
runOneTest(gl, shaderInfos[i]);
|
|
}
|
|
|
|
finishTest();
|
|
};
|
|
|
|
function getSource(elem) {
|
|
var str = elem.text;
|
|
return str.replace(/^\s*/, '').replace(/\s*$/, '');
|
|
}
|
|
|
|
function getPassMessage(source) {
|
|
var lines = source.split('\n');
|
|
return lines[0].substring(3);
|
|
}
|
|
|
|
function getSuccess(msg) {
|
|
if (msg.indexOf("fail") >= 0) {
|
|
return false;
|
|
}
|
|
if (msg.indexOf("succeed") >= 0) {
|
|
return true;
|
|
}
|
|
testFailed("bad test description. Must have 'fail' or 'succeed'");
|
|
}
|
|
|
|
function setupTest() {
|
|
var info = {};
|
|
|
|
var vShaderElem = document.getElementById('vertexShader');
|
|
if (vShaderElem) {
|
|
info.vShaderSource = getSource(vShaderElem);
|
|
info.passMsg = getPassMessage(info.vShaderSource);
|
|
info.vShaderSuccess = getSuccess(info.passMsg);
|
|
}
|
|
|
|
var fShaderElem = document.getElementById('fragmentShader');
|
|
if (fShaderElem) {
|
|
info.fShaderSource = getSource(fShaderElem);
|
|
info.passMsg = getPassMessage(info.fShaderSource);
|
|
info.fShaderSuccess = getSuccess(info.passMsg);
|
|
}
|
|
|
|
// linkSuccess should be true if shader success value is undefined or true for both shaders.
|
|
info.linkSuccess = info.vShaderSuccess !== false && info.fShaderSuccess !== false;
|
|
|
|
if (info.passMsg === undefined) {
|
|
testFailed("no test shader found.");
|
|
finishTest();
|
|
return;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
function runTest() {
|
|
var info = setupTest();
|
|
description(info.passMsg);
|
|
runTests([info]);
|
|
}
|
|
|
|
function runRenderTests(tests, opt_contextVersion) {
|
|
for (var ii = 0; ii < tests.length; ++ii) {
|
|
tests[ii].render = true
|
|
}
|
|
runTests(tests, opt_contextVersion);
|
|
}
|
|
|
|
function runRenderTest() {
|
|
var info = setupTest();
|
|
description(info.passMsg);
|
|
runRenderTests([info]);
|
|
}
|
|
|
|
return {
|
|
runTest: runTest,
|
|
runTests: runTests,
|
|
runRenderTest: runRenderTest,
|
|
runRenderTests: runRenderTests
|
|
};
|
|
}());
|