diff options
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/destroyed/texture.spec.ts')
-rw-r--r-- | dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/destroyed/texture.spec.ts | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/destroyed/texture.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/destroyed/texture.spec.ts new file mode 100644 index 0000000000..42036bd881 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/queue/destroyed/texture.spec.ts @@ -0,0 +1,294 @@ +export const description = ` +Tests using a destroyed texture on a queue. +`; + +import { makeTestGroup } from '../../../../../common/framework/test_group.js'; +import { unreachable } from '../../../../../common/util/util.js'; +import { ValidationTest } from '../../validation_test.js'; + +export const g = makeTestGroup(ValidationTest); + +g.test('writeTexture') + .desc( + ` +Tests that using a destroyed texture in writeTexture fails. +- x= {destroyed, not destroyed (control case)} + ` + ) + .paramsSubcasesOnly(u => u.combine('destroyed', [false, true] as const)) + .fn(t => { + const { destroyed } = t.params; + const texture = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'rgba8unorm', + usage: GPUTextureUsage.COPY_DST, + }) + ); + + if (destroyed) { + texture.destroy(); + } + + t.expectValidationError( + () => t.queue.writeTexture({ texture }, new Uint8Array(4), { bytesPerRow: 4 }, [1, 1, 1]), + destroyed + ); + }); + +g.test('copyTextureToTexture') + .desc( + ` +Tests that using a destroyed texture in copyTextureToTexture fails. +- x= {not destroyed (control case), src destroyed, dst destroyed} + ` + ) + .paramsSubcasesOnly(u => u.combine('destroyed', ['none', 'src', 'dst', 'both'] as const)) + .fn(t => { + const src = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'rgba8unorm', + usage: GPUTextureUsage.COPY_SRC, + }) + ); + const dst = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'rgba8unorm', + usage: GPUTextureUsage.COPY_DST, + }) + ); + + const encoder = t.device.createCommandEncoder(); + encoder.copyTextureToTexture({ texture: src }, { texture: dst }, [1, 1, 1]); + const commandBuffer = encoder.finish(); + + let shouldError = true; + switch (t.params.destroyed) { + case 'none': + shouldError = false; + break; + case 'src': + src.destroy(); + break; + case 'dst': + dst.destroy(); + break; + case 'both': + src.destroy(); + dst.destroy(); + break; + } + + t.expectValidationError(() => { + t.queue.submit([commandBuffer]); + }, shouldError); + }); + +g.test('copyBufferToTexture') + .desc( + ` +Tests that using a destroyed texture in copyBufferToTexture fails. +- x= {not destroyed (control case), dst destroyed} + ` + ) + .paramsSubcasesOnly(u => u.combine('destroyed', [false, true] as const)) + .fn(t => { + const { destroyed } = t.params; + const buffer = t.trackForCleanup( + t.device.createBuffer({ size: 4, usage: GPUBufferUsage.COPY_SRC }) + ); + const texture = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'rgba8unorm', + usage: GPUTextureUsage.COPY_DST, + }) + ); + + const encoder = t.device.createCommandEncoder(); + encoder.copyBufferToTexture({ buffer }, { texture }, [1, 1, 1]); + const commandBuffer = encoder.finish(); + + if (destroyed) { + texture.destroy(); + } + + t.expectValidationError(() => { + t.queue.submit([commandBuffer]); + }, destroyed); + }); + +g.test('copyTextureToBuffer') + .desc( + ` +Tests that using a destroyed texture in copyTextureToBuffer fails. +- x= {not destroyed (control case), src destroyed} + ` + ) + .paramsSubcasesOnly(u => u.combine('destroyed', [false, true] as const)) + .fn(t => { + const { destroyed } = t.params; + const texture = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'rgba8unorm', + usage: GPUTextureUsage.COPY_SRC, + }) + ); + const buffer = t.trackForCleanup( + t.device.createBuffer({ size: 4, usage: GPUBufferUsage.COPY_DST }) + ); + + const encoder = t.device.createCommandEncoder(); + encoder.copyTextureToBuffer({ texture }, { buffer }, [1, 1, 1]); + const commandBuffer = encoder.finish(); + + if (destroyed) { + texture.destroy(); + } + + t.expectValidationError(() => { + t.queue.submit([commandBuffer]); + }, destroyed); + }); + +g.test('setBindGroup') + .desc( + ` +Tests that using a destroyed texture referenced by a bindGroup set with setBindGroup fails +- x= {not destroyed (control case), destroyed} + ` + ) + .paramsSubcasesOnly(u => + u + .combine('destroyed', [false, true] as const) + .combine('encoderType', ['compute pass', 'render pass', 'render bundle'] as const) + ) + .fn(t => { + const { destroyed, encoderType } = t.params; + const { device } = t; + const texture = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'rgba8unorm', + usage: GPUTextureUsage.TEXTURE_BINDING, + }) + ); + + const layout = device.createBindGroupLayout({ + entries: [ + { + binding: 0, + visibility: GPUShaderStage.COMPUTE, + texture: {}, + }, + ], + }); + + const bindGroup = device.createBindGroup({ + layout, + entries: [{ binding: 0, resource: texture.createView() }], + }); + + const { encoder, finish } = t.createEncoder(encoderType); + encoder.setBindGroup(0, bindGroup); + const commandBuffer = finish(); + + if (destroyed) { + texture.destroy(); + } + + t.expectValidationError(() => { + t.queue.submit([commandBuffer]); + }, destroyed); + }); + +g.test('beginRenderPass') + .desc( + ` +Tests that using a destroyed texture referenced by a render pass fails +- x= {not destroyed (control case), colorAttachment destroyed, depthAttachment destroyed, resolveTarget destroyed} + ` + ) + .paramsSubcasesOnly(u => + u.combine('textureToDestroy', [ + 'none', + 'colorAttachment', + 'resolveAttachment', + 'depthStencilAttachment', + ]) + ) + .fn(t => { + const { textureToDestroy } = t.params; + const { device } = t; + + const colorAttachment = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'rgba8unorm', + sampleCount: 4, + usage: GPUTextureUsage.RENDER_ATTACHMENT, + }) + ); + + const resolveAttachment = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'rgba8unorm', + usage: GPUTextureUsage.RENDER_ATTACHMENT, + }) + ); + + const depthStencilAttachment = t.trackForCleanup( + t.device.createTexture({ + size: [1, 1, 1], + format: 'depth32float', + sampleCount: 4, + usage: GPUTextureUsage.RENDER_ATTACHMENT, + }) + ); + + const encoder = device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: colorAttachment.createView(), + resolveTarget: resolveAttachment.createView(), + loadOp: 'clear', + storeOp: 'store', + }, + ], + depthStencilAttachment: { + view: depthStencilAttachment.createView(), + depthClearValue: 0, + depthLoadOp: 'clear', + depthStoreOp: 'store', + }, + }); + pass.end(); + const commandBuffer = encoder.finish(); + + switch (textureToDestroy) { + case 'none': + break; + case 'colorAttachment': + colorAttachment.destroy(); + break; + case 'resolveAttachment': + resolveAttachment.destroy(); + break; + case 'depthStencilAttachment': + depthStencilAttachment.destroy(); + break; + default: + unreachable(); + } + + const shouldError = textureToDestroy !== 'none'; + + t.expectValidationError(() => { + t.queue.submit([commandBuffer]); + }, shouldError); + }); |