export const description = ` Tests for GPUDevice.lost. `; import { Fixture } from '../../../../common/framework/fixture.js'; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { attemptGarbageCollection } from '../../../../common/util/collect_garbage.js'; import { getGPU } from '../../../../common/util/navigator_gpu.js'; import { assert, assertNotSettledWithinTime, raceWithRejectOnTimeout, } from '../../../../common/util/util.js'; class DeviceLostTests extends Fixture { // Default timeout for waiting for device lost is 2 seconds. readonly kDeviceLostTimeoutMS = 2000; getDeviceLostWithTimeout(lost: Promise): Promise { return raceWithRejectOnTimeout(lost, this.kDeviceLostTimeoutMS, 'device was not lost'); } expectDeviceDestroyed(device: GPUDevice): void { this.eventualAsyncExpectation(async niceStack => { try { const lost = await this.getDeviceLostWithTimeout(device.lost); this.expect(lost.reason === 'destroyed', 'device was lost from destroy'); } catch (ex) { niceStack.message = 'device was not lost'; this.rec.expectationFailed(niceStack); } }); } } export const g = makeTestGroup(DeviceLostTests); g.test('not_lost_on_gc') .desc( `'lost' is never resolved by GPUDevice being garbage collected (with attemptGarbageCollection).` ) .fn(async t => { // Wraps a lost promise object creation in a function scope so that the device has the best // chance of being gone and ready for GC before trying to resolve the lost promise. const { lost } = await (async () => { const adapter = await getGPU(t.rec).requestAdapter(); assert(adapter !== null); const device = await t.requestDeviceTracked(adapter); return { lost: device.lost }; })(); await assertNotSettledWithinTime(lost, t.kDeviceLostTimeoutMS, 'device was unexpectedly lost'); await attemptGarbageCollection(); }); g.test('lost_on_destroy') .desc(`'lost' is resolved, with reason='destroyed', on GPUDevice.destroy().`) .fn(async t => { const adapter = await getGPU(t.rec).requestAdapter(); assert(adapter !== null); const device: GPUDevice = await t.requestDeviceTracked(adapter); t.expectDeviceDestroyed(device); device.destroy(); }); g.test('same_object') .desc(`'lost' provides the same Promise and GPUDeviceLostInfo objects each time it's accessed.`) .fn(async t => { const adapter = await getGPU(t.rec).requestAdapter(); assert(adapter !== null); const device: GPUDevice = await t.requestDeviceTracked(adapter); // The promises should be the same promise object. const lostPromise1 = device.lost; const lostPromise2 = device.lost; t.expect(lostPromise1 === lostPromise2); // Promise object should still be the same after destroy. device.destroy(); const lostPromise3 = device.lost; t.expect(lostPromise1 === lostPromise3); // The results should also be the same result object. const lost1 = await t.getDeviceLostWithTimeout(lostPromise1); const lost2 = await t.getDeviceLostWithTimeout(lostPromise2); const lost3 = await t.getDeviceLostWithTimeout(lostPromise3); // Promise object should still be the same after we've been notified about device loss. const lostPromise4 = device.lost; t.expect(lostPromise1 === lostPromise4); const lost4 = await t.getDeviceLostWithTimeout(lostPromise4); t.expect(lost1 === lost2 && lost2 === lost3 && lost3 === lost4); });