blob: b3a2126ecaddca4afcbcca16bb5e7d425345b732 [file] [log] [blame]
<!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>