diff --git a/src/webgpu/api/operation/command_buffer/queries/timestampQuery.spec.ts b/src/webgpu/api/operation/command_buffer/queries/timestampQuery.spec.ts index 7ac90ec7a007..9e83f8044c88 100644 --- a/src/webgpu/api/operation/command_buffer/queries/timestampQuery.spec.ts +++ b/src/webgpu/api/operation/command_buffer/queries/timestampQuery.spec.ts @@ -40,6 +40,11 @@ and prevent pages from running. t.skipIfDeviceDoesNotHaveFeature('timestamp-query'); + if (numQuerySets === 65536) { + // Allow extra time for massive timestamp query allocation/cleanup churn. + t.setEndTestScopeTimeout(10000); + } + const view = t .createTextureTracked({ size: [1, 1, 1], diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index 9335592d4849..24b047267695 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -1460,6 +1460,12 @@ export class GPUTest extends GPUTestBase { assert(this.provider !== undefined, 'internal error: GPUDevice missing?'); this.provider.expectDeviceLost(reason); } + + /** Adjust timeout used when releasing the device after a test finishes. */ + setEndTestScopeTimeout(timeoutMs: number): void { + assert(this.provider !== undefined, 'internal error: GPUDevice missing?'); + this.provider.setEndTestScopeTimeout(timeoutMs); + } } /** diff --git a/src/webgpu/util/device_pool.ts b/src/webgpu/util/device_pool.ts index ba3135777998..5055c3577edb 100644 --- a/src/webgpu/util/device_pool.ts +++ b/src/webgpu/util/device_pool.ts @@ -14,11 +14,14 @@ import { getDefaultLimits, kPossibleLimits } from '../capability_info.js'; // MUST_NOT_BE_IMPORTED_BY_DATA_CACHE // This file should not be transitively imported by .cache.ts files +const kDefaultEndTestScopeTimeoutMs = 5000; + export interface DeviceProvider { /** Adapter the device was created from. Cannot be reused; just for adapter info. */ readonly adapter: GPUAdapter; readonly device: GPUDevice; expectDeviceLost(reason: GPUDeviceLostReason): void; + setEndTestScopeTimeout(timeoutMs: number): void; } class TestFailedButDeviceReusable extends Error {} @@ -351,6 +354,7 @@ class DeviceHolder implements DeviceProvider { expectedLostReason?: GPUDeviceLostReason; /** Number of test cases the device has been used for. */ testCaseUseCounter = 0; + private endTestScopeTimeoutMs = kDefaultEndTestScopeTimeoutMs; // Gets a device and creates a DeviceHolder. // If the device is lost, DeviceHolder.lost gets set. @@ -395,6 +399,7 @@ class DeviceHolder implements DeviceProvider { this.device.pushErrorScope('validation'); this.device.pushErrorScope('internal'); this.device.pushErrorScope('out-of-memory'); + this.endTestScopeTimeoutMs = kDefaultEndTestScopeTimeoutMs; } /** Mark the DeviceHolder as expecting a device loss when the test scope ends. */ @@ -403,21 +408,29 @@ class DeviceHolder implements DeviceProvider { this.expectedLostReason = reason; } + setEndTestScopeTimeout(timeoutMs: number): void { + assert(this.state === 'acquired'); + assert(timeoutMs >= 0, 'timeout must be non-negative'); + this.endTestScopeTimeoutMs = timeoutMs; + } + /** * Attempt to end test scopes: Check that there are no extra error scopes, and that no * otherwise-uncaptured errors occurred during the test. Time out if it takes too long. */ endTestScope(): Promise { assert(this.state === 'acquired'); - const kTimeout = 5000; - // Time out if attemptEndTestScope (popErrorScope or onSubmittedWorkDone) never completes. If // this rejects, the device won't be reused, so it's OK that popErrorScope calls may not have // finished. // // This could happen due to a browser bug - e.g., // as of this writing, on Chrome GPU process crash, popErrorScope just hangs. - return raceWithRejectOnTimeout(this.attemptEndTestScope(), kTimeout, 'endTestScope timed out'); + return raceWithRejectOnTimeout( + this.attemptEndTestScope(), + this.endTestScopeTimeoutMs, + 'endTestScope timed out' + ); } private async attemptEndTestScope(): Promise {