blob: f66cf5d82ccd16789eccebdad70fa35d71ebba66 [file] [log] [blame]
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 { AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js';
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
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.createTextureTracked({
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.createTextureTracked({
size: [1, 1, 1],
format: 'rgba8unorm',
usage: GPUTextureUsage.COPY_SRC,
});
const dst = t.createTextureTracked({
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.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) {
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.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) {
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)
.combine('bindingType', ['texture', 'storageTexture'] as const)
)
.fn(t => {
const { destroyed, encoderType, bindingType } = t.params;
const { device } = t;
const texture = t.createTextureTracked({
size: [1, 1, 1],
format: 'rgba8unorm',
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,
});
const layout = device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.COMPUTE,
[bindingType]: {
format: texture.format,
},
},
],
});
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.createTextureTracked({
size: [1, 1, 1],
format: 'rgba8unorm',
sampleCount: 4,
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
const resolveAttachment = t.createTextureTracked({
size: [1, 1, 1],
format: 'rgba8unorm',
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
const depthStencilAttachment = t.createTextureTracked({
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);
});