| /* |
| 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 uniformLocation = gl.getUniformLocation(program, info.uniforms[i].name); |
| if (uniformLocation !== null) { |
| gl[info.uniforms[i].functionName](uniformLocation, info.uniforms[i].value); |
| debug(info.uniforms[i].name + ' set to ' + info.uniforms[i].value); |
| } else { |
| debug('uniform ' + info.uniforms[i].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 |
| }; |
| }()); |