<html>
<head>
<script type='module'>
let lostJustHappened = false;
const initTextureColor = [1.0, 0.0, 0.0];
const reinitTextureColor = [0.0, 1.0, 0.0];
const reinitTextureColorArray = new Uint8Array([0x00, 0xff, 0x00, 0xff]);
function contextLostTest(kind) {
switch (kind) {
case 'kill_after_notification':
// The browser test waits for notification from the page that it
// has been loaded before navigating to about:gpucrash.
window.domAutomationController.send('LOADED');
break;
}
}
async function init(canvas, color) {
let adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
console.warn('Failed to get WebGPU adapter');
}
let device = await adapter.requestDevice();
device.lost.then(async value => {
// After device lost, re-initialize for new adapter and device.
// We choose a different texture color from when we first
// init to be sure that the texture is coming from here
device = null;
adapter = null;
lostJustHappened = true;
await init(canvas, reinitTextureColor);
});
const context = canvas.getContext('webgpu');
if (!context)
return;
const swapChainFormat = 'bgra8unorm';
context.configure({
device,
format: swapChainFormat,
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT
});
async function checkTextureColor(
src,
format,
{ x, y },
exp
) {
const byteLength = 4;
const bytesPerRow = 256;
const rowsPerImage = 1;
const mipSize = [1, 1, 1];
const buffer = device.createBuffer({
size: byteLength,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
});
const commandEncoder = device.createCommandEncoder();
commandEncoder.copyTextureToBuffer(
{ texture: src, mipLevel: 0, arrayLayer: 0, origin: { x, y, z: 0 } },
{ buffer, bytesPerRow, rowsPerImage },
mipSize
);
device.queue.submit([commandEncoder.finish()]);
await buffer.mapAsync(GPUMapMode.READ);
const actual = new Uint8Array(buffer.getMappedRange());
// check that the actual and expected buffers are the same
if (actual.byteLength !== exp.byteLength) {
window.domAutomationController.send('FAILED');
}
for (var i = 0; i !== actual.byteLength; i++) {
if (actual[i] != exp[i]) {
window.domAutomationController.send('FAILED');
return;
}
}
// if we did not fail in the previous checks, the context successfully restored
window.domAutomationController.send('SUCCESS');
}
function drawFrame() {
const commandEncoder = device.createCommandEncoder();
const texture = context ? context.getCurrentTexture() : null;
const textureView = texture ? texture.createView() : null;
const renderPassDescriptor = {
colorAttachments: [{
view: textureView,
loadOp: 'clear',
clearValue: { r: color[0], g: color[1], b: color[2], a: 1.0 },
storeOp: 'store',
}],
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
if (lostJustHappened) {
// We don't want to check for the texture color until device and adapter have been reinitialized
// Checking prematurely was causing hangs in buffer.mapAync to never return (crbug.com/dawn/556)
if (device == null || adapter == null) {
return;
}
// We want to check if it's been restored to the reinit texture color.
checkTextureColor(texture, swapChainFormat, { x: 0, y: 0 }, reinitTextureColorArray);
lostJustHappened = false;
}
}
function doFrame(timestamp) {
drawFrame(timestamp);
requestAnimationFrame(doFrame);
}
requestAnimationFrame(doFrame);
}
export async function onLoad() {
const container = document.getElementById('container');
const canvas = document.createElement('canvas');
canvas.width = 100;
canvas.height = 100;
container.appendChild(canvas);
if (!navigator.gpu) {
console.warn('webgpu not supported');
return;
}
await init(canvas, initTextureColor);
const query = new URLSearchParams(window.location.search).get('query');
if (query)
contextLostTest(query);
}
document.addEventListener('DOMContentLoaded', onLoad);
</script>
</head>
<body>
<div id='container'></div>
</body>
</html>