export const description = ` Tests using a destroyed buffer on a queue. `; import { makeTestGroup } from '../../../../../common/framework/test_group.js'; import { AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js'; export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest); g.test('writeBuffer') .desc( ` Tests that using a destroyed buffer in writeBuffer fails. - x= {destroyed, not destroyed (control case)} ` ) .paramsSubcasesOnly(u => u.combine('destroyed', [false, true] as const)) .fn(t => { const { destroyed } = t.params; const buffer = t.createBufferTracked({ size: 4, usage: GPUBufferUsage.COPY_DST, }); if (destroyed) { buffer.destroy(); } t.expectValidationError(() => t.queue.writeBuffer(buffer, 0, new Uint8Array(4)), destroyed); }); g.test('copyBufferToBuffer') .desc( ` Tests that using a destroyed buffer in copyBufferToBuffer 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.createBufferTracked({ size: 4, usage: GPUBufferUsage.COPY_SRC }); const dst = t.createBufferTracked({ size: 4, usage: GPUBufferUsage.COPY_DST }); const encoder = t.device.createCommandEncoder(); encoder.copyBufferToBuffer(src, 0, dst, 0, dst.size); 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 buffer in copyBufferToTexture fails. - x= {not destroyed (control case), src destroyed} ` ) .paramsSubcasesOnly(u => u.combine('destroyed', [false, true] as const)) .fn(t => { const { destroyed } = t.params; const buffer = t.createBufferTracked({ size: 4, usage: GPUBufferUsage.COPY_SRC }); const texture = t.createTextureTracked({ 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) { buffer.destroy(); } t.expectValidationError(() => { t.queue.submit([commandBuffer]); }, destroyed); }); g.test('copyTextureToBuffer') .desc( ` Tests that using a destroyed buffer in copyTextureToBuffer fails. - x= {not destroyed (control case), dst destroyed} ` ) .paramsSubcasesOnly(u => u.combine('destroyed', [false, true] as const)) .fn(t => { const { destroyed } = t.params; const texture = t.createTextureTracked({ size: [1, 1, 1], format: 'rgba8unorm', usage: GPUTextureUsage.COPY_SRC, }); const buffer = t.createBufferTracked({ size: 4, usage: GPUBufferUsage.COPY_DST }); const encoder = t.device.createCommandEncoder(); encoder.copyTextureToBuffer({ texture }, { buffer }, [1, 1, 1]); const commandBuffer = encoder.finish(); if (destroyed) { buffer.destroy(); } t.expectValidationError(() => { t.queue.submit([commandBuffer]); }, destroyed); }); g.test('setBindGroup') .desc( ` Tests that using a destroyed buffer 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 buffer = t.createBufferTracked({ size: 4, usage: GPUBufferUsage.UNIFORM, }); const layout = device.createBindGroupLayout({ entries: [ { binding: 0, visibility: GPUShaderStage.COMPUTE | GPUShaderStage.VERTEX, buffer: {}, }, ], }); const bindGroup = device.createBindGroup({ layout, entries: [{ binding: 0, resource: { buffer } }], }); const { encoder, finish } = t.createEncoder(encoderType); encoder.setBindGroup(0, bindGroup); const commandBuffer = finish(); if (destroyed) { buffer.destroy(); } t.expectValidationError(() => { t.queue.submit([commandBuffer]); }, destroyed); }); g.test('setVertexBuffer') .desc( ` Tests that using a destroyed buffer referenced in a render pass fails - x= {not destroyed (control case), destroyed} ` ) .paramsSubcasesOnly(u => u .combine('destroyed', [false, true] as const) .combine('encoderType', ['render pass', 'render bundle'] as const) ) .fn(t => { const { destroyed, encoderType } = t.params; const vertexBuffer = t.createBufferTracked({ size: 4, usage: GPUBufferUsage.VERTEX, }); const { encoder, finish } = t.createEncoder(encoderType); encoder.setVertexBuffer(0, vertexBuffer); const commandBuffer = finish(); if (destroyed) { vertexBuffer.destroy(); } t.expectValidationError(() => { t.queue.submit([commandBuffer]); }, destroyed); }); g.test('setIndexBuffer') .desc( ` Tests that using a destroyed buffer referenced in a render pass fails - x= {not destroyed (control case), destroyed} ` ) .paramsSubcasesOnly(u => u .combine('destroyed', [false, true] as const) .combine('encoderType', ['render pass', 'render bundle'] as const) ) .fn(t => { const { destroyed, encoderType } = t.params; const indexBuffer = t.createBufferTracked({ size: 4, usage: GPUBufferUsage.INDEX, }); const { encoder, finish } = t.createEncoder(encoderType); encoder.setIndexBuffer(indexBuffer, 'uint16'); const commandBuffer = finish(); if (destroyed) { indexBuffer.destroy(); } t.expectValidationError(() => { t.queue.submit([commandBuffer]); }, destroyed); }); g.test('resolveQuerySet') .desc( ` Tests that using a destroyed buffer referenced via resolveQuerySet fails - x= {not destroyed (control case), destroyed} ` ) .paramsSubcasesOnly(u => u.combine('destroyed', [false, true] as const)) .fn(t => { const { destroyed } = t.params; const querySet = t.createQuerySetTracked({ type: 'occlusion', count: 1, }); const querySetBuffer = t.createBufferTracked({ size: 8, usage: GPUBufferUsage.QUERY_RESOLVE, }); const encoder = t.device.createCommandEncoder(); encoder.resolveQuerySet(querySet, 0, 1, querySetBuffer, 0); const commandBuffer = encoder.finish(); if (destroyed) { querySetBuffer.destroy(); } t.expectValidationError(() => { t.queue.submit([commandBuffer]); }, destroyed); });