export const description = `Validation tests for buffer related parameters for buffer <-> texture copies`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { kTextureDimensions } from '../../../capability_info.js';
import { GPUConst } from '../../../constants.js';
import {
getBlockInfoForSizedTextureFormat,
isDepthOrStencilTextureFormat,
kSizedTextureFormats,
textureFormatAndDimensionPossiblyCompatible,
} from '../../../format_info.js';
import { kResourceStates } from '../../../gpu_test.js';
import { kImageCopyTypes } from '../../../util/texture/layout.js';
import * as vtu from '../validation_test_utils.js';
import { ImageCopyTest, formatCopyableWithMethod } from './image_copy.js';
export const g = makeTestGroup(ImageCopyTest);
g.test('buffer_state')
.desc(
`
Test that the buffer must be valid and not destroyed.
- for all buffer <-> texture copy methods
- for various buffer states
`
)
.params(u =>
u
.combine('method', ['CopyB2T', 'CopyT2B'] as const)
.combine('state', kResourceStates)
)
.fn(t => {
const { method, state } = t.params;
const buffer = vtu.createBufferWithState(t, state, {
size: 16,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
});
const submit = state !== 'invalid';
const success = state === 'valid';
const texture = t.createTextureTracked({
size: { width: 2, height: 2, depthOrArrayLayers: 1 },
format: 'rgba8unorm',
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST,
});
t.testBuffer(
buffer,
texture,
{ bytesPerRow: 0 },
{ width: 0, height: 0, depthOrArrayLayers: 0 },
{ dataSize: 16, method, success, submit }
);
});
g.test('buffer,device_mismatch')
.desc('Tests the image copies cannot be called with a buffer created from another device')
.paramsSubcasesOnly(u =>
u.combine('method', ['CopyB2T', 'CopyT2B'] as const).combine('mismatched', [true, false])
)
.beforeAllSubcases(t => t.usesMismatchedDevice())
.fn(t => {
const { method, mismatched } = t.params;
const sourceDevice = mismatched ? t.mismatchedDevice : t.device;
const buffer = t.trackForCleanup(
sourceDevice.createBuffer({
size: 16,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
})
);
const texture = t.createTextureTracked({
size: { width: 2, height: 2, depthOrArrayLayers: 1 },
format: 'rgba8unorm',
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST,
});
const success = !mismatched;
t.testBuffer(
buffer,
texture,
{ bytesPerRow: 0 },
{ width: 0, height: 0, depthOrArrayLayers: 0 },
{ dataSize: 16, method, success, submit: success }
);
});
g.test('usage')
.desc(
`
Test the buffer must have the appropriate COPY_SRC/COPY_DST usage.
TODO update such that it tests
- for all buffer source usages
- for all buffer destination usages
`
)
.params(u =>
u
.combine('method', ['CopyB2T', 'CopyT2B'] as const)
.beginSubcases()
.combine('usage', [
GPUConst.BufferUsage.COPY_SRC | GPUConst.BufferUsage.UNIFORM,
GPUConst.BufferUsage.COPY_DST | GPUConst.BufferUsage.UNIFORM,
GPUConst.BufferUsage.COPY_SRC | GPUConst.BufferUsage.COPY_DST,
])
)
.fn(t => {
const { method, usage } = t.params;
const buffer = t.createBufferTracked({
size: 16,
usage,
});
const success =
method === 'CopyB2T'
? (usage & GPUBufferUsage.COPY_SRC) !== 0
: (usage & GPUBufferUsage.COPY_DST) !== 0;
const texture = t.createTextureTracked({
size: { width: 2, height: 2, depthOrArrayLayers: 1 },
format: 'rgba8unorm',
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST,
});
t.testBuffer(
buffer,
texture,
{ bytesPerRow: 0 },
{ width: 0, height: 0, depthOrArrayLayers: 0 },
{ dataSize: 16, method, success, submit: success }
);
});
g.test('bytes_per_row_alignment')
.desc(
`
Test that bytesPerRow must be a multiple of 256 for CopyB2T and CopyT2B if it is required.
- for all copy methods between linear data and textures
- for all texture dimensions
- for all sized formats.
- for various bytesPerRow aligned to 256 or not
- for various number of blocks rows copied
`
)
.params(u =>
u
.combine('method', kImageCopyTypes)
.combine('format', kSizedTextureFormats)
.filter(formatCopyableWithMethod)
.combine('dimension', kTextureDimensions)
.filter(({ dimension, format }) =>
textureFormatAndDimensionPossiblyCompatible(dimension, format)
)
.beginSubcases()
.combine('bytesPerRow', [undefined, 0, 1, 255, 256, 257, 512])
.combine('copyHeightInBlocks', [0, 1, 2, 3])
.expand('_textureHeightInBlocks', p => [
p.copyHeightInBlocks === 0 ? 1 : p.copyHeightInBlocks,
])
.unless(p => p.dimension === '1d' && p.copyHeightInBlocks > 1)
.unless(p => {
return (
isDepthOrStencilTextureFormat(p.format) &&
p.copyHeightInBlocks !== p._textureHeightInBlocks
);
})
.filter(
({ format, bytesPerRow, copyHeightInBlocks }) =>
(bytesPerRow === undefined && copyHeightInBlocks <= 1) ||
(bytesPerRow !== undefined &&
bytesPerRow >= getBlockInfoForSizedTextureFormat(format).bytesPerBlock)
)
)
.fn(t => {
const { method, dimension, format, bytesPerRow, copyHeightInBlocks, _textureHeightInBlocks } =
t.params;
t.skipIfTextureFormatNotSupported(format);
t.skipIfTextureFormatAndDimensionNotCompatible(format, dimension);
const info = getBlockInfoForSizedTextureFormat(format);
const buffer = t.createBufferTracked({
size: 512 * 8 * 16,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
});
let success = false;
if (method === 'WriteTexture') success = true;
if (copyHeightInBlocks <= 1 && bytesPerRow === undefined) success = true;
if (bytesPerRow !== undefined && bytesPerRow > 0 && bytesPerRow % 256 === 0) success = true;
const size = [info.blockWidth, _textureHeightInBlocks * info.blockHeight, 1];
const texture = t.createTextureTracked({
size,
dimension,
format,
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST,
});
const copySize = [info.blockWidth, copyHeightInBlocks * info.blockHeight, 1];
t.testBuffer(buffer, texture, { bytesPerRow }, copySize, {
dataSize: 512 * 8 * 16,
method,
success,
submit: success,
});
});