| 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<GPUDeviceLostInfo>): Promise<GPUDeviceLostInfo> { |
| 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); |
| }); |