| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <link rel="stylesheet" href="resources/webgl_test_files/resources/js-test-style.css"/> |
| <script src="resources/webgl_test_files/js/js-test-pre.js"></script> |
| <script src="resources/webgl_test_files/js/webgl-test-utils.js"></script> |
| </head> |
| <body onload="test()"> |
| <div id="description"></div> |
| <div id="console"></div> |
| <script> |
| "use strict"; |
| description("Test that first losing context, trying to restore it, and then doing something to really lose it does not crash."); |
| |
| // The test would crash with following sequence: |
| // 1. Cause a real context lost. In this test, "gpuStatusFailure". |
| // 2. Page would try to restore the context. This would start the restore timer. |
| // 3. Before the restore timer fires, really lose the context. In this test, "manyContext". |
| // 4. The restore timer would fire, and restore function would use nullptr context. |
| |
| var wtu = WebGLTestUtils; |
| var gl; |
| |
| async function waitForEvent(element, eventName, timeoutMS) |
| { |
| timeoutMS = timeoutMS || 2000; |
| return new Promise((resolve, reject) => { |
| function timeoutHandler() { |
| element.removeEventListener(eventName, handler, { once: true }); |
| reject(); |
| } |
| const rejectID = setTimeout(timeoutHandler, timeoutMS); |
| function handler(event) { |
| clearTimeout(rejectID); |
| resolve(event); |
| } |
| element.addEventListener(eventName, handler, { once: true }); |
| }); |
| } |
| |
| async function waitForWebGLContextRestored(canvas, timeoutMS) |
| { |
| await waitForEvent(canvas, "webglcontextrestored", timeoutMS); |
| } |
| |
| async function waitForWebGLContextLostAndRestore(canvas, timeoutMS) |
| { |
| let event = await waitForEvent(canvas, "webglcontextlost", timeoutMS); |
| event.preventDefault(); |
| } |
| |
| function testDescription(subcase) { |
| return Object.keys(subcase).map((k) => `${k}: ${typeof subcase[k] === "function" ? subcase[k].name : subcase[k]}`).join(", "); |
| } |
| |
| async function runTest(subcase) |
| { |
| debug(`Running test: ${testDescription(subcase)}`); |
| |
| const canvas = document.createElement("canvas"); |
| canvas.width = 1; |
| canvas.height = 1; |
| gl = wtu.create3DContext(canvas); |
| const WEBGL_lose_context = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_lose_context"); |
| |
| const webglcontextlostandrestore = waitForWebGLContextLostAndRestore(canvas); |
| const webglcontextrestored = waitForWebGLContextRestored(canvas); |
| let expectRestoreIgnored = subcase.loseMethod(gl, WEBGL_lose_context); |
| |
| try { |
| await webglcontextlostandrestore; |
| testPassed("Got webglcontextlost and restore was attempted."); |
| wtu.glErrorShouldBe(gl, gl.CONTEXT_LOST_WEBGL); |
| } catch (e) { |
| if (e) |
| throw e; |
| testFailed("Timed out waiting webglcontextlost that would attempt to be restored."); |
| } |
| expectRestoreIgnored = subcase.loseMethod2(gl, WEBGL_lose_context) || expectRestoreIgnored; |
| |
| try { |
| await webglcontextrestored; |
| if (expectRestoreIgnored) |
| testFailed("Expected restore be ignored, but it was not."); |
| else |
| shouldBeFalse("gl.isContextLost()"); |
| } catch (e) { |
| if (e) |
| throw e; |
| if (!expectRestoreIgnored) |
| testFailed("Did not expect restore be ignored, but it was."); |
| else |
| shouldBeTrue("gl.isContextLost()"); |
| } |
| |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| } |
| |
| function loseContext(gl, WEBGL_lose_context) |
| { |
| if (!WEBGL_lose_context) { |
| testFailed("Could not find WEBGL_lose_context extension"); |
| return; |
| } |
| let wasLost = gl.isContextLost(); |
| WEBGL_lose_context.loseContext(); |
| if (wasLost) |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); |
| return true; // Request for restore is ignored. |
| } |
| |
| function manyContexts() |
| { |
| // This causes the older contexts to be lost, including the first one we created |
| // for testing. |
| const contexts = [] |
| for (let i = 0; i < 50; ++i) |
| contexts.push(document.createElement("canvas").getContext("webgl")); |
| return true; // Request for restore is ignored. |
| } |
| |
| function gpuStatusFailure(gl) |
| { |
| internals.simulateEventForWebGLContext("GPUStatusFailure", gl); |
| gl.clear(gl.COLOR_BUFFER_BIT); |
| return true; // Request for restore is honored. |
| } |
| |
| function nothing() |
| { |
| return false; |
| } |
| |
| const loseMethods = [loseContext, manyContexts]; |
| if (window.internals) |
| loseMethods.push(gpuStatusFailure); |
| |
| const subcases = []; |
| for (const loseMethod of loseMethods) |
| for (const loseMethod2 of loseMethods.concat(nothing)) |
| subcases.push({loseMethod, loseMethod2}); |
| |
| async function test() |
| { |
| for (let subcase of subcases) |
| await runTest(subcase); |
| finishTest(); |
| } |
| </script> |
| </body> |
| </html> |