blob: 47de480aa3d46b3bbd9b46f3ebe166db0466b52f [file] [log] [blame]
// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "tests/unittests/validation/ValidationTest.h"
class CopyCommandTest : public ValidationTest {
protected:
nxt::Buffer CreateFrozenBuffer(uint32_t size, nxt::BufferUsageBit usage) {
nxt::Buffer buf = AssertWillBeSuccess(device.CreateBufferBuilder())
.SetSize(size)
.SetAllowedUsage(usage)
.GetResult();
buf.FreezeUsage(usage);
return buf;
}
nxt::Texture CreateFrozen2DTexture(uint32_t width, uint32_t height, uint32_t levels,
nxt::TextureFormat format, nxt::TextureUsageBit usage) {
nxt::Texture tex = AssertWillBeSuccess(device.CreateTextureBuilder())
.SetDimension(nxt::TextureDimension::e2D)
.SetExtent(width, height, 1)
.SetFormat(format)
.SetMipLevels(levels)
.SetAllowedUsage(usage)
.GetResult();
tex.FreezeUsage(usage);
return tex;
}
};
class CopyCommandTest_B2B : public CopyCommandTest {
};
// TODO(cwallez@chromium.org): Test that copies are forbidden inside renderpasses
// Test a successfull B2B copy
TEST_F(CopyCommandTest_B2B, Success) {
nxt::Buffer source = CreateFrozenBuffer(16, nxt::BufferUsageBit::TransferSrc);
nxt::Buffer destination = CreateFrozenBuffer(16, nxt::BufferUsageBit::TransferDst);
// Copy different copies, including some that touch the OOB condition
{
nxt::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
.CopyBufferToBuffer(source, 0, destination, 0, 16)
.CopyBufferToBuffer(source, 8, destination, 0, 8)
.CopyBufferToBuffer(source, 0, destination, 8, 8)
.GetResult();
}
// Empty copies are valid
{
nxt::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
.CopyBufferToBuffer(source, 0, destination, 0, 0)
.CopyBufferToBuffer(source, 0, destination, 16, 0)
.CopyBufferToBuffer(source, 16, destination, 0, 0)
.GetResult();
}
}
// Test B2B copies with OOB
TEST_F(CopyCommandTest_B2B, OutOfBounds) {
nxt::Buffer source = CreateFrozenBuffer(16, nxt::BufferUsageBit::TransferSrc);
nxt::Buffer destination = CreateFrozenBuffer(16, nxt::BufferUsageBit::TransferDst);
// OOB on the source
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToBuffer(source, 8, destination, 0, 12)
.GetResult();
}
// OOB on the destination
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToBuffer(source, 0, destination, 8, 12)
.GetResult();
}
}
// Test B2B copies with incorrect buffer usage
TEST_F(CopyCommandTest_B2B, BadUsage) {
nxt::Buffer source = CreateFrozenBuffer(16, nxt::BufferUsageBit::TransferSrc);
nxt::Buffer destination = CreateFrozenBuffer(16, nxt::BufferUsageBit::TransferDst);
nxt::Buffer vertex = CreateFrozenBuffer(16, nxt::BufferUsageBit::Vertex);
// Source with incorrect usage
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToBuffer(vertex, 0, destination, 0, 16)
.GetResult();
}
// Destination with incorrect usage
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToBuffer(source, 0, vertex, 0, 16)
.GetResult();
}
}
class CopyCommandTest_B2T : public CopyCommandTest {
};
// Test a successfull B2T copy
TEST_F(CopyCommandTest_B2T, Success) {
nxt::Buffer source = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferSrc);
nxt::Texture destination = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferDst);
// Different copies, including some that touch the OOB condition
{
nxt::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
// Copy 4x4 block in corner of first mip.
.CopyBufferToTexture(source, 0, destination, 0, 0, 0, 4, 4, 1, 0)
// Copy 4x4 block in opposite corner of first mip.
.CopyBufferToTexture(source, 0, destination, 12, 12, 0, 4, 4, 1, 0)
// Copy 4x4 block in the 4x4 mip.
.CopyBufferToTexture(source, 0, destination, 0, 0, 0, 4, 4, 1, 2)
// Copy with a buffer offset
.CopyBufferToTexture(source, 15 * 4, destination, 0, 0, 0, 1, 1, 1, 4)
.GetResult();
}
// Empty copies are valid
{
nxt::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
// An empty copy
.CopyBufferToTexture(source, 0, destination, 0, 0, 0, 0, 0, 1, 0)
// An empty copy touching the end of the buffer
.CopyBufferToTexture(source, 16 * 4, destination, 0, 0, 0, 0, 0, 1, 0)
// An empty copy touching the side of the texture
.CopyBufferToTexture(source, 0, destination, 16, 16, 0, 0, 0, 1, 0)
.GetResult();
}
}
// Test OOB conditions on the buffer
TEST_F(CopyCommandTest_B2T, OutOfBoundsOnBuffer) {
nxt::Buffer source = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferSrc);
nxt::Texture destination = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferDst);
// OOB on the buffer because we copy too many pixels
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 0, destination, 0, 0, 0, 4, 5, 1, 0)
.GetResult();
}
// OOB on the buffer because of the offset
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 1, destination, 0, 0, 0, 4, 4, 1, 0)
.GetResult();
}
}
// Test OOB conditions on the texture
TEST_F(CopyCommandTest_B2T, OutOfBoundsOnTexture) {
nxt::Buffer source = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferSrc);
nxt::Texture destination = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferDst);
// OOB on the texture because x + width overflows
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 0, destination, 13, 12, 0, 4, 4, 1, 0)
.GetResult();
}
// OOB on the texture because y + width overflows
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 0, destination, 12, 13, 0, 4, 4, 1, 0)
.GetResult();
}
// OOB on the texture because we overflow a non-zero mip
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 0, destination, 1, 0, 0, 4, 4, 1, 2)
.GetResult();
}
// OOB on the texture even on an empty copy when we copy to a non-existent mip.
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 0, destination, 0, 0, 0, 0, 0, 1, 5)
.GetResult();
}
}
// Test that we force Z=0 and Depth=1 on copies to 2D textures
TEST_F(CopyCommandTest_B2T, ZDepthConstraintFor2DTextures) {
nxt::Buffer source = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferSrc);
nxt::Texture destination = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferDst);
// Z=1 on an empty copy still errors
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 0, destination, 0, 0, 1, 0, 0, 1, 0)
.GetResult();
}
// Depth=0 on an empty copy still errors
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 0, destination, 0, 0, 0, 0, 0, 0, 0)
.GetResult();
}
}
// Test B2B copies with incorrect buffer usage
TEST_F(CopyCommandTest_B2T, IncorrectUsage) {
nxt::Buffer source = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferSrc);
nxt::Buffer vertex = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::Vertex);
nxt::Texture destination = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferDst);
nxt::Texture sampled = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::Sampled);
// Incorrect source usage
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(vertex, 0, destination, 0, 0, 0, 4, 4, 1, 0)
.GetResult();
}
// Incorrect destination usage
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyBufferToTexture(source, 0, sampled, 0, 0, 0, 4, 4, 1, 0)
.GetResult();
}
}
class CopyCommandTest_T2B : public CopyCommandTest {
};
// Test a successfull T2B copy
TEST_F(CopyCommandTest_T2B, Success) {
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferSrc);
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
// Different copies, including some that touch the OOB condition
{
nxt::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
// Copy from 4x4 block in corner of first mip.
.CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, destination, 0)
// Copy from 4x4 block in opposite corner of first mip.
.CopyTextureToBuffer(source, 12, 12, 0, 4, 4, 1, 0, destination, 0)
// Copy from 4x4 block in the 4x4 mip.
.CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 2, destination, 0)
// Copy with a buffer offset
.CopyTextureToBuffer(source, 0, 0, 0, 1, 1, 1, 4, destination, 15 * 4)
.GetResult();
}
// Empty copies are valid
{
nxt::CommandBuffer commands = AssertWillBeSuccess(device.CreateCommandBufferBuilder())
// An empty copy
.CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 0, destination, 0)
// An empty copy touching the end of the buffer
.CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 0, destination, 16 * 4)
// An empty copy touching the side of the texture
.CopyTextureToBuffer(source, 16, 16, 0, 0, 0, 1, 0, destination, 0)
.GetResult();
}
}
// Test OOB conditions on the texture
TEST_F(CopyCommandTest_T2B, OutOfBoundsOnTexture) {
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferSrc);
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
// OOB on the texture because x + width overflows
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 13, 12, 0, 4, 4, 1, 0, destination, 0)
.GetResult();
}
// OOB on the texture because y + width overflows
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 12, 13, 0, 4, 4, 1, 0, destination, 0)
.GetResult();
}
// OOB on the texture because we overflow a non-zero mip
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 1, 0, 0, 4, 4, 1, 2, destination, 0)
.GetResult();
}
// OOB on the texture even on an empty copy when we copy from a non-existent mip.
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 1, 5, destination, 0)
.GetResult();
}
}
// Test OOB conditions on the buffer
TEST_F(CopyCommandTest_T2B, OutOfBoundsOnBuffer) {
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferSrc);
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
// OOB on the buffer because we copy too many pixels
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 0, 0, 0, 4, 5, 1, 0, destination, 0)
.GetResult();
}
// OOB on the buffer because of the offset
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, destination, 1)
.GetResult();
}
}
// Test that we force Z=0 and Depth=1 on copies from to 2D textures
TEST_F(CopyCommandTest_T2B, ZDepthConstraintFor2DTextures) {
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferSrc);
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
// Z=1 on an empty copy still errors
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 0, 0, 1, 0, 0, 1, 0, destination, 0)
.GetResult();
}
// Depth=0 on an empty copy still errors
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 0, 0, 0, 0, 0, 0, 0, destination, 0)
.GetResult();
}
}
// Test B2B copies with incorrect buffer usage
TEST_F(CopyCommandTest_T2B, IncorrectUsage) {
nxt::Texture source = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::TransferSrc);
nxt::Texture sampled = CreateFrozen2DTexture(16, 16, 5, nxt::TextureFormat::R8G8B8A8Unorm,
nxt::TextureUsageBit::Sampled);
nxt::Buffer destination = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::TransferDst);
nxt::Buffer vertex = CreateFrozenBuffer(16 * 4, nxt::BufferUsageBit::Vertex);
// Incorrect source usage
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(sampled, 0, 0, 0, 4, 4, 1, 0, destination, 0)
.GetResult();
}
// Incorrect destination usage
{
nxt::CommandBuffer commands = AssertWillBeError(device.CreateCommandBufferBuilder())
.CopyTextureToBuffer(source, 0, 0, 0, 4, 4, 1, 0, vertex, 0)
.GetResult();
}
}