context-lost.html (15855B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <!-- 6 Copyright (c) 2019 The Khronos Group Inc. 7 Use of this source code is governed by an MIT-style license that can be 8 found in the LICENSE.txt file. 9 --> 10 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 11 <script src="../../js/js-test-pre.js"></script> 12 <script src="../../js/webgl-test-utils.js"></script> 13 <script> 14 "use strict"; 15 var wtu; 16 var canvas; 17 var gl; 18 var shouldGenerateGLError; 19 var extensionName; 20 var extension; 21 22 var buffer; 23 var framebuffer; 24 var program; 25 var renderbuffer; 26 var shader; 27 var texture; 28 var uniformLocation; 29 var arrayBuffer; 30 var arrayBufferView 31 var image; 32 var video; 33 var canvas2d; 34 var ctx2d; 35 var imageData; 36 var float32array; 37 var int32array; 38 var OES_vertex_array_object; 39 var vertexArrayObject; 40 41 var secondCanvas; 42 var secondGL; 43 44 function init() 45 { 46 wtu = WebGLTestUtils; 47 canvas = document.getElementById("canvas"); 48 gl = wtu.create3DContext(canvas); 49 secondCanvas = document.getElementById("canvas2"); 50 secondGL = wtu.create3DContext(secondCanvas); 51 shouldGenerateGLError = wtu.shouldGenerateGLError; 52 53 description("Tests behavior under a lost context"); 54 55 // call testValidContext() before checking for the extension, because this is where we check 56 // for the isContextLost() method, which we want to do regardless of the extension's presence. 57 testValidContext(); 58 59 extensionName = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_lose_context"); 60 if (!extensionName) { 61 debug("Could not find WEBGL_lose_context extension"); 62 finishTest(); 63 return false; 64 } 65 extension = gl.getExtension(extensionName); 66 67 // need an extension that exposes new API methods. 68 OES_vertex_array_object = wtu.getExtensionWithKnownPrefixes(gl, "OES_vertex_array_object"); 69 70 canvas.addEventListener("webglcontextlost", testLostContext, false); 71 72 // We need to initialize |uniformLocation| before losing context. 73 // Otherwise gl.getUniform() when context is lost will throw. 74 uniformLocation = gl.getUniformLocation(program, "tex"); 75 loseContext(); 76 } 77 78 function loseContext() 79 { 80 debug(""); 81 debug("Lose context"); 82 83 // Note: this will cause the context to be lost, but the 84 // webglcontextlost event listener to be queued. 85 extension.loseContext(); 86 debug(""); 87 } 88 89 function testValidContext() 90 { 91 debug("Test valid context"); 92 93 shouldBeFalse("gl.isContextLost()"); 94 95 arrayBuffer = new ArrayBuffer(4); 96 arrayBufferView = new Int8Array(arrayBuffer); 97 98 // Generate resources for testing. 99 buffer = gl.createBuffer(); 100 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 101 framebuffer = gl.createFramebuffer(); 102 gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); 103 program = wtu.setupSimpleTextureProgram(gl); 104 renderbuffer = gl.createRenderbuffer(); 105 gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); 106 shader = gl.createShader(gl.VERTEX_SHADER); 107 texture = gl.createTexture(); 108 gl.bindTexture(gl.TEXTURE_2D, texture); 109 shouldBe("gl.getError()", "gl.NO_ERROR"); 110 111 // Test is queries that will later be false 112 shouldGenerateGLError(gl, gl.NO_ERROR, "gl.enable(gl.BLEND)"); 113 shouldBeTrue("gl.isBuffer(buffer)"); 114 shouldBeTrue("gl.isEnabled(gl.BLEND)"); 115 shouldBeTrue("gl.isFramebuffer(framebuffer)"); 116 shouldBeTrue("gl.isProgram(program)"); 117 shouldBeTrue("gl.isRenderbuffer(renderbuffer)"); 118 shouldBeTrue("gl.isShader(shader)"); 119 shouldBeTrue("gl.isTexture(texture)"); 120 121 if (OES_vertex_array_object) { 122 vertexArrayObject = OES_vertex_array_object.createVertexArrayOES(); 123 shouldBe("gl.getError()", "gl.NO_ERROR"); 124 shouldBeTrue("OES_vertex_array_object.isVertexArrayOES(vertexArrayObject)"); 125 } 126 } 127 128 function testGLNOErrorFunctions(tests) { 129 tests.forEach(function(test) { 130 shouldGenerateGLError(gl, gl.NO_ERROR, test); 131 }); 132 } 133 134 function testFunctionsThatReturnNULL(tests) { 135 tests.forEach(function(test) { 136 shouldBeNull(test); 137 }); 138 } 139 140 function testUploadingLostContextToTexture() { 141 debug("Testing uploading a canvas with a lost WebGL context to a texture"); 142 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors at beginning"); 143 let texture = secondGL.createTexture(); 144 secondGL.bindTexture(secondGL.TEXTURE_2D, texture); 145 secondGL.texImage2D(secondGL.TEXTURE_2D, 0, secondGL.RGBA, secondGL.RGBA, secondGL.UNSIGNED_BYTE, canvas); 146 wtu.glErrorShouldBe(secondGL, [secondGL.INVALID_OPERATION, secondGL.NO_ERROR], "Should not crash when uploading canvas with lost WebGL context to a texture"); 147 } 148 149 function testLostContext() 150 { 151 debug("Test lost context"); 152 153 // Functions with special return values. 154 shouldBeTrue("gl.isContextLost()"); 155 shouldBe("gl.getError()", "gl.CONTEXT_LOST_WEBGL"); 156 shouldBe("gl.getError()", "gl.NO_ERROR"); 157 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_UNSUPPORTED"); 158 shouldBe("gl.getAttribLocation(program, 'u_modelViewProjMatrix')", "-1"); 159 shouldBe("gl.getVertexAttribOffset(0, gl.VERTEX_ATTRIB_ARRAY_POINTER)", "0"); 160 161 // Attempt resize of lost context should succeed, still lost. 162 gl.canvas.width = gl.canvas.width; // Try to hit any same-size resize path. 163 shouldBeTrue("gl.isContextLost()"); 164 165 const newSize = gl.canvas.width + 1; 166 gl.canvas.width = newSize; 167 shouldBe("gl.canvas.width", newSize); 168 shouldBeTrue("gl.isContextLost()"); 169 170 // Test the extension itself. 171 shouldGenerateGLError(gl, gl.INVALID_OPERATION, "extension.loseContext()"); 172 173 image = document.createElement("img"); 174 video = document.createElement("video"); 175 canvas2d = document.createElement("canvas"); 176 ctx2d = canvas2d.getContext("2d"); 177 imageData = ctx2d.createImageData(1, 1); 178 float32array = new Float32Array(1); 179 int32array = new Int32Array(1); 180 181 // Functions returning void should return immediately. 182 // This is untestable, but we can at least be sure they cause no errors 183 // and the codepaths are exercised. 184 var voidTests = [ 185 "gl.activeTexture(gl.TEXTURE0)", 186 "gl.attachShader(program, shader)", 187 "gl.bindBuffer(gl.ARRAY_BUFFER, buffer)", 188 "gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)", 189 "gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer)", 190 "gl.bindTexture(gl.TEXTURE_2D, texture)", 191 "gl.blendColor(1.0, 1.0, 1.0, 1.0)", 192 "gl.blendEquation(gl.FUNC_ADD)", 193 "gl.blendEquationSeparate(gl.FUNC_ADD, gl.FUNC_ADD)", 194 "gl.blendFunc(gl.ONE, gl.ONE)", 195 "gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.ONE)", 196 "gl.bufferData(gl.ARRAY_BUFFER, 0, gl.STATIC_DRAW)", 197 "gl.bufferData(gl.ARRAY_BUFFER, arrayBufferView, gl.STATIC_DRAW)", 198 "gl.bufferData(gl.ARRAY_BUFFER, arrayBuffer, gl.STATIC_DRAW)", 199 "gl.bufferSubData(gl.ARRAY_BUFFRE, 0, arrayBufferView)", 200 "gl.bufferSubData(gl.ARRAY_BUFFRE, 0, arrayBuffer)", 201 "gl.clear(gl.COLOR_BUFFER_BIT)", 202 "gl.clearColor(1, 1, 1, 1)", 203 "gl.clearDepth(1)", 204 "gl.clearStencil(0)", 205 "gl.colorMask(1, 1, 1, 1)", 206 "gl.compileShader(shader)", 207 "gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 0, 0, 0)", 208 "gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 0, 0)", 209 "gl.cullFace(gl.FRONT)", 210 "gl.deleteBuffer(buffer)", 211 "gl.deleteFramebuffer(framebuffer)", 212 "gl.deleteProgram(program)", 213 "gl.deleteRenderbuffer(renderbuffer)", 214 "gl.deleteShader(shader)", 215 "gl.deleteTexture(texture)", 216 "gl.depthFunc(gl.NEVER)", 217 "gl.depthMask(0)", 218 "gl.depthRange(0, 1)", 219 "gl.detachShader(program, shader)", 220 "gl.disable(gl.BLEND)", 221 "gl.disableVertexAttribArray(0)", 222 "gl.drawArrays(gl.POINTS, 0, 0)", 223 "gl.drawElements(gl.POINTS, 0, gl.UNSIGNED_SHORT, 0)", 224 "gl.enable(gl.BLEND)", 225 "gl.enableVertexAttribArray(0)", 226 "gl.finish()", 227 "gl.flush()", 228 "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer)", 229 "gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)", 230 "gl.frontFace(gl.CW)", 231 "gl.generateMipmap(gl.TEXTURE_2D)", 232 "gl.hint(gl.GENERATE_MIPMAP_HINT, gl.FASTEST)", 233 "gl.lineWidth(0)", 234 "gl.linkProgram(program)", 235 "gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0)", 236 "gl.polygonOffset(0, 0)", 237 "gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)", 238 "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0)", 239 "gl.sampleCoverage(0, 0)", 240 "gl.scissor(0, 0, 0, 0)", 241 "gl.shaderSource(shader, '')", 242 "gl.stencilFunc(gl.NEVER, 0, 0)", 243 "gl.stencilFuncSeparate(gl.FRONT, gl.NEVER, 0, 0)", 244 "gl.stencilMask(0)", 245 "gl.stencilMaskSeparate(gl.FRONT, 0)", 246 "gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP)", 247 "gl.stencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.KEEP)", 248 "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)", 249 "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imageData)", 250 "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)", 251 "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d)", 252 "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, video)", 253 "gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)", 254 "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)", 255 "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, arrayBufferView)", 256 "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, imageData)", 257 "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, image)", 258 "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, canvas2d)", 259 "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, video)", 260 "gl.uniform1f(uniformLocation, 0)", 261 "gl.uniform1fv(uniformLocation, float32array)", 262 "gl.uniform1fv(uniformLocation, [0])", 263 "gl.uniform1i(uniformLocation, 0)", 264 "gl.uniform1iv(uniformLocation, int32array)", 265 "gl.uniform1iv(uniformLocation, [0])", 266 "gl.uniform2f(uniformLocation, 0, 0)", 267 "gl.uniform2fv(uniformLocation, float32array)", 268 "gl.uniform2fv(uniformLocation, [0, 0])", 269 "gl.uniform2i(uniformLocation, 0, 0)", 270 "gl.uniform2iv(uniformLocation, int32array)", 271 "gl.uniform2iv(uniformLocation, [0, 0])", 272 "gl.uniform3f(uniformLocation, 0, 0, 0)", 273 "gl.uniform3fv(uniformLocation, float32array)", 274 "gl.uniform3fv(uniformLocation, [0, 0, 0])", 275 "gl.uniform3i(uniformLocation, 0, 0, 0)", 276 "gl.uniform3iv(uniformLocation, int32array)", 277 "gl.uniform3iv(uniformLocation, [0, 0, 0])", 278 "gl.uniform4f(uniformLocation, 0, 0, 0, 0)", 279 "gl.uniform4fv(uniformLocation, float32array)", 280 "gl.uniform4fv(uniformLocation, [0, 0, 0, 0])", 281 "gl.uniform4i(uniformLocation, 0, 0, 0, 0)", 282 "gl.uniform4iv(uniformLocation, int32array)", 283 "gl.uniform4iv(uniformLocation, [0, 0, 0, 0])", 284 "gl.uniformMatrix2fv(uniformLocation, false, float32array)", 285 "gl.uniformMatrix2fv(uniformLocation, false, [0, 0, 0, 0])", 286 "gl.uniformMatrix3fv(uniformLocation, false, float32array)", 287 "gl.uniformMatrix3fv(uniformLocation, false, [0, 0, 0, 0, 0, 0, 0, 0, 0])", 288 "gl.uniformMatrix4fv(uniformLocation, false, float32array)", 289 "gl.uniformMatrix4fv(uniformLocation, false, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])", 290 "gl.useProgram(program)", 291 "gl.validateProgram(program)", 292 "gl.vertexAttrib1f(0, 0)", 293 "gl.vertexAttrib1fv(0, float32array)", 294 "gl.vertexAttrib1fv(0, [0])", 295 "gl.vertexAttrib2f(0, 0, 0)", 296 "gl.vertexAttrib2fv(0, float32array)", 297 "gl.vertexAttrib2fv(0, [0, 0])", 298 "gl.vertexAttrib3f(0, 0, 0, 0)", 299 "gl.vertexAttrib3fv(0, float32array)", 300 "gl.vertexAttrib3fv(0, [0, 0, 0])", 301 "gl.vertexAttrib4f(0, 0, 0, 0, 0)", 302 "gl.vertexAttrib4fv(0, float32array)", 303 "gl.vertexAttrib4fv(0, [0, 0, 0, 0])", 304 "gl.vertexAttribPointer(0, 0, gl.FLOAT, false, 0, 0)", 305 "gl.viewport(0, 0, 0, 0)", 306 ]; 307 testGLNOErrorFunctions(voidTests); 308 309 // Functions return nullable values should all return null. 310 var nullTests = [ 311 "gl.getActiveAttrib(program, 0)", 312 "gl.getActiveUniform(program, 0)", 313 "gl.getAttachedShaders(program)", 314 "gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE)", 315 "gl.getContextAttributes()", 316 "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", 317 "gl.getParameter(gl.CURRENT_PROGRAM)", 318 "gl.getProgramInfoLog(program)", 319 "gl.getProgramParameter(program, gl.LINK_STATUS)", 320 "gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)", 321 "gl.getShaderInfoLog(shader)", 322 "gl.getShaderParameter(shader, gl.SHADER_TYPE)", 323 "gl.getShaderSource(shader)", 324 "gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)", 325 "gl.getUniform(program, uniformLocation)", 326 "gl.getUniformLocation(program, 'vPosition')", 327 "gl.getVertexAttrib(0, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)", 328 "gl.getSupportedExtensions()", 329 "gl.getExtension('" + extensionName + "')", 330 ]; 331 testFunctionsThatReturnNULL(nullTests); 332 333 [ 334 [() => gl.createBuffer(), x => gl.bindBuffer(gl.ARRAY_BUFFER, x), x => gl.isBuffer(x)], 335 [() => gl.createFramebuffer(), x => gl.bindFramebuffer(gl.FRAMEBUFFER, x), x => gl.isFramebuffer(x)], 336 [() => gl.createProgram(), x => undefined, x => gl.isProgram(x)], 337 [() => gl.createRenderbuffer(), x => gl.bindRenderbuffer(gl.RENDERBUFFER, x), x => gl.isRenderbuffer(x)], 338 [() => gl.createShader(gl.VERTEX_SHADER), x => undefined, x => gl.isShader(x)], 339 [() => gl.createTexture(), x => gl.bindTexture(gl.TEXTURE_2D, x), x => gl.isTexture(x)], 340 ].forEach(([fn_create, fn_bind, fn_is]) => { 341 const x = fn_create(); 342 assertMsg(x, `${fn_create.toString()} -> non-null`); 343 assertMsg(fn_is(x) === false, `[before bind] ${fn_is.toString()} -> false`); 344 fn_bind(x); 345 fn_bind(null); 346 assertMsg(fn_is(x) === false, `[after bind] ${fn_is.toString()} -> false`); 347 }); 348 349 // "Is" queries should all return false. 350 shouldBeFalse("gl.isEnabled(gl.BLEND)"); 351 352 shouldBe("gl.getError()", "gl.NO_ERROR"); 353 354 // test extensions 355 if (OES_vertex_array_object) { 356 testGLNOErrorFunctions( 357 [ 358 "OES_vertex_array_object.bindVertexArrayOES(vertexArrayObject)", 359 "OES_vertex_array_object.isVertexArrayOES(vertexArrayObject)", 360 "OES_vertex_array_object.deleteVertexArrayOES(vertexArrayObject)", 361 ]); 362 363 [ 364 [() => OES_vertex_array_object.createVertexArrayOES(), x => OES_vertex_array_object.isVertexArrayOES(x), x => OES_vertex_array_object.isVertexArrayOES(x)], 365 ].forEach(([fn_create, fn_bind, fn_is]) => { 366 const x = fn_create(); 367 assertMsg(x, `${fn_create.toString()} -> non-null`); 368 assertMsg(fn_is(x) === false, `[before bind] ${fn_is.toString()} -> false`); 369 fn_bind(x); 370 fn_bind(null); 371 assertMsg(fn_is(x) === false, `[after bind] ${fn_is.toString()} -> false`); 372 }); 373 } 374 375 testUploadingLostContextToTexture(); 376 377 debug(""); 378 379 finishTest(); 380 } 381 382 </script> 383 </head> 384 <body onload="init()"> 385 <div id="description"></div> 386 <div id="console"></div> 387 <canvas id="canvas"> 388 <canvas id="canvas2"> 389 </body> 390 </html>