program-test.html (19113B)
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 <!DOCTYPE html> 7 <html> 8 <head> 9 <meta charset="utf-8"> 10 <title>WebGL Program Compiling/Linking Conformance Test</title> 11 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 12 <script src="../../js/js-test-pre.js"></script> 13 <script src="../../js/desktop-gl-constants.js"></script> 14 <script src="../../js/webgl-test-utils.js"></script> 15 </head> 16 <body> 17 <script id="vshader" type="x-shader/x-vertex"> 18 attribute vec4 a_position; 19 void main() 20 { 21 gl_Position = a_position; 22 } 23 </script> 24 <script id="fshader-red" type="x-shader/x-fragment"> 25 void main() 26 { 27 gl_FragColor = vec4(1, 0, 0, 1); 28 } 29 </script> 30 <script id="fshader-green" type="x-shader/x-fragment"> 31 void main() 32 { 33 gl_FragColor = vec4(0, 1, 0, 1); 34 } 35 </script> 36 <script id="fshader-settable" type="x-shader/x-fragment"> 37 precision mediump float; 38 uniform vec4 u_color; 39 void main() 40 { 41 gl_FragColor = u_color; 42 } 43 </script> 44 <div id="description"></div> 45 <div id="console"></div> 46 <canvas id="canvas" width="2" height="2"> </canvas> 47 <script type="application/javascript"> 48 var wtu = WebGLTestUtils; 49 let gl; 50 function go() { 51 description("Tests that program compiling/linking/using works correctly."); 52 53 debug(""); 54 debug("Canvas.getContext"); 55 56 gl = wtu.create3DContext("canvas"); 57 if (!gl) { 58 testFailed("context does not exist"); 59 return; 60 } 61 62 testPassed("context exists"); 63 64 gl.clearColor(0.0, 0.0, 0.0, 0.0); 65 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 66 67 function doArraysHaveSameContents(a, b) { 68 var flags = []; 69 function hasUnusedValue(a, value) { 70 for (var ii = 0; ii < a.length; ++ii) { 71 if (a[ii] === value && !flags[ii]) { 72 flags[ii] = true; 73 return true; 74 } 75 } 76 return false; 77 } 78 79 try { 80 if (a.length !== b.length) { 81 return false; 82 } 83 for (var ii = 0; ii < a.length; ii++) { 84 if (!hasUnusedValue(b, a[ii])) { 85 return false; 86 } 87 } 88 } catch (ex) { 89 return false; 90 } 91 return true; 92 } 93 94 /////// Check compileShader() ///////////////////////////// 95 96 var vs = gl.createShader(gl.VERTEX_SHADER); 97 gl.shaderSource(vs, "attribute vec4 aVertex; attribute vec4 aColor; varying vec4 vColor; void main() { vColor = aColor; gl_Position = aVertex; }"); 98 gl.compileShader(vs); 99 100 assertMsg(gl.getShaderParameter(vs, gl.COMPILE_STATUS) == true, 101 "good vertex shader should compile"); 102 103 // Verify that constants removed from the WebGL spec generate INVALID_ENUM errors 104 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors at this point"); 105 assertMsg(gl.getShaderParameter(vs, desktopGL['INFO_LOG_LENGTH']) === null, "invalid call to getShaderParameter should return null"); 106 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "INFO_LOG_LENGTH is not a valid argument to getShaderParameter in WebGL"); 107 assertMsg(gl.getShaderParameter(vs, desktopGL['SHADER_SOURCE_LENGTH']) === null, "invalid call to getShaderParameter should return null"); 108 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "SHADER_SOURCE_LENGTH is not a valid argument to getShaderParameter in WebGL"); 109 110 var vs2 = gl.createShader(gl.VERTEX_SHADER); 111 gl.shaderSource(vs2, "attribute vec4 aVertex; attribute vec4 aColor; varying vec4 vColor; void main() { vColor = aColor; gl_Position = aVertex * 0.5; }"); 112 gl.compileShader(vs2); 113 114 assertMsg(gl.getShaderParameter(vs2, gl.COMPILE_STATUS) == true, 115 "good vertex shader #2 should compile"); 116 117 var vsBad = gl.createShader(gl.VERTEX_SHADER); 118 gl.shaderSource(vsBad, "WILL NOT COMPILE;"); 119 gl.compileShader(vsBad); 120 121 // GLSL 1.0.17 section 10.27. compile shader does not have to return failure. 122 //assertMsg(gl.getShaderParameter(vsBad, gl.COMPILE_STATUS) == false, 123 // "bad vertex shader should fail to compile"); 124 125 var fs = gl.createShader(gl.FRAGMENT_SHADER); 126 gl.shaderSource(fs, "precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; }"); 127 gl.compileShader(fs); 128 129 assertMsg(gl.getShaderParameter(fs, gl.COMPILE_STATUS) == true, 130 "good fragment shader should compile"); 131 132 var fs2 = gl.createShader(gl.FRAGMENT_SHADER); 133 gl.shaderSource(fs2, "precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor * 0.5; }"); 134 gl.compileShader(fs2); 135 136 assertMsg(gl.getShaderParameter(fs2, gl.COMPILE_STATUS) == true, 137 "good fragment shader #2 should compile"); 138 139 var fsBad = gl.createShader(gl.FRAGMENT_SHADER); 140 gl.shaderSource(fsBad, "WILL NOT COMPILE;"); 141 gl.compileShader(fsBad); 142 143 // GLSL 1.0.17 section 10.27. compile shader does not have to return failure. 144 //assertMsg(gl.getShaderParameter(fsBad, gl.COMPILE_STATUS) == false, 145 // "bad fragment shader should fail to compile"); 146 147 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors at this point"); 148 149 /////// Check attachShader() ///////////////////////////// 150 151 function checkAttachShader(already_attached_shaders, shader, expected_error_code, errmsg) { 152 var prog = gl.createProgram(); 153 for (var i = 0; i < already_attached_shaders.length; ++i) 154 gl.attachShader(prog, already_attached_shaders[i]); 155 if(gl.getError() != gl.NO_ERROR) 156 assertMsg(false, "unexpected error in attachShader()"); 157 gl.attachShader(prog, shader); 158 wtu.glErrorShouldBe(gl, expected_error_code, errmsg); 159 } 160 161 checkAttachShader([], vs, gl.NO_ERROR, "attaching a vertex shader should succeed"); 162 checkAttachShader([vs], vs, gl.INVALID_OPERATION, 163 "attaching an already attached vertex shader should generate INVALID_OPERATION"); 164 checkAttachShader([], fs, gl.NO_ERROR, "attaching a fragment shader should succeed"); 165 checkAttachShader([fs], fs, gl.INVALID_OPERATION, 166 "attaching an already attached fragment shader should generate INVALID_OPERATION"); 167 checkAttachShader([vs], vs2, gl.INVALID_OPERATION, 168 "attaching shaders of the same type to a program should generate INVALID_OPERATION"); 169 checkAttachShader([fs], fs2, gl.INVALID_OPERATION, 170 "attaching shaders of the same type to a program should generate INVALID_OPERATION"); 171 172 /////// Check detachShader() ///////////////////////////// 173 174 function checkDetachShader(already_attached_shaders, shader, expected_error_code, errmsg) { 175 var prog = gl.createProgram(); 176 for (var i = 0; i < already_attached_shaders.length; ++i) 177 gl.attachShader(prog, already_attached_shaders[i]); 178 if(gl.getError() != gl.NO_ERROR) 179 assertMsg(false, "unexpected error in attachShader()"); 180 gl.detachShader(prog, shader); 181 wtu.glErrorShouldBe(gl, expected_error_code, errmsg); 182 } 183 184 checkDetachShader([vs], vs, gl.NO_ERROR, "detaching a vertex shader should succeed"); 185 checkDetachShader([fs], vs, gl.INVALID_OPERATION, 186 "detaching a not already attached vertex shader should generate INVALID_OPERATION"); 187 checkDetachShader([fs], fs, gl.NO_ERROR, "detaching a fragment shader should succeed"); 188 checkDetachShader([vs], fs, gl.INVALID_OPERATION, 189 "detaching a not already attached fragment shader should generate INVALID_OPERATION"); 190 191 /////// Check getAttachedShaders() ///////////////////////////// 192 193 function checkGetAttachedShaders(shaders_to_attach, shaders_to_detach, expected_shaders, errmsg) { 194 prog = gl.createProgram(); 195 for (var i = 0; i < shaders_to_attach.length; ++i) 196 gl.attachShader(prog, shaders_to_attach[i]); 197 if(gl.getError() != gl.NO_ERROR) 198 assertMsg(false, "unexpected error in attachShader()"); 199 for (var i = 0; i < shaders_to_detach.length; ++i) 200 gl.detachShader(prog, shaders_to_detach[i]); 201 if(gl.getError() != gl.NO_ERROR) 202 assertMsg(false, "unexpected error in detachShader()"); 203 assertMsg(doArraysHaveSameContents(gl.getAttachedShaders(prog), expected_shaders), errmsg); 204 shouldBe('gl.getProgramParameter(prog, gl.ATTACHED_SHADERS)', `${expected_shaders.length}`); 205 } 206 checkGetAttachedShaders([], [], [], "getAttachedShaders should return an empty list by default"); 207 checkGetAttachedShaders([fs], [], [fs], "attaching a single shader should give the expected list"); 208 checkGetAttachedShaders([fs, vs], [], [fs, vs], 209 "attaching some shaders should give the expected list"); 210 checkGetAttachedShaders([fs], [fs], [], "attaching a shader and detaching it should leave an empty list"); 211 checkGetAttachedShaders([fs, vs], [fs, vs], [], 212 "attaching some shaders and detaching them in same order should leave an empty list"); 213 checkGetAttachedShaders([fs, vs], [vs, fs], [], 214 "attaching some shaders and detaching them in random order should leave an empty list"); 215 checkGetAttachedShaders([fs, vs], [vs], [fs], 216 "attaching and detaching some shaders should leave the difference list"); 217 checkGetAttachedShaders([fs, vs], [fs], [vs], 218 "attaching and detaching some shaders should leave the difference list"); 219 checkGetAttachedShaders([fsBad], [], [fsBad], 220 "attaching a shader that failed to compile should still show it in the list"); 221 checkGetAttachedShaders([fs, vsBad], [], [fs, vsBad], 222 "attaching shaders, including one that failed to compile, should still show the it in the list"); 223 224 /////// Check linkProgram() and useProgram ///////////////////////////// 225 226 function checkLinkAndUse(shaders, deleteShaderAfterAttach, expected_status, testInvalidEnums, errmsg) { 227 var prog = gl.createProgram(); 228 for (var i = 0; i < shaders.length; ++i) { 229 gl.attachShader(prog, shaders[i]); 230 if (deleteShaderAfterAttach) 231 gl.deleteShader(shaders[i]); 232 } 233 gl.bindAttribLocation(prog, 0, "aVertex"); 234 gl.bindAttribLocation(prog, 1, "aColor"); 235 gl.linkProgram(prog); 236 if (gl.getError() != gl.NO_ERROR) 237 assertMsg(false, "unexpected error in linkProgram()"); 238 assertMsg(gl.getProgramParameter(prog, gl.LINK_STATUS) == expected_status, errmsg); 239 var infolog = gl.getProgramInfoLog(prog); 240 if (gl.getError() != gl.NO_ERROR) 241 assertMsg(false, "unexpected error in getProgramInfoLog()"); 242 if (typeof(infolog) != "string") 243 assertMsg(false, "getProgramInfoLog() did not return a string"); 244 if (expected_status == true && gl.getProgramParameter(prog, gl.LINK_STATUS) == false) 245 debug(infolog); 246 if (gl.getError() != gl.NO_ERROR) 247 assertMsg(false, "unexpected error in getProgramParameter()"); 248 249 if (testInvalidEnums) { 250 // Verify that constants removed from the WebGL spec generate INVALID_ENUM errors 251 assertMsg(gl.getProgramParameter(prog, desktopGL['INFO_LOG_LENGTH']) === null, "invalid call to getProgramParameter should return null"); 252 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "INFO_LOG_LENGTH is not a valid argument to getProgramParameter in WebGL"); 253 assertMsg(gl.getProgramParameter(prog, desktopGL['ACTIVE_ATTRIBUTE_MAX_LENGTH']) === null, "invalid call to getProgramParameter should return null"); 254 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "ACTIVE_ATTRIBUTE_MAX_LENGTH is not a valid argument to getProgramParameter in WebGL"); 255 assertMsg(gl.getProgramParameter(prog, desktopGL['ACTIVE_UNIFORM_MAX_LENGTH']) === null, "invalid call to getProgramParameter should return null"); 256 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "ACTIVE_UNIFORM_MAX_LENGTH is not a valid argument to getProgramParameter in WebGL"); 257 } 258 259 gl.useProgram(prog); 260 if (expected_status == true) 261 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "using a valid program should succeed"); 262 if (expected_status == false) 263 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "using an invalid program should generate INVALID_OPERATION"); 264 return prog; 265 } 266 267 var progGood1 = checkLinkAndUse([vs, fs], false, true, true, "valid program should link"); 268 var progGood2 = checkLinkAndUse([vs, fs2], false, true, false, "valid program #2 should link"); 269 var progBad1 = checkLinkAndUse([vs], false, false, false, "program with no fragment shader should fail to link"); 270 var progBad2 = checkLinkAndUse([fs], false, false, false, "program with no vertex shader should fail to link"); 271 var progBad3 = checkLinkAndUse([vsBad, fs], false, false, false, "program with bad vertex shader should fail to link"); 272 var progBad4 = checkLinkAndUse([vs, fsBad], false, false, false, "program with bad fragment shader should fail to link"); 273 var progBad5 = checkLinkAndUse([vsBad, fsBad], false, false, false, "program with bad shaders should fail to link"); 274 275 gl.useProgram(progGood1); 276 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "using a valid program shouldn't generate a GL error"); 277 278 var vbuf = gl.createBuffer(); 279 gl.bindBuffer(gl.ARRAY_BUFFER, vbuf); 280 gl.bufferData(gl.ARRAY_BUFFER, 281 new Float32Array([ 282 0.0, 0.0, 0.0, 1.0, 283 1.0, 0.0, 0.0, 1.0, 284 1.0, 1.0, 0.0, 1.0, 285 0.0, 1.0, 0.0, 1.0]), 286 gl.STATIC_DRAW); 287 gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0); 288 gl.enableVertexAttribArray(0); 289 gl.vertexAttrib3f(1, 1.0, 0.0, 0.0); 290 291 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors at this point #2"); 292 293 gl.useProgram(null); 294 gl.drawArrays(gl.TRIANGLES, 0, 3); 295 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawing with a null program should generate INVALID_OPERATION"); 296 297 gl.useProgram(progGood1); 298 gl.drawArrays(gl.TRIANGLES, 0, 3); 299 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid program shouldn't generate a GL error"); 300 301 gl.useProgram(progBad1); 302 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "using an invalid program should generate INVALID_OPERATION"); 303 gl.drawArrays(gl.TRIANGLES, 0, 3); 304 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Try to use an invalid program should not change the current rendering state"); 305 306 gl.useProgram(progGood2); 307 gl.drawArrays(gl.TRIANGLES, 0, 3); 308 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid program shouldn't generate a GL error"); 309 gl.detachShader(progGood2, fs2); 310 gl.attachShader(progGood2, fsBad); 311 gl.linkProgram(progGood2); 312 assertMsg(gl.getProgramParameter(progGood2, gl.LINK_STATUS) == false, 313 "linking should fail with in-use formerly good program, with new bad shader attached"); 314 315 // In WebGL, an unsuccessful link immediately invalidates the previous valid program. 316 gl.drawArrays(gl.TRIANGLES, 0, 3); 317 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawing with a newly-invalidated program should generate INVALID_OPERATION"); 318 319 gl.useProgram(progGood1); 320 gl.drawArrays(gl.TRIANGLES, 0, 4); 321 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid when last used program shouldn't generate a GL error"); 322 323 var progGood1 = checkLinkAndUse([vs, fs], true, true, false, "delete shaders after attaching them and before linking program should not affect linkProgram"); 324 gl.useProgram(progGood1); 325 gl.drawArrays(gl.TRIANGLES, 0, 4); 326 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing with a valid when last used program shouldn't generate a GL error"); 327 328 /////// Check deleteProgram() and deleteShader() ///////////////////////////// 329 330 gl.useProgram(progGood1); 331 gl.deleteProgram(progGood1); 332 gl.drawArrays(gl.TRIANGLES, 0, 4); 333 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "delete the current program shouldn't change the current rendering state"); 334 335 gl.linkProgram(progGood1); 336 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "The current program shouldn't be deleted"); 337 338 var fs3 = gl.createShader(gl.FRAGMENT_SHADER); 339 gl.shaderSource(fs3, "precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; }"); 340 gl.compileShader(fs3); 341 342 assertMsg(gl.getShaderParameter(fs3, gl.COMPILE_STATUS) == true, 343 "good fragment shader should compile"); 344 345 gl.deleteShader(fs3); 346 gl.compileShader(fs3); 347 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "an unattached shader should be deleted immediately"); 348 349 fs3 = gl.createShader(gl.FRAGMENT_SHADER); 350 gl.shaderSource(fs3, "precision mediump float; varying vec4 vColor; void main() { gl_FragColor = vColor; }"); 351 gl.compileShader(fs3); 352 353 assertMsg(gl.getShaderParameter(fs3, gl.COMPILE_STATUS) == true, 354 "good fragment shader should compile"); 355 356 gl.detachShader(progGood1, fs); 357 gl.attachShader(progGood1, fs3); 358 359 gl.deleteShader(fs3); 360 gl.compileShader(fs3); 361 assertMsg(gl.getShaderParameter(fs3, gl.COMPILE_STATUS) == true, 362 "an attached shader shouldn't be deleted"); 363 364 gl.useProgram(null); 365 gl.linkProgram(progGood1); 366 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "a delete-marked program should be deleted once it's no longer the current program"); 367 368 gl.compileShader(fs3); 369 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "a delete-marked shader should be deleted once all its attachments are removed"); 370 371 //////// Check linkProgram() with relinked program ////////// 372 var vs = wtu.loadShaderFromScript(gl, "vshader"); 373 var fs = wtu.loadShaderFromScript(gl, "fshader-red"); 374 var prg = wtu.createProgram(gl, vs, fs); 375 gl.useProgram(prg); 376 var posLoc = gl.getAttribLocation(prg, "a_position"); 377 wtu.setupUnitQuad(gl, posLoc); 378 wtu.clearAndDrawUnitQuad(gl); 379 wtu.checkCanvas(gl, [255, 0, 0, 255], "should be red"); 380 gl.shaderSource(fs, wtu.getScript("fshader-green")); 381 gl.compileShader(fs); 382 gl.linkProgram(prg); 383 // Program should be new program at this point without calling useProgram 384 wtu.clearAndDrawUnitQuad(gl); 385 wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); 386 var prg = wtu.setupProgram(gl, ["vshader", "fshader-settable"], ["a_position"]); 387 var colorLoc = gl.getUniformLocation(prg, "u_color"); 388 gl.uniform4f(colorLoc, 1, 0, 0, 1); 389 wtu.clearAndDrawUnitQuad(gl); 390 wtu.checkCanvas(gl, [255, 0, 0, 255], "should be red"); 391 gl.linkProgram(prg); 392 // Program's uniforms should be cleared at this point without calling useProgram 393 wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); 394 wtu.checkCanvas(gl, [0, 0, 0, 0], "should be tranparent black"); 395 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 396 } 397 398 debug(""); 399 go(); 400 401 var successfullyParsed = true; 402 </script> 403 <script src="../../js/js-test-post.js"></script> 404 405 </body> 406 </html>