1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
content / test / data / gpu / worker-webgl-raf-after-gpu-crash.html [blame]
<!DOCTYPE HTML>
<html>
<head>
<title>Worker requestAnimationFrame with WebGLcontext after GPU crash</title>
<script id="worker" type="text/worker">
self.onmessage = msg => {
const canvas = msg.data.canvas;
const gl = canvas.getContext('webgl');
let restored = false;
canvas.addEventListener('webglcontextlost', evt => {
self.postMessage("context lost");
// To allow restore event to be fired:
evt.preventDefault();
});
canvas.addEventListener('webglcontextrestored', evt => {
restored = true;
self.postMessage("context restored");
});
self.postMessage("setup complete");
function animate() {
if (restored) {
gl.clearColor(0, 1, 0, 1);
} else {
gl.clearColor(1, 0, 0, 1)
}
gl.clear(gl.COLOR_BUFFER_BIT);
self.postMessage("animation frame");
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
}
</script>
<script>
// Test that a WebGL rAF loop keeps running after recovering from a context loss.
// Test will timeout if animation fails to resume.
function UnexpectedMessage(state, msg) {
console.log(`Unexpected message '${msg}' received while in state '${state}'`);
window.domAutomationController.send('FAILED');
}
window.onload = () => {
const worker_blob = new Blob([document.getElementById("worker").textContent]);
const worker = new Worker(URL.createObjectURL(worker_blob));
let state = "init";
let frames_after_restore = 0;
worker.onmessage = evt => {
// Expected sequence (state: expected message):
//
// "init": expect to receive "setup complete" once
// "ready": expect to receive "animation frame" x1 -> request gpu crash
// "expecting crash": receive "animation frame" (any count), then "context lost" once
// "expecting restore": receive "animation frame" (any count), then "context restored" once
// "recover": receive 5+ "animation frame".
switch(state) {
case "init":
if (evt.data == "setup complete") {
state = "ready";
} else {
UnexpectedMessage(state, evt.data);
}
break;
case "ready":
if (evt.data == "animation frame") {
state = "expecting crash";
// send message that test is ready for GPU process crash
window.domAutomationController.send('LOADED');
} else {
UnexpectedMessage(state, evt.data);
}
break;
case "expecting crash":
if (evt.data == "animation frame") {
break;
}
if (evt.data == "context lost") {
state = "expecting restore";
} else {
UnexpectedMessage(state, evt.data);
}
break;
case "expecting restore":
if (evt.data == "animation frame") {
break;
}
if (evt.data == "context restored") {
state = "recover";
} else {
UnexpectedMessage(state, evt.data);
}
break;
case "recover":
if (evt.data == "animation frame") {
// Wait for enough frames to pass to ensure the pipeline
// is not stalled.
if (++frames_after_restore == 5)
window.domAutomationController.send('SUCCESS');
} else {
UnexpectedMessage(state, evt.data);
}
break;
}
}
let offscreen = document.getElementById('c').transferControlToOffscreen();
worker.postMessage({
canvas: offscreen
}, [offscreen]);
}
</script>
</head>
<body>
<canvas id="c" width="300" height="300" class="nomargin" style="position:absolute; top:0px; left:0px;"></canvas>
</body>
</html>