glsl-conformance-test.js (12929B)
1 /* 2 Copyright (c) 2019 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 GLSLConformanceTester = (function(){ 7 8 var wtu = WebGLTestUtils; 9 var defaultVertexShader = [ 10 "attribute vec4 vPosition;", 11 "void main()", 12 "{", 13 " gl_Position = vPosition;", 14 "}" 15 ].join('\n'); 16 17 var defaultFragmentShader = [ 18 "precision mediump float;", 19 "void main()", 20 "{", 21 " gl_FragColor = vec4(1.0,0.0,0.0,1.0);", 22 "}" 23 ].join('\n'); 24 25 var defaultESSL3VertexShader = [ 26 "#version 300 es", 27 "in vec4 vPosition;", 28 "void main()", 29 "{", 30 " gl_Position = vPosition;", 31 "}" 32 ].join('\n'); 33 34 var defaultESSL3FragmentShader = [ 35 "#version 300 es", 36 "precision mediump float;", 37 "out vec4 my_FragColor;", 38 "void main()", 39 "{", 40 " my_FragColor = vec4(1.0,0.0,0.0,1.0);", 41 "}" 42 ].join('\n'); 43 44 function log(msg) { 45 bufferedLogToConsole(msg); 46 } 47 48 var vShaderDB = {}; 49 var fShaderDB = {}; 50 51 /** 52 * The info parameter should contain the following keys. Note that you may leave 53 * the parameters for one shader out, in which case the default shader will be 54 * used. 55 * vShaderSource: the source code for vertex shader 56 * vShaderId: id of an element containing vertex shader source code. Used if 57 * vShaderSource is not specified. 58 * vShaderSuccess: true if vertex shader compilation should 59 * succeed. 60 * fShaderSource: the source code for fragment shader 61 * fShaderId: id of an element containing fragment shader source code. Used if 62 * fShaderSource is not specified. 63 * fShaderSuccess: true if fragment shader compilation should 64 * succeed. 65 * linkSuccess: true if link should succeed 66 * passMsg: msg to describe success condition. 67 * render: if true render to unit quad. Green = success 68 * uniforms: an array of objects specifying uniforms to set prior to rendering. 69 * Each object should have the following keys: 70 * name: uniform variable name in the shader source. Uniform location will 71 * be queried based on its name. 72 * functionName: name of the function used to set the uniform. For example: 73 * 'uniform1i' 74 * value: value of the uniform to set. 75 */ 76 function runOneTest(gl, info) { 77 var passMsg = info.passMsg 78 debug(""); 79 debug("test: " + passMsg); 80 81 var consoleDiv = document.getElementById("console"); 82 83 var vIsDefault = false; 84 var fIsDefault = false; 85 86 if (info.vShaderSource === undefined) { 87 if (info.vShaderId) { 88 info.vShaderSource = document.getElementById(info.vShaderId).text; 89 } else { 90 vIsDefault = true; 91 } 92 } 93 if (info.fShaderSource === undefined) { 94 if (info.fShaderId) { 95 info.fShaderSource = document.getElementById(info.fShaderId).text; 96 } else { 97 fIsDefault = true; 98 } 99 } 100 101 var vLabel = (vIsDefault ? "default" : "test") + " vertex shader"; 102 var fLabel = (fIsDefault ? "default" : "test") + " fragment shader"; 103 if (vIsDefault) { 104 info.vShaderSource = defaultVertexShader; 105 info.vShaderSuccess = true; 106 } 107 if (fIsDefault) { 108 info.fShaderSource = defaultFragmentShader; 109 info.fShaderSuccess = true; 110 } 111 112 if (vIsDefault != fIsDefault) { 113 // The language version of the default shader is chosen 114 // according to the language version of the other shader. 115 // We rely on "#version 300 es" being in this usual format. 116 // It must be on the first line of the shader according to the spec. 117 if (fIsDefault) { 118 // If we're using the default fragment shader, we need to make sure that 119 // it's language version matches with the vertex shader. 120 if (info.vShaderSource.split('\n')[0] == '#version 300 es') { 121 info.fShaderSource = defaultESSL3FragmentShader; 122 } 123 } else { 124 // If we're using the default vertex shader, we need to make sure that 125 // it's language version matches with the fragment shader. 126 if (info.fShaderSource.split('\n')[0] == '#version 300 es') { 127 info.vShaderSource = defaultESSL3VertexShader; 128 } 129 } 130 } 131 132 var vSource = info.vShaderPrep ? info.vShaderPrep(info.vShaderSource) : 133 info.vShaderSource; 134 135 if (!quietMode()) { 136 wtu.addShaderSource(consoleDiv, vLabel, vSource); 137 } 138 139 // Reuse identical shaders so we test shared shader. 140 var vShader = vShaderDB[vSource]; 141 if (!vShader) { 142 // loadShader, with opt_skipCompileStatus: true. 143 vShader = wtu.loadShader(gl, vSource, gl.VERTEX_SHADER, null, null, null, null, true); 144 let compiledVShader = vShader; 145 if (vShader && !gl.getShaderParameter(vShader, gl.COMPILE_STATUS)) { 146 compiledVShader = null; 147 } 148 if (info.vShaderTest) { 149 if (!info.vShaderTest(compiledVShader)) { 150 testFailed("[vertex shader test] " + passMsg); 151 return; 152 } 153 } 154 // As per GLSL 1.0.17 10.27 we can only check for success on 155 // compileShader, not failure. 156 if (!info.ignoreResults && info.vShaderSuccess && !compiledVShader) { 157 testFailed("[unexpected vertex shader compile status] (expected: " + 158 info.vShaderSuccess + ") " + passMsg); 159 if (!quietMode() && vShader) { 160 const info = gl.getShaderInfoLog(vShader); 161 wtu.addShaderSource(consoleDiv, vLabel + " info log", info); 162 } 163 } 164 // Save the shaders so we test shared shader. 165 if (compiledVShader) { 166 vShaderDB[vSource] = compiledVShader; 167 } else { 168 vShader = null; 169 } 170 } 171 172 var debugShaders = gl.getExtension('WEBGL_debug_shaders'); 173 if (debugShaders && vShader && !quietMode()) { 174 wtu.addShaderSource(consoleDiv, vLabel + " translated for driver", 175 debugShaders.getTranslatedShaderSource(vShader)); 176 } 177 178 var fSource = info.fShaderPrep ? info.fShaderPrep(info.fShaderSource) : 179 info.fShaderSource; 180 181 if (!quietMode()) { 182 wtu.addShaderSource(consoleDiv, fLabel, fSource); 183 } 184 185 // Reuse identical shaders so we test shared shader. 186 var fShader = fShaderDB[fSource]; 187 if (!fShader) { 188 // loadShader, with opt_skipCompileStatus: true. 189 fShader = wtu.loadShader(gl, fSource, gl.FRAGMENT_SHADER, null, null, null, null, true); 190 let compiledFShader = fShader; 191 if (fShader && !gl.getShaderParameter(fShader, gl.COMPILE_STATUS)) { 192 compiledFShader = null; 193 } 194 if (info.fShaderTest) { 195 if (!info.fShaderTest(compiledFShader)) { 196 testFailed("[fragment shader test] " + passMsg); 197 return; 198 } 199 } 200 //debug(fShader == null ? "fail" : "succeed"); 201 // As per GLSL 1.0.17 10.27 we can only check for success on 202 // compileShader, not failure. 203 if (!info.ignoreResults && info.fShaderSuccess && !compiledFShader) { 204 testFailed("[unexpected fragment shader compile status] (expected: " + 205 info.fShaderSuccess + ") " + passMsg); 206 if (!quietMode() && fShader) { 207 const info = gl.getShaderInfoLog(fShader); 208 wtu.addShaderSource(consoleDiv, fLabel + " info log", info); 209 } 210 return; 211 } 212 213 // Safe the shaders so we test shared shader. 214 if (compiledFShader) { 215 fShaderDB[fSource] = compiledFShader; 216 } else { 217 fShader = null; 218 } 219 } 220 221 if (debugShaders && fShader && !quietMode()) { 222 wtu.addShaderSource(consoleDiv, fLabel + " translated for driver", 223 debugShaders.getTranslatedShaderSource(fShader)); 224 } 225 226 if (vShader && fShader) { 227 var program = gl.createProgram(); 228 gl.attachShader(program, vShader); 229 gl.attachShader(program, fShader); 230 231 if (vSource.indexOf("vPosition") >= 0) { 232 gl.bindAttribLocation(program, 0, "vPosition"); 233 } 234 if (vSource.indexOf("texCoord0") >= 0) { 235 gl.bindAttribLocation(program, 1, "texCoord0"); 236 } 237 gl.linkProgram(program); 238 var linked = (gl.getProgramParameter(program, gl.LINK_STATUS) != 0); 239 if (!linked) { 240 var error = gl.getProgramInfoLog(program); 241 log("*** Error linking program '"+program+"':"+error); 242 } 243 if (!info.ignoreResults && linked != info.linkSuccess) { 244 testFailed("[unexpected link status] (expected: " + 245 info.linkSuccess + ") " + passMsg); 246 return; 247 } 248 } else { 249 if (!info.ignoreResults && info.linkSuccess) { 250 testFailed("[link failed] " + passMsg); 251 return; 252 } 253 } 254 255 if (parseInt(wtu.getUrlOptions().dumpShaders)) { 256 var vInfo = { 257 shader: vShader, 258 shaderSuccess: info.vShaderSuccess, 259 label: vLabel, 260 source: vSource 261 }; 262 var fInfo = { 263 shader: fShader, 264 shaderSuccess: info.fShaderSuccess, 265 label: fLabel, 266 source: fSource 267 }; 268 wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vInfo, fInfo); 269 } 270 271 if (!info.render) { 272 testPassed(passMsg); 273 return; 274 } 275 276 gl.useProgram(program); 277 278 if (info.uniforms !== undefined) { 279 for (var i = 0; i < info.uniforms.length; ++i) { 280 var uniform = info.uniforms[i]; 281 var uniformLocation = gl.getUniformLocation(program, uniform.name); 282 if (uniformLocation !== null) { 283 if (uniform.functionName.includes("Matrix")) { 284 gl[uniform.functionName](uniformLocation, false, uniform.value); 285 } else { 286 gl[uniform.functionName](uniformLocation, uniform.value); 287 } 288 debug(uniform.name + ' set to ' + uniform.value); 289 } else { 290 debug('uniform ' + uniform.name + ' had null location and was not set'); 291 } 292 } 293 } 294 295 if (info.uniformBlocks !== undefined) { 296 for (var i = 0; i < info.uniformBlocks.length; ++i) { 297 var uniformBlockIndex = gl.getUniformBlockIndex(program, info.uniformBlocks[i].name); 298 if (uniformBlockIndex !== null) { 299 gl.uniformBlockBinding(program, uniformBlockIndex, i); 300 debug(info.uniformBlocks[i].name + ' (index ' + uniformBlockIndex + ') bound to slot ' + i); 301 302 var uboValueBuffer = gl.createBuffer(); 303 gl.bindBufferBase(gl.UNIFORM_BUFFER, i, uboValueBuffer); 304 gl.bufferData(gl.UNIFORM_BUFFER, info.uniformBlocks[i].value, info.uniformBlocks[i].usage || gl.STATIC_DRAW); 305 } else { 306 debug('uniform block' + info.uniformBlocks[i].name + ' had null block index and was not set'); 307 } 308 } 309 } 310 311 wtu.setupUnitQuad(gl); 312 wtu.clearAndDrawUnitQuad(gl); 313 314 var div = document.createElement("div"); 315 div.className = "testimages"; 316 wtu.insertImage(div, "result", wtu.makeImageFromCanvas(gl.canvas)); 317 div.appendChild(document.createElement('br')); 318 consoleDiv.appendChild(div); 319 320 var tolerance = 0; 321 if (info.renderTolerance !== undefined) { 322 tolerance = info.renderTolerance; 323 } 324 if (info.renderColor !== undefined) { 325 wtu.checkCanvas(gl, info.renderColor, "should be expected color " + info.renderColor, tolerance); 326 } else { 327 wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green", tolerance); 328 } 329 } 330 331 function runTests(shaderInfos, opt_contextVersion) { 332 var wtu = WebGLTestUtils; 333 var canvas = document.createElement('canvas'); 334 canvas.width = 32; 335 canvas.height = 32; 336 var gl = wtu.create3DContext(canvas, undefined, opt_contextVersion); 337 if (!gl) { 338 testFailed("context does not exist"); 339 finishTest(); 340 return; 341 } 342 343 for (var i = 0; i < shaderInfos.length; i++) { 344 runOneTest(gl, shaderInfos[i]); 345 } 346 347 finishTest(); 348 }; 349 350 function getSource(elem) { 351 var str = elem.text; 352 return str.replace(/^\s*/, '').replace(/\s*$/, ''); 353 } 354 355 function getPassMessage(source) { 356 var lines = source.split('\n'); 357 return lines[0].substring(3); 358 } 359 360 function getSuccess(msg) { 361 if (msg.indexOf("fail") >= 0) { 362 return false; 363 } 364 if (msg.indexOf("succeed") >= 0) { 365 return true; 366 } 367 testFailed("bad test description. Must have 'fail' or 'succeed'"); 368 } 369 370 function setupTest() { 371 var info = {}; 372 373 var vShaderElem = document.getElementById('vertexShader'); 374 if (vShaderElem) { 375 info.vShaderSource = getSource(vShaderElem); 376 info.passMsg = getPassMessage(info.vShaderSource); 377 info.vShaderSuccess = getSuccess(info.passMsg); 378 } 379 380 var fShaderElem = document.getElementById('fragmentShader'); 381 if (fShaderElem) { 382 info.fShaderSource = getSource(fShaderElem); 383 info.passMsg = getPassMessage(info.fShaderSource); 384 info.fShaderSuccess = getSuccess(info.passMsg); 385 } 386 387 // linkSuccess should be true if shader success value is undefined or true for both shaders. 388 info.linkSuccess = info.vShaderSuccess !== false && info.fShaderSuccess !== false; 389 390 if (info.passMsg === undefined) { 391 testFailed("no test shader found."); 392 finishTest(); 393 return; 394 } 395 396 return info; 397 } 398 399 function runTest() { 400 var info = setupTest(); 401 description(info.passMsg); 402 runTests([info]); 403 } 404 405 function runRenderTests(tests, opt_contextVersion) { 406 for (var ii = 0; ii < tests.length; ++ii) { 407 tests[ii].render = true 408 } 409 runTests(tests, opt_contextVersion); 410 } 411 412 function runRenderTest() { 413 var info = setupTest(); 414 description(info.passMsg); 415 runRenderTests([info]); 416 } 417 418 return { 419 runTest: runTest, 420 runTests: runTests, 421 runRenderTest: runRenderTest, 422 runRenderTests: runRenderTests 423 }; 424 }());