From c51c6a20ee97988536fca17eb699fb745631b748 Mon Sep 17 00:00:00 2001 From: Hao Li Date: Wed, 23 Mar 2022 21:44:58 +0800 Subject: [PATCH] Update DevicePool to provide multiple devices - Remove mismatched device pool, make the device pool could provide multiple devices at once, and reserve a default mismatched device at test initialization. - Cancel selecting the default mismatched device in tests. For requesting mismatched device with particular features, we still need to call selectMismatchedDeviceOrSkipTestCase. --- .../api/validation/createBindGroup.spec.ts | 8 -- .../validation/createComputePipeline.spec.ts | 8 -- .../validation/createPipelineLayout.spec.ts | 4 - .../validation/createRenderPipeline.spec.ts | 8 -- .../encoding/beginRenderPass.spec.ts | 12 -- .../encoding/cmds/clearBuffer.spec.ts | 4 - .../encoding/cmds/compute_pass.spec.ts | 8 -- .../encoding/cmds/copyBufferToBuffer.spec.ts | 4 - .../cmds/copyTextureToTexture.spec.ts | 4 - .../cmds/render/indirect_draw.spec.ts | 4 - .../cmds/render/setIndexBuffer.spec.ts | 4 - .../encoding/cmds/render/setPipeline.spec.ts | 4 - .../cmds/render/setVertexBuffer.spec.ts | 4 - .../encoding/cmds/setBindGroup.spec.ts | 4 - .../encoding/queries/general.spec.ts | 2 +- .../encoding/queries/resolveQuerySet.spec.ts | 4 - .../validation/encoding/render_bundle.spec.ts | 4 - .../image_copy/buffer_related.spec.ts | 4 - .../image_copy/texture_related.spec.ts | 4 - .../CopyExternalImageToTexture.spec.ts | 4 - .../api/validation/queue/submit.spec.ts | 4 - .../api/validation/queue/writeBuffer.spec.ts | 4 - src/webgpu/gpu_test.ts | 119 +++++++++--------- src/webgpu/util/device_pool.ts | 42 +++++-- .../web_platform/copyToTexture/canvas.spec.ts | 1 - 25 files changed, 89 insertions(+), 183 deletions(-) diff --git a/src/webgpu/api/validation/createBindGroup.spec.ts b/src/webgpu/api/validation/createBindGroup.spec.ts index f8af528a274e..a454eefeb3bb 100644 --- a/src/webgpu/api/validation/createBindGroup.spec.ts +++ b/src/webgpu/api/validation/createBindGroup.spec.ts @@ -502,10 +502,6 @@ g.test('bind_group_layout,device_mismatch') .fn(async t => { const mismatched = t.params.mismatched; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const bgl = device.createBindGroupLayout({ @@ -560,10 +556,6 @@ g.test('binding_resources,device_mismatch') .fn(async t => { const { entry, resource0Mismatched, resource1Mismatched } = t.params; - if (resource0Mismatched || resource1Mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const info = bindingTypeInfo(entry); const resource0 = resource0Mismatched diff --git a/src/webgpu/api/validation/createComputePipeline.spec.ts b/src/webgpu/api/validation/createComputePipeline.spec.ts index a1d9705e83e4..94514414a5ba 100644 --- a/src/webgpu/api/validation/createComputePipeline.spec.ts +++ b/src/webgpu/api/validation/createComputePipeline.spec.ts @@ -182,10 +182,6 @@ g.test('pipeline_layout,device_mismatch') .fn(async t => { const { isAsync, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const layout = device.createPipelineLayout({ bindGroupLayouts: [] }); @@ -209,10 +205,6 @@ g.test('shader_module,device_mismatch') .fn(async t => { const { isAsync, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const module = device.createShaderModule({ diff --git a/src/webgpu/api/validation/createPipelineLayout.spec.ts b/src/webgpu/api/validation/createPipelineLayout.spec.ts index 7c234a70d83d..04f4bba92106 100644 --- a/src/webgpu/api/validation/createPipelineLayout.spec.ts +++ b/src/webgpu/api/validation/createPipelineLayout.spec.ts @@ -122,10 +122,6 @@ g.test('bind_group_layouts,device_mismatch') const mismatched = layout0Mismatched || layout1Mismatched; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const bglDescriptor: GPUBindGroupLayoutDescriptor = { entries: [], }; diff --git a/src/webgpu/api/validation/createRenderPipeline.spec.ts b/src/webgpu/api/validation/createRenderPipeline.spec.ts index dea5da8884fa..8e39aa4fca16 100644 --- a/src/webgpu/api/validation/createRenderPipeline.spec.ts +++ b/src/webgpu/api/validation/createRenderPipeline.spec.ts @@ -664,10 +664,6 @@ g.test('pipeline_layout,device_mismatch') .fn(async t => { const { isAsync, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const layout = device.createPipelineLayout({ bindGroupLayouts: [] }); @@ -708,10 +704,6 @@ g.test('shader_module,device_mismatch') .fn(async t => { const { isAsync, vertex_mismatched, fragment_mismatched, _success } = t.params; - if (vertex_mismatched || fragment_mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const code = ` @stage(vertex) fn main() -> @builtin(position) vec4 { return vec4(0.0, 0.0, 0.0, 1.0); diff --git a/src/webgpu/api/validation/encoding/beginRenderPass.spec.ts b/src/webgpu/api/validation/encoding/beginRenderPass.spec.ts index 38536f0316db..28ebefe803c1 100644 --- a/src/webgpu/api/validation/encoding/beginRenderPass.spec.ts +++ b/src/webgpu/api/validation/encoding/beginRenderPass.spec.ts @@ -65,10 +65,6 @@ g.test('color_attachments,device_mismatch') const mismatched = view0Mismatched || target0Mismatched || view1Mismatched || target1Mismatched; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const view0Texture = view0Mismatched ? t.getDeviceMismatchedRenderTexture(4) : t.getRenderTexture(4); @@ -114,10 +110,6 @@ g.test('depth_stencil_attachment,device_mismatch') .fn(async t => { const { mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const descriptor: GPUTextureDescriptor = { size: { width: 4, height: 4, depthOrArrayLayers: 1 }, format: 'depth24plus-stencil8', @@ -154,10 +146,6 @@ g.test('occlusion_query_set,device_mismatch') .fn(async t => { const { mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const occlusionQuerySet = device.createQuerySet({ diff --git a/src/webgpu/api/validation/encoding/cmds/clearBuffer.spec.ts b/src/webgpu/api/validation/encoding/cmds/clearBuffer.spec.ts index b58062bac4b9..079b90f9c8f9 100644 --- a/src/webgpu/api/validation/encoding/cmds/clearBuffer.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/clearBuffer.spec.ts @@ -60,10 +60,6 @@ g.test('buffer,device_mismatch') .fn(async t => { const { mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const size = 8; diff --git a/src/webgpu/api/validation/encoding/cmds/compute_pass.spec.ts b/src/webgpu/api/validation/encoding/cmds/compute_pass.spec.ts index 06aaa7b4e8c4..ef640a03d3d7 100644 --- a/src/webgpu/api/validation/encoding/cmds/compute_pass.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/compute_pass.spec.ts @@ -68,10 +68,6 @@ g.test('pipeline,device_mismatch') .fn(async t => { const { mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const pipeline = device.createComputePipeline({ @@ -184,10 +180,6 @@ g.test('indirect_dispatch_buffer,device_mismatch') .fn(async t => { const { mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const pipeline = t.createNoOpComputePipeline(); const device = mismatched ? t.mismatchedDevice : t.device; diff --git a/src/webgpu/api/validation/encoding/cmds/copyBufferToBuffer.spec.ts b/src/webgpu/api/validation/encoding/cmds/copyBufferToBuffer.spec.ts index 2559d982b65c..91020e6cf440 100644 --- a/src/webgpu/api/validation/encoding/cmds/copyBufferToBuffer.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/copyBufferToBuffer.spec.ts @@ -106,10 +106,6 @@ g.test('buffer,device_mismatch') const { srcMismatched, dstMismatched } = t.params; const mismatched = srcMismatched || dstMismatched; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const srcBuffer = device.createBuffer({ diff --git a/src/webgpu/api/validation/encoding/cmds/copyTextureToTexture.spec.ts b/src/webgpu/api/validation/encoding/cmds/copyTextureToTexture.spec.ts index 3bbba48d524b..213df332ea0a 100644 --- a/src/webgpu/api/validation/encoding/cmds/copyTextureToTexture.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/copyTextureToTexture.spec.ts @@ -121,10 +121,6 @@ g.test('texture,device_mismatch') const { srcMismatched, dstMismatched } = t.params; const mismatched = srcMismatched || dstMismatched; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const size = { width: 4, height: 4, depthOrArrayLayers: 1 }; const format = 'rgba8unorm'; diff --git a/src/webgpu/api/validation/encoding/cmds/render/indirect_draw.spec.ts b/src/webgpu/api/validation/encoding/cmds/render/indirect_draw.spec.ts index 56aec227dfbd..83ffacfd2c76 100644 --- a/src/webgpu/api/validation/encoding/cmds/render/indirect_draw.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/render/indirect_draw.spec.ts @@ -58,10 +58,6 @@ g.test('indirect_buffer,device_mismatch') .fn(async t => { const { encoderType, indexed, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const indirectBuffer = device.createBuffer({ diff --git a/src/webgpu/api/validation/encoding/cmds/render/setIndexBuffer.spec.ts b/src/webgpu/api/validation/encoding/cmds/render/setIndexBuffer.spec.ts index 5052905e71c5..39f5dd8297de 100644 --- a/src/webgpu/api/validation/encoding/cmds/render/setIndexBuffer.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/render/setIndexBuffer.spec.ts @@ -36,10 +36,6 @@ g.test('index_buffer,device_mismatch') .fn(async t => { const { encoderType, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const indexBuffer = device.createBuffer({ diff --git a/src/webgpu/api/validation/encoding/cmds/render/setPipeline.spec.ts b/src/webgpu/api/validation/encoding/cmds/render/setPipeline.spec.ts index 9f640d25b295..65dbae898573 100644 --- a/src/webgpu/api/validation/encoding/cmds/render/setPipeline.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/render/setPipeline.spec.ts @@ -34,10 +34,6 @@ g.test('pipeline,device_mismatch') .fn(async t => { const { encoderType, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const pipeline = device.createRenderPipeline({ diff --git a/src/webgpu/api/validation/encoding/cmds/render/setVertexBuffer.spec.ts b/src/webgpu/api/validation/encoding/cmds/render/setVertexBuffer.spec.ts index 5de456161a31..c5a921db8af7 100644 --- a/src/webgpu/api/validation/encoding/cmds/render/setVertexBuffer.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/render/setVertexBuffer.spec.ts @@ -62,10 +62,6 @@ g.test('vertex_buffer,device_mismatch') .fn(async t => { const { encoderType, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const vertexBuffer = device.createBuffer({ diff --git a/src/webgpu/api/validation/encoding/cmds/setBindGroup.spec.ts b/src/webgpu/api/validation/encoding/cmds/setBindGroup.spec.ts index 94b441f6bccc..f89b77b4cfee 100644 --- a/src/webgpu/api/validation/encoding/cmds/setBindGroup.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/setBindGroup.spec.ts @@ -143,10 +143,6 @@ g.test('bind_group,device_mismatch') .fn(async t => { const { encoderType, useU32Array, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const buffer = device.createBuffer({ diff --git a/src/webgpu/api/validation/encoding/queries/general.spec.ts b/src/webgpu/api/validation/encoding/queries/general.spec.ts index b294fd23f85a..7d240c356b29 100644 --- a/src/webgpu/api/validation/encoding/queries/general.spec.ts +++ b/src/webgpu/api/validation/encoding/queries/general.spec.ts @@ -128,7 +128,7 @@ g.test('timestamp_query,device_mismatch') .fn(async t => { const { mismatched } = t.params; - await t.selectDeviceForQueryTypeOrSkipTestCase('timestamp'); + await t.selectDeviceOrSkipTestCase('timestamp-query'); if (mismatched) { await t.selectMismatchedDeviceOrSkipTestCase('timestamp-query'); diff --git a/src/webgpu/api/validation/encoding/queries/resolveQuerySet.spec.ts b/src/webgpu/api/validation/encoding/queries/resolveQuerySet.spec.ts index c5b0e07194ca..91c2574fffb8 100644 --- a/src/webgpu/api/validation/encoding/queries/resolveQuerySet.spec.ts +++ b/src/webgpu/api/validation/encoding/queries/resolveQuerySet.spec.ts @@ -157,10 +157,6 @@ g.test('query_set_buffer,device_mismatch') const { querySetMismatched, bufferMismatched } = t.params; const mismatched = querySetMismatched || bufferMismatched; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const queryCout = 1; diff --git a/src/webgpu/api/validation/encoding/render_bundle.spec.ts b/src/webgpu/api/validation/encoding/render_bundle.spec.ts index ecec1001c283..0456742fbd2a 100644 --- a/src/webgpu/api/validation/encoding/render_bundle.spec.ts +++ b/src/webgpu/api/validation/encoding/render_bundle.spec.ts @@ -38,10 +38,6 @@ g.test('device_mismatch') const { bundle0Mismatched, bundle1Mismatched } = t.params; const mismatched = bundle0Mismatched || bundle1Mismatched; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const descriptor: GPURenderBundleEncoderDescriptor = { diff --git a/src/webgpu/api/validation/image_copy/buffer_related.spec.ts b/src/webgpu/api/validation/image_copy/buffer_related.spec.ts index d42e6aa39f84..0c5886e3ce77 100644 --- a/src/webgpu/api/validation/image_copy/buffer_related.spec.ts +++ b/src/webgpu/api/validation/image_copy/buffer_related.spec.ts @@ -65,10 +65,6 @@ g.test('buffer,device_mismatch') .fn(async t => { const { method, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const buffer = device.createBuffer({ diff --git a/src/webgpu/api/validation/image_copy/texture_related.spec.ts b/src/webgpu/api/validation/image_copy/texture_related.spec.ts index 1c99d760ee63..3607b8005cb6 100644 --- a/src/webgpu/api/validation/image_copy/texture_related.spec.ts +++ b/src/webgpu/api/validation/image_copy/texture_related.spec.ts @@ -73,10 +73,6 @@ g.test('texture,device_mismatch') .fn(async t => { const { method, mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const texture = device.createTexture({ diff --git a/src/webgpu/api/validation/queue/copyToTexture/CopyExternalImageToTexture.spec.ts b/src/webgpu/api/validation/queue/copyToTexture/CopyExternalImageToTexture.spec.ts index d4c918b25496..e21019d8267a 100644 --- a/src/webgpu/api/validation/queue/copyToTexture/CopyExternalImageToTexture.spec.ts +++ b/src/webgpu/api/validation/queue/copyToTexture/CopyExternalImageToTexture.spec.ts @@ -618,10 +618,6 @@ g.test('destination_texture,device_mismatch') .fn(async t => { const { mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const copySize = { width: 1, height: 1, depthOrArrayLayers: 1 }; diff --git a/src/webgpu/api/validation/queue/submit.spec.ts b/src/webgpu/api/validation/queue/submit.spec.ts index 791003ed6c9e..81042549b886 100644 --- a/src/webgpu/api/validation/queue/submit.spec.ts +++ b/src/webgpu/api/validation/queue/submit.spec.ts @@ -28,10 +28,6 @@ g.test('command_buffer,device_mismatch') const { cb0Mismatched, cb1Mismatched } = t.params; const mismatched = cb0Mismatched || cb1Mismatched; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const encoder0 = cb0Mismatched ? t.mismatchedDevice.createCommandEncoder() : t.device.createCommandEncoder(); diff --git a/src/webgpu/api/validation/queue/writeBuffer.spec.ts b/src/webgpu/api/validation/queue/writeBuffer.spec.ts index de4caf7574f8..3c7f214e2166 100644 --- a/src/webgpu/api/validation/queue/writeBuffer.spec.ts +++ b/src/webgpu/api/validation/queue/writeBuffer.spec.ts @@ -179,10 +179,6 @@ g.test('buffer,device_mismatch') .fn(async t => { const { mismatched } = t.params; - if (mismatched) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); - } - const device = mismatched ? t.mismatchedDevice : t.device; const buffer = device.createBuffer({ diff --git a/src/webgpu/gpu_test.ts b/src/webgpu/gpu_test.ts index 3bfe0ccb9412..9727e5c94059 100644 --- a/src/webgpu/gpu_test.ts +++ b/src/webgpu/gpu_test.ts @@ -37,10 +37,6 @@ import { PerTexelComponent, kTexelRepresentationInfo } from './util/texture/texe const devicePool = new DevicePool(); -// MAINTENANCE_TODO: When DevicePool becomes able to provide multiple devices at once, use the -// usual one instead of a new one. -const mismatchedDevicePool = new DevicePool(); - const kResourceStateValues = ['valid', 'invalid', 'destroyed'] as const; export type ResourceState = typeof kResourceStateValues[number]; export const kResourceStates: readonly ResourceState[] = kResourceStateValues; @@ -100,31 +96,6 @@ export class GPUTest extends Fixture { return this.mismatchedAcquiredDevice; } - /** - * Create other device different with current test device, which could be got by `.mismatchedDevice`. - * A `descriptor` may be undefined, which returns a `default` mismatched device. - * If the request descriptor or feature name can't be supported, throws an exception to skip the entire test case. - */ - async selectMismatchedDeviceOrSkipTestCase( - descriptor: - | UncanonicalizedDeviceDescriptor - | GPUFeatureName - | undefined - | Array - ): Promise { - assert( - this.mismatchedProvider === undefined, - "Can't selectMismatchedDeviceOrSkipTestCase() multiple times" - ); - - this.mismatchedProvider = - descriptor === undefined - ? await mismatchedDevicePool.reserve() - : await mismatchedDevicePool.reserve(initUncanonicalizedDeviceDescriptor(descriptor)); - - this.mismatchedAcquiredDevice = this.mismatchedProvider.acquire(); - } - /** GPUQueue for the test to use. (Same as `t.device.queue`.) */ get queue(): GPUQueue { return this.device.queue; @@ -134,57 +105,47 @@ export class GPUTest extends Fixture { await super.init(); this.provider = await devicePool.reserve(); + this.mismatchedProvider = await devicePool.reserve(undefined, true); } protected async finalize(): Promise { await super.finalize(); - if (this.provider) { + const tryRelease = async (provider: DeviceProvider | undefined) => { let threw = false; let thrownValue: unknown; - { - const provider = this.provider; - this.provider = undefined; - try { - await devicePool.release(provider); - } catch (ex) { - threw = true; - thrownValue = ex; + if (provider) { + const oldProvider = provider; + if (provider === this.provider) { + this.provider = undefined; + } else { + this.mismatchedProvider = undefined; } - } - // The GPUDevice and GPUQueue should now have no outstanding references. - - if (threw) { - if (thrownValue instanceof TestOOMedShouldAttemptGC) { - // Try to clean up, in case there are stray GPU resources in need of collection. - await attemptGarbageCollection(); - } - throw thrownValue; - } - } - if (this.mismatchedProvider) { - // MAINTENANCE_TODO(kainino0x): Deduplicate this with code in GPUTest.finalize - let threw = false; - let thrownValue: unknown; - { - const provider = this.mismatchedProvider; - this.mismatchedProvider = undefined; try { - await mismatchedDevicePool.release(provider); + await devicePool.release(oldProvider, provider !== this.provider); } catch (ex) { threw = true; thrownValue = ex; } + + // The GPUDevice and GPUQueue should now have no outstanding references. } + return { threw, thrownValue }; + }; - if (threw) { - if (thrownValue instanceof TestOOMedShouldAttemptGC) { - // Try to clean up, in case there are stray GPU resources in need of collection. - await attemptGarbageCollection(); - } - throw thrownValue; + const defaultError = await tryRelease(this.provider); + const mismatchedError = await tryRelease(this.mismatchedProvider); + + if (defaultError.threw || mismatchedError.threw) { + if ( + defaultError.thrownValue instanceof TestOOMedShouldAttemptGC || + mismatchedError.thrownValue instanceof TestOOMedShouldAttemptGC + ) { + // Try to clean up, in case there are stray GPU resources in need of collection. + await attemptGarbageCollection(); } + throw defaultError.threw ? defaultError.thrownValue : mismatchedError.thrownValue; } } @@ -221,6 +182,38 @@ export class GPUTest extends Fixture { this.acquiredDevice = this.provider.acquire(); } + /** + * Create other device different with current test device, which could be got by `.mismatchedDevice`. + * A `descriptor` may be undefined, which returns a `default` mismatched device. + * If the request descriptor or feature name can't be supported, throws an exception to skip the entire test case. + */ + async selectMismatchedDeviceOrSkipTestCase( + descriptor: + | UncanonicalizedDeviceDescriptor + | GPUFeatureName + | undefined + | Array + ): Promise { + if (descriptor === undefined) return; + + assert(this.mismatchedProvider !== undefined); + // Make sure the device isn't replaced after it's been retrieved once. + assert( + !this.mismatchedAcquiredDevice, + "Can't selectMismatchedDeviceOrSkipTestCase() after the device has been used" + ); + + const oldProvider = this.mismatchedProvider; + this.mismatchedProvider = undefined; + await devicePool.release(oldProvider, true); + + this.mismatchedProvider = await devicePool.reserve( + initUncanonicalizedDeviceDescriptor(descriptor), + true + ); + this.mismatchedAcquiredDevice = this.mismatchedProvider.acquire(); + } + /** * Create device with texture format(s) required feature(s). * If the device creation fails, then skip the test for that format(s). diff --git a/src/webgpu/util/device_pool.ts b/src/webgpu/util/device_pool.ts index 49456ca29e1d..fcd415cd7d40 100644 --- a/src/webgpu/util/device_pool.ts +++ b/src/webgpu/util/device_pool.ts @@ -17,31 +17,47 @@ export class DevicePool { private defaultHolder: DeviceHolder | 'uninitialized' | 'failed' = 'uninitialized'; /** Devices with descriptors. */ private nonDefaultHolders = new DescriptorToHolderMap(); + /** Device with no descriptor for device mismatch validation. */ + private mismatchedDefaultHolder: DeviceHolder | 'uninitialized' | 'failed' = 'uninitialized'; + /** Devices with descriptors for device mismatch validation. */ + private mismatchedNonDefaultHolders = new DescriptorToHolderMap(); /** Request a device from the pool. */ - async reserve(descriptor?: UncanonicalizedDeviceDescriptor): Promise { + async reserve( + descriptor?: UncanonicalizedDeviceDescriptor, + mismatched: boolean = false + ): Promise { // Always attempt to initialize default device, to see if it succeeds. let errorMessage = ''; - if (this.defaultHolder === 'uninitialized') { + let defaultHolder = mismatched ? this.mismatchedDefaultHolder : this.defaultHolder; + if (defaultHolder === 'uninitialized') { try { - this.defaultHolder = await DeviceHolder.create(undefined); + defaultHolder = await DeviceHolder.create(undefined); } catch (ex) { - this.defaultHolder = 'failed'; + defaultHolder = 'failed'; if (ex instanceof Error) { errorMessage = ` with ${ex.name} "${ex.message}"`; } } } + + if (mismatched) { + this.mismatchedDefaultHolder = defaultHolder; + } else { + this.defaultHolder = defaultHolder; + } assert( - this.defaultHolder !== 'failed', + defaultHolder !== 'failed', `WebGPU device failed to initialize${errorMessage}; not retrying` ); let holder; if (descriptor === undefined) { - holder = this.defaultHolder; + holder = defaultHolder; } else { - holder = await this.nonDefaultHolders.getOrCreate(descriptor); + holder = mismatched + ? await this.mismatchedNonDefaultHolders.getOrCreate(descriptor) + : await this.nonDefaultHolders.getOrCreate(descriptor); } assert(holder.state === 'free', 'Device was in use on DevicePool.acquire'); @@ -51,8 +67,9 @@ export class DevicePool { // When a test is done using a device, it's released back into the pool. // This waits for error scopes, checks their results, and checks for various error conditions. - async release(holder: DeviceProvider): Promise { - assert(this.defaultHolder instanceof DeviceHolder); + async release(holder: DeviceProvider, mismatched: boolean = false): Promise { + const defaultHolder = mismatched ? this.mismatchedDefaultHolder : this.defaultHolder; + assert(defaultHolder instanceof DeviceHolder); assert(holder instanceof DeviceHolder); assert(holder.state !== 'free', 'trying to release a device while already released'); @@ -73,8 +90,13 @@ export class DevicePool { if (!(ex instanceof TestFailedButDeviceReusable)) { if (holder === this.defaultHolder) { this.defaultHolder = 'uninitialized'; + } else if (holder === this.mismatchedDefaultHolder) { + this.mismatchedDefaultHolder = 'uninitialized'; } else { - this.nonDefaultHolders.deleteByDevice(holder.device); + const nonDefaultHolders = mismatched + ? this.mismatchedNonDefaultHolders + : this.nonDefaultHolders; + nonDefaultHolders.deleteByDevice(holder.device); } if ('destroy' in holder.device) { holder.device.destroy(); diff --git a/src/webgpu/web_platform/copyToTexture/canvas.spec.ts b/src/webgpu/web_platform/copyToTexture/canvas.spec.ts index 7b3cad456baa..46c1e6ad14fd 100644 --- a/src/webgpu/web_platform/copyToTexture/canvas.spec.ts +++ b/src/webgpu/web_platform/copyToTexture/canvas.spec.ts @@ -640,7 +640,6 @@ g.test('copy_contents_from_gpu_context_canvas') let device: GPUDevice; if (!srcAndDstInSameGPUDevice) { - await t.selectMismatchedDeviceOrSkipTestCase(undefined); device = t.mismatchedDevice; } else { device = t.device;