context-lost-restored.html (10112B)
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 = WebGLTestUtils; 16 var canvas; 17 var gl; 18 var shouldGenerateGLError; 19 var WEBGL_lose_context; 20 var new_WEBGL_lose_context; 21 var bufferObjects; 22 var program; 23 var texture; 24 var texColor = [255, 10, 20, 255]; 25 var allowRestore; 26 var contextLostEventFired; 27 var contextRestoredEventFired; 28 var OES_vertex_array_object; 29 var old_OES_vertex_array_object; 30 var vertexArrayObject; 31 var OES_texture_float; 32 var newExtension; 33 34 function init() 35 { 36 enableJSTestPreVerboseLogging(); 37 description("Tests behavior under a restored context."); 38 39 shouldGenerateGLError = wtu.shouldGenerateGLError; 40 testLosingContext(); 41 } 42 43 function setupTest() 44 { 45 canvas = document.createElement("canvas"); 46 canvas.width = 1; 47 canvas.height = 1; 48 gl = wtu.create3DContext(canvas); 49 WEBGL_lose_context = getExtensionAndAddProperty(gl, "WEBGL_lose_context"); 50 if (!WEBGL_lose_context) { 51 debug("Could not find WEBGL_lose_context extension"); 52 return false; 53 } 54 55 // Try to get a few extensions 56 OES_vertex_array_object = getExtensionAndAddProperty(gl, "OES_vertex_array_object"); 57 OES_texture_float = getExtensionAndAddProperty(gl, "OES_texture_float"); 58 59 return true; 60 } 61 62 function getExtensionAndAddProperty(gl, name) { 63 var ext = wtu.getExtensionWithKnownPrefixes(gl, name); 64 if (ext) { 65 ext.webglTestProperty = true; 66 } 67 return ext; 68 } 69 70 function reGetExtensionAndTestForProperty(gl, name, expectProperty) { 71 newExtension = wtu.getExtensionWithKnownPrefixes(gl, name); 72 // NOTE: while getting a extension after context lost/restored is allowed to fail 73 // for the purpose the conformance tests it is not. 74 // 75 // Hypothetically the user can switch GPUs live. For example on Windows, install 2 GPUs, 76 // then in the control panen enable 1, disable the others and visa versa. Since the GPUs 77 // have different capabilities one or the other may not support a particlar extension. 78 // 79 // But, for the purpose of the conformance tests the context is expected to restore 80 // on the same GPU and therefore the extensions that succeeded previously should 81 // succeed on restore. 82 shouldBeTrue("newExtension != null"); 83 if (expectProperty) { 84 shouldBeTrue("newExtension.webglTestProperty === true"); 85 } else { 86 shouldBeTrue("newExtension.webglTestProperty === undefined"); 87 } 88 return newExtension; 89 } 90 91 function testLosingContext() 92 { 93 if (!setupTest()) { 94 finishTest(); 95 return; 96 } 97 98 debug("Test losing a context and inability to restore it."); 99 100 canvas.addEventListener("webglcontextlost", function(e) { 101 testLostContext(e); 102 // restore the context after this event has exited. 103 setTimeout(function() { 104 // we didn't call prevent default so we should not be able to restore the context 105 shouldGenerateGLError(gl, gl.INVALID_OPERATION, "WEBGL_lose_context.restoreContext()"); 106 testLosingAndRestoringContext(); 107 }, 0); 108 }); 109 canvas.addEventListener("webglcontextrestored", testShouldNotRestoreContext); 110 allowRestore = false; 111 contextLostEventFired = false; 112 contextRestoredEventFired = false; 113 114 testOriginalContext(); 115 WEBGL_lose_context.loseContext(); 116 // The context should be lost immediately. 117 shouldBeTrue("gl.isContextLost()"); 118 shouldBe("gl.getError()", "gl.CONTEXT_LOST_WEBGL"); 119 shouldBe("gl.getError()", "gl.NO_ERROR"); 120 // gl methods should be no-ops 121 shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)"); 122 // but the event should not have been fired. 123 shouldBeFalse("contextLostEventFired"); 124 } 125 126 function testLosingAndRestoringContext() 127 { 128 if (!setupTest()) 129 finishTest(); 130 131 debug(""); 132 debug("Test losing and restoring a context."); 133 134 canvas.addEventListener("webglcontextlost", function(e) { 135 testLostContext(e); 136 // restore the context after this event has exited. 137 setTimeout(function() { 138 shouldGenerateGLError(gl, gl.NO_ERROR, "WEBGL_lose_context.restoreContext()"); 139 // Calling restoreContext() twice should not cause error or crash 140 shouldGenerateGLError(gl, gl.NO_ERROR, "WEBGL_lose_context.restoreContext()"); 141 // The context should still be lost. It will not get restored until the 142 // webglrestorecontext event is fired. 143 shouldBeTrue("gl.isContextLost()"); 144 shouldBe("gl.getError()", "gl.NO_ERROR"); 145 // gl methods should still be no-ops 146 shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)"); 147 }, 0); 148 }); 149 canvas.addEventListener("webglcontextrestored", function() { 150 testRestoredContext(); 151 finishTest(); 152 }); 153 allowRestore = true; 154 contextLostEventFired = false; 155 contextRestoredEventFired = false; 156 157 testOriginalContext(); 158 WEBGL_lose_context.loseContext(); 159 // The context should be lost immediately. 160 shouldBeTrue("gl.isContextLost()"); 161 shouldBe("gl.getError()", "gl.CONTEXT_LOST_WEBGL"); 162 shouldBe("gl.getError()", "gl.NO_ERROR"); 163 // gl methods should be no-ops 164 shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendFunc(gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP)"); 165 // but the event should not have been fired. 166 shouldBeFalse("contextLostEventFired"); 167 } 168 169 function testRendering() 170 { 171 gl.clearColor(0, 0, 0, 255); 172 gl.colorMask(1, 1, 1, 0); 173 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 174 175 program = wtu.setupSimpleTextureProgram(gl); 176 bufferObjects = wtu.setupUnitQuad(gl); 177 texture = wtu.createColoredTexture(gl, canvas.width, canvas.height, texColor); 178 179 gl.uniform1i(gl.getUniformLocation(program, "tex"), 0); 180 wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); 181 182 var compare = texColor.slice(0, 3); 183 wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, compare, "shouldBe " + compare); 184 185 shouldBe("gl.getError()", "gl.NO_ERROR"); 186 } 187 188 function testOriginalContext() 189 { 190 debug("Test valid context"); 191 shouldBeFalse("gl.isContextLost()"); 192 shouldBe("gl.getError()", "gl.NO_ERROR"); 193 testRendering(); 194 debug(""); 195 } 196 197 function testLostContext(e) 198 { 199 debug("Test lost context"); 200 shouldBeFalse("contextLostEventFired"); 201 contextLostEventFired = true; 202 shouldBeTrue("gl.isContextLost()"); 203 shouldBe("gl.getError()", "gl.NO_ERROR"); 204 debug(""); 205 if (allowRestore) 206 e.preventDefault(); 207 } 208 209 function testShouldNotRestoreContext(e) 210 { 211 testFailed("Should not restore the context unless preventDefault is called on the context lost event"); 212 debug(""); 213 } 214 215 function testResources(expected) 216 { 217 var tests = [ 218 "gl.bindTexture(gl.TEXTURE_2D, texture)", 219 "gl.useProgram(program)", 220 "gl.bindBuffer(gl.ARRAY_BUFFER, bufferObjects[0])", 221 ]; 222 223 for (var i = 0; i < tests.length; ++i) 224 shouldGenerateGLError(gl, expected, tests[i]); 225 } 226 227 function testOESTextureFloat() { 228 if (OES_texture_float) { 229 // Extension must still be lost. 230 var tex = gl.createTexture(); 231 gl.bindTexture(gl.TEXTURE_2D, tex); 232 shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.FLOAT, null)"); 233 // Try re-enabling extension 234 OES_texture_float = reGetExtensionAndTestForProperty(gl, "OES_texture_float", false); 235 shouldGenerateGLError(gl, gl.NO_ERROR, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.FLOAT, null)"); 236 } 237 } 238 239 function testOESVertexArrayObject() { 240 if (OES_vertex_array_object) { 241 // Extension must still be lost. 242 let vao = OES_vertex_array_object.createVertexArrayOES(); 243 assertMsg(vao, "[with extension lost] createVertexArrayOES() -> non-null"); 244 assertMsg(!OES_vertex_array_object.isVertexArrayOES(vao), "[with context lost] isVertexArrayOES(createVertexArrayOES()) -> false"); 245 // Try re-enabling extension 246 247 old_OES_vertex_array_object = OES_vertex_array_object; 248 OES_vertex_array_object = reGetExtensionAndTestForProperty(gl, "OES_vertex_array_object", false); 249 250 vao = OES_vertex_array_object.createVertexArrayOES(); 251 assertMsg(vao, "[with new non-lost extension] createVertexArrayOES() -> non-null"); 252 assertMsg(!OES_vertex_array_object.isVertexArrayOES(vao), "[with new non-lost extension, before bindVAO] isVertexArrayOES(createVertexArrayOES()) -> false"); 253 OES_vertex_array_object.bindVertexArrayOES(vao); 254 OES_vertex_array_object.bindVertexArrayOES(null); 255 assertMsg(OES_vertex_array_object.isVertexArrayOES(vao), "[with new non-lost extension, after bindVAO] isVertexArrayOES(createVertexArrayOES()) -> true"); 256 257 vao = old_OES_vertex_array_object.createVertexArrayOES(); 258 assertMsg(vao, "[with old lost extension] createVertexArrayOES() -> non-null"); 259 assertMsg(!old_OES_vertex_array_object.isVertexArrayOES(vao), "[with old lost extension] isVertexArrayOES(createVertexArrayOES()) -> false"); 260 } 261 } 262 263 function testExtensions() { 264 testOESTextureFloat(); 265 testOESVertexArrayObject(); 266 // Only the WEBGL_lose_context extension should be the same object after context lost. 267 new_WEBGL_lose_context = reGetExtensionAndTestForProperty(gl, "WEBGL_lose_context", true); 268 } 269 270 function testRestoredContext() 271 { 272 debug("Test restored context"); 273 shouldBeFalse("contextRestoredEventFired"); 274 contextRestoredEventFired = true; 275 shouldBeFalse("gl.isContextLost()"); 276 shouldBe("gl.getError()", "gl.NO_ERROR"); 277 278 // Validate that using old resources fails. 279 testResources(gl.INVALID_OPERATION); 280 281 testRendering(); 282 283 // Validate new resources created in testRendering(). 284 testResources(gl.NO_ERROR); 285 286 testExtensions(); 287 288 debug(""); 289 } 290 291 292 </script> 293 </head> 294 <body onload="init()"> 295 <div id="description"></div> 296 <div id="console"></div> 297 </body> 298 </html>