// Copyright 2017 The Dawn 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 "common/Constants.h" #include "common/Math.h" #include "tests/unittests/validation/ValidationTest.h" #include "utils/DawnHelpers.h" class CopyCommandTest : public ValidationTest { protected: dawn::Buffer CreateBuffer(uint32_t size, dawn::BufferUsageBit usage) { dawn::BufferDescriptor descriptor; descriptor.size = size; descriptor.usage = usage; return device.CreateBuffer(&descriptor); } dawn::Texture Create2DTexture(uint32_t width, uint32_t height, uint32_t mipLevelCount, uint32_t arrayLayerCount, dawn::TextureFormat format, dawn::TextureUsageBit usage, uint32_t sampleCount = 1) { dawn::TextureDescriptor descriptor; descriptor.dimension = dawn::TextureDimension::e2D; descriptor.size.width = width; descriptor.size.height = height; descriptor.size.depth = 1; descriptor.arrayLayerCount = arrayLayerCount; descriptor.sampleCount = sampleCount; descriptor.format = format; descriptor.mipLevelCount = mipLevelCount; descriptor.usage = usage; dawn::Texture tex = device.CreateTexture(&descriptor); return tex; } uint32_t BufferSizeForTextureCopy(uint32_t width, uint32_t height, uint32_t depth) { uint32_t rowPitch = Align(width * 4, kTextureRowPitchAlignment); return (rowPitch * (height - 1) + width) * depth; } void TestB2TCopy(utils::Expectation expectation, dawn::Buffer srcBuffer, uint32_t srcOffset, uint32_t srcRowPitch, uint32_t srcImageHeight, dawn::Texture destTexture, uint32_t destLevel, uint32_t destSlice, dawn::Origin3D destOrigin, dawn::Extent3D extent3D) { dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(srcBuffer, srcOffset, srcRowPitch, srcImageHeight); dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(destTexture, destLevel, destSlice, destOrigin); dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D); if (expectation == utils::Expectation::Success) { encoder.Finish(); } else { ASSERT_DEVICE_ERROR(encoder.Finish()); } } void TestT2BCopy(utils::Expectation expectation, dawn::Texture srcTexture, uint32_t srcLevel, uint32_t srcSlice, dawn::Origin3D srcOrigin, dawn::Buffer destBuffer, uint32_t destOffset, uint32_t destRowPitch, uint32_t destImageHeight, dawn::Extent3D extent3D) { dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(destBuffer, destOffset, destRowPitch, destImageHeight); dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(srcTexture, srcLevel, srcSlice, srcOrigin); dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D); if (expectation == utils::Expectation::Success) { encoder.Finish(); } else { ASSERT_DEVICE_ERROR(encoder.Finish()); } } }; 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) { dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); // Copy different copies, including some that touch the OOB condition { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 0, destination, 0, 16); encoder.CopyBufferToBuffer(source, 8, destination, 0, 8); encoder.CopyBufferToBuffer(source, 0, destination, 8, 8); encoder.Finish(); } // Empty copies are valid { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 0, destination, 0, 0); encoder.CopyBufferToBuffer(source, 0, destination, 16, 0); encoder.CopyBufferToBuffer(source, 16, destination, 0, 0); encoder.Finish(); } } // Test B2B copies with OOB TEST_F(CopyCommandTest_B2B, OutOfBounds) { dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); // OOB on the source { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 8, destination, 0, 12); ASSERT_DEVICE_ERROR(encoder.Finish()); } // OOB on the destination { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 0, destination, 8, 12); ASSERT_DEVICE_ERROR(encoder.Finish()); } } // Test B2B copies with incorrect buffer usage TEST_F(CopyCommandTest_B2B, BadUsage) { dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); dawn::Buffer vertex = CreateBuffer(16, dawn::BufferUsageBit::Vertex); // Source with incorrect usage { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(vertex, 0, destination, 0, 16); ASSERT_DEVICE_ERROR(encoder.Finish()); } // Destination with incorrect usage { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 0, vertex, 0, 16); ASSERT_DEVICE_ERROR(encoder.Finish()); } } // Test B2B copies with unaligned data size TEST_F(CopyCommandTest_B2B, UnalignedSize) { dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 8, destination, 0, sizeof(uint8_t)); ASSERT_DEVICE_ERROR(encoder.Finish()); } // Test B2B copies with unaligned offset TEST_F(CopyCommandTest_B2B, UnalignedOffset) { dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); // Unaligned source offset { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 9, destination, 0, 4); ASSERT_DEVICE_ERROR(encoder.Finish()); } // Unaligned destination offset { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 8, destination, 1, 4); ASSERT_DEVICE_ERROR(encoder.Finish()); } } // Test B2B copies with buffers in error state cause errors. TEST_F(CopyCommandTest_B2B, BuffersInErrorState) { dawn::BufferDescriptor errorBufferDescriptor; errorBufferDescriptor.size = 4; errorBufferDescriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferSrc; ASSERT_DEVICE_ERROR(dawn::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); constexpr uint32_t bufferSize = 4; dawn::Buffer validBuffer = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(errorBuffer, 0, validBuffer, 0, 4); ASSERT_DEVICE_ERROR(encoder.Finish()); } { dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(validBuffer, 0, errorBuffer, 0, 4); ASSERT_DEVICE_ERROR(encoder.Finish()); } } class CopyCommandTest_B2T : public CopyCommandTest { }; // Test a successfull B2T copy TEST_F(CopyCommandTest_B2T, Success) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); // Different copies, including some that touch the OOB condition { // Copy 4x4 block in corner of first mip. TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {4, 4, 1}); // Copy 4x4 block in opposite corner of first mip. TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {12, 12, 0}, {4, 4, 1}); // Copy 4x4 block in the 4x4 mip. TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 2, 0, {0, 0, 0}, {4, 4, 1}); // Copy with a buffer offset TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 0, destination, 0, 0, {0, 0, 0}, {1, 1, 1}); } // Copies with a 256-byte aligned row pitch but unaligned texture region { // Unaligned region TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {3, 4, 1}); // Unaligned region with texture offset TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {5, 7, 0}, {2, 3, 1}); // Unaligned region, with buffer offset TestB2TCopy(utils::Expectation::Success, source, 31 * 4, 256, 0, destination, 0, 0, {0, 0, 0}, {3, 3, 1}); } // Empty copies are valid { // An empty copy TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, 0, {0, 0, 0}, {0, 0, 1}); // An empty copy touching the end of the buffer TestB2TCopy(utils::Expectation::Success, source, bufferSize, 0, 0, destination, 0, 0, {0, 0, 0}, {0, 0, 1}); // An empty copy touching the side of the texture TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, 0, {16, 16, 0}, {0, 0, 1}); } } // Test OOB conditions on the buffer TEST_F(CopyCommandTest_B2T, OutOfBoundsOnBuffer) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); // OOB on the buffer because we copy too many pixels TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {4, 5, 1}); // OOB on the buffer because of the offset TestB2TCopy(utils::Expectation::Failure, source, 4, 256, 0, destination, 0, 0, {0, 0, 0}, {4, 4, 1}); // OOB on the buffer because (row pitch * (height - 1) + width) * depth overflows TestB2TCopy(utils::Expectation::Failure, source, 0, 512, 0, destination, 0, 0, {0, 0, 0}, {4, 3, 1}); // Not OOB on the buffer although row pitch * height overflows // but (row pitch * (height - 1) + width) * depth does not overlow { uint32_t sourceBufferSize = BufferSizeForTextureCopy(7, 3, 1); ASSERT_TRUE(256 * 3 > sourceBufferSize) << "row pitch * height should overflow buffer"; dawn::Buffer sourceBuffer = CreateBuffer(sourceBufferSize, dawn::BufferUsageBit::TransferSrc); TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {7, 3, 1}); } } // Test OOB conditions on the texture TEST_F(CopyCommandTest_B2T, OutOfBoundsOnTexture) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); // OOB on the texture because x + width overflows TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {13, 12, 0}, {4, 4, 1}); // OOB on the texture because y + width overflows TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {12, 13, 0}, {4, 4, 1}); // OOB on the texture because we overflow a non-zero mip TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 2, 0, {1, 0, 0}, {4, 4, 1}); // OOB on the texture even on an empty copy when we copy to a non-existent mip. TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 5, 0, {0, 0, 0}, {0, 0, 1}); // OOB on the texture because slice overflows TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 2, {0, 0, 0}, {0, 0, 1}); } // Test that we force Z=0 and Depth=1 on copies to 2D textures TEST_F(CopyCommandTest_B2T, ZDepthConstraintFor2DTextures) { dawn::Buffer source = CreateBuffer(16 * 4, dawn::BufferUsageBit::TransferSrc); dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); // Z=1 on an empty copy still errors TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 1}, {0, 0, 1}); // Depth=0 on an empty copy still errors TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 0}, {0, 0, 0}); } // Test B2T copies with incorrect buffer usage TEST_F(CopyCommandTest_B2T, IncorrectUsage) { dawn::Buffer source = CreateBuffer(16 * 4, dawn::BufferUsageBit::TransferSrc); dawn::Buffer vertex = CreateBuffer(16 * 4, dawn::BufferUsageBit::Vertex); dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); dawn::Texture sampled = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::Sampled); // Incorrect source usage TestB2TCopy(utils::Expectation::Failure, vertex, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {4, 4, 1}); // Incorrect destination usage TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, sampled, 0, 0, {0, 0, 0}, {4, 4, 1}); } TEST_F(CopyCommandTest_B2T, IncorrectRowPitch) { uint32_t bufferSize = BufferSizeForTextureCopy(128, 16, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::Texture destination = Create2DTexture(128, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); // Default row pitch is not 256-byte aligned TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 0}, {3, 4, 1}); // Row pitch is not 256-byte aligned TestB2TCopy(utils::Expectation::Failure, source, 0, 128, 0, destination, 0, 0, {0, 0, 0}, {4, 4, 1}); // Row pitch is less than width * bytesPerPixel TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {65, 1, 1}); } TEST_F(CopyCommandTest_B2T, ImageHeightConstraint) { uint32_t bufferSize = BufferSizeForTextureCopy(5, 5, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::Texture destination = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); // Image height is zero (Valid) TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {4, 4, 1}); // Image height is equal to copy height (Valid) TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {4, 4, 1}); // Image height is larger than copy height (Valid) TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 0, 0, {0, 0, 0}, {4, 4, 1}); // Image height is less than copy height (Invalid) TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 3, destination, 0, 0, {0, 0, 0}, {4, 4, 1}); } // Test B2T copies with incorrect buffer offset usage TEST_F(CopyCommandTest_B2T, IncorrectBufferOffset) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); // Correct usage TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 0, destination, 0, 0, {0, 0, 0}, {1, 1, 1}); // Incorrect usages { TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 5, 256, 0, destination, 0, 0, {0, 0, 0}, {1, 1, 1}); TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 6, 256, 0, destination, 0, 0, {0, 0, 0}, {1, 1, 1}); TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 7, 256, 0, destination, 0, 0, {0, 0, 0}, {1, 1, 1}); } } // Test multisampled textures cannot be used in B2T copies. TEST_F(CopyCommandTest_B2T, CopyToMultisampledTexture) { uint32_t bufferSize = BufferSizeForTextureCopy(16, 16, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::Texture destination = Create2DTexture(2, 2, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst, 4); TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, {2, 2, 1}); } // Test B2T copies with buffer or texture in error state causes errors. TEST_F(CopyCommandTest_B2T, BufferOrTextureInErrorState) { dawn::BufferDescriptor errorBufferDescriptor; errorBufferDescriptor.size = 4; errorBufferDescriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferSrc; ASSERT_DEVICE_ERROR(dawn::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); dawn::TextureDescriptor errorTextureDescriptor; errorTextureDescriptor.arrayLayerCount = 0; ASSERT_DEVICE_ERROR(dawn::Texture errorTexture = device.CreateTexture(&errorTextureDescriptor)); dawn::BufferCopyView errorBufferCopyView = utils::CreateBufferCopyView(errorBuffer, 0, 0, 0); dawn::TextureCopyView errorTextureCopyView = utils::CreateTextureCopyView(errorTexture, 0, 0, {1, 1, 1}); dawn::Extent3D extent3D = {1, 1, 1}; { dawn::Texture destination = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(destination, 0, 0, {1, 1, 1}); dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&errorBufferCopyView, &textureCopyView, &extent3D); ASSERT_DEVICE_ERROR(encoder.Finish()); } { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(source, 0, 0, 0); dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &errorTextureCopyView, &extent3D); ASSERT_DEVICE_ERROR(encoder.Finish()); } } class CopyCommandTest_T2B : public CopyCommandTest { }; // Test a successfull T2B copy TEST_F(CopyCommandTest_T2B, Success) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); // Different copies, including some that touch the OOB condition { // Copy from 4x4 block in corner of first mip. TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // Copy from 4x4 block in opposite corner of first mip. TestT2BCopy(utils::Expectation::Success, source, 0, 0, {12, 12, 0}, destination, 0, 256, 0, {4, 4, 1}); // Copy from 4x4 block in the 4x4 mip. TestT2BCopy(utils::Expectation::Success, source, 2, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // Copy with a buffer offset TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, bufferSize - 4, 256, 0, {1, 1, 1}); } // Copies with a 256-byte aligned row pitch but unaligned texture region { // Unaligned region TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, {3, 4, 1}); // Unaligned region with texture offset TestT2BCopy(utils::Expectation::Success, source, 0, 0, {5, 7, 0}, destination, 0, 256, 0, {2, 3, 1}); // Unaligned region, with buffer offset TestT2BCopy(utils::Expectation::Success, source, 2, 0, {0, 0, 0}, destination, 31 * 4, 256, 0, {3, 3, 1}); } // Empty copies are valid { // An empty copy TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, 0, {0, 0, 1}); // An empty copy touching the end of the buffer TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, bufferSize, 0, 0, {0, 0, 1}); // An empty copy touching the side of the texture TestT2BCopy(utils::Expectation::Success, source, 0, 0, {16, 16, 0}, destination, 0, 0, 0, {0, 0, 1}); } } // Test OOB conditions on the texture TEST_F(CopyCommandTest_T2B, OutOfBoundsOnTexture) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); // OOB on the texture because x + width overflows TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {13, 12, 0}, destination, 0, 256, 0, {4, 4, 1}); // OOB on the texture because y + width overflows TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {12, 13, 0}, destination, 0, 256, 0, {4, 4, 1}); // OOB on the texture because we overflow a non-zero mip TestT2BCopy(utils::Expectation::Failure, source, 2, 0, {1, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // OOB on the texture even on an empty copy when we copy from a non-existent mip. TestT2BCopy(utils::Expectation::Failure, source, 5, 0, {0, 0, 0}, destination, 0, 0, 0, {0, 0, 1}); } // Test OOB conditions on the buffer TEST_F(CopyCommandTest_T2B, OutOfBoundsOnBuffer) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); // OOB on the buffer because we copy too many pixels TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 5, 1}); // OOB on the buffer because of the offset TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 4, 256, 0, {4, 4, 1}); // OOB on the buffer because (row pitch * (height - 1) + width) * depth overflows TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 512, 0, {4, 3, 1}); // Not OOB on the buffer although row pitch * height overflows // but (row pitch * (height - 1) + width) * depth does not overlow { uint32_t destinationBufferSize = BufferSizeForTextureCopy(7, 3, 1); ASSERT_TRUE(256 * 3 > destinationBufferSize) << "row pitch * height should overflow buffer"; dawn::Buffer destinationBuffer = CreateBuffer(destinationBufferSize, dawn::BufferUsageBit::TransferDst); TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destinationBuffer, 0, 256, 0, {7, 3, 1}); } } // Test that we force Z=0 and Depth=1 on copies from to 2D textures TEST_F(CopyCommandTest_T2B, ZDepthConstraintFor2DTextures) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); // Z=1 on an empty copy still errors TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 1}, destination, 0, 0, 0, {0, 0, 1}); // Depth=0 on an empty copy still errors TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, 0, {0, 0, 0}); } // Test T2B copies with incorrect buffer usage TEST_F(CopyCommandTest_T2B, IncorrectUsage) { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc); dawn::Texture sampled = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::Sampled); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); dawn::Buffer vertex = CreateBuffer(bufferSize, dawn::BufferUsageBit::Vertex); // Incorrect source usage TestT2BCopy(utils::Expectation::Failure, sampled, 0, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // Incorrect destination usage TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, vertex, 0, 256, 0, {4, 4, 1}); } TEST_F(CopyCommandTest_T2B, IncorrectRowPitch) { uint32_t bufferSize = BufferSizeForTextureCopy(128, 16, 1); dawn::Texture source = Create2DTexture(128, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); // Default row pitch is not 256-byte aligned TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, {3, 4, 1}); // Row pitch is not 256-byte aligned TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 257, 0, {4, 4, 1}); // Row pitch is less than width * bytesPerPixel TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, {65, 1, 1}); } TEST_F(CopyCommandTest_T2B, ImageHeightConstraint) { uint32_t bufferSize = BufferSizeForTextureCopy(5, 5, 1); dawn::Texture source = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); // Image height is zero (Valid) TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // Image height is equal to copy height (Valid) TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 4, {4, 4, 1}); // Image height exceeds copy height (Valid) TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 5, {4, 4, 1}); // Image height is less than copy height (Invalid) TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 3, {4, 4, 1}); } // Test T2B copies with incorrect buffer offset usage TEST_F(CopyCommandTest_T2B, IncorrectBufferOffset) { uint32_t bufferSize = BufferSizeForTextureCopy(128, 16, 1); dawn::Texture source = Create2DTexture(128, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); // Correct usage TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, bufferSize - 4, 256, 0, {1, 1, 1}); // Incorrect usages TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, bufferSize - 5, 256, 0, {1, 1, 1}); TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, bufferSize - 6, 256, 0, {1, 1, 1}); TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, bufferSize - 7, 256, 0, {1, 1, 1}); } // Test multisampled textures cannot be used in T2B copies. TEST_F(CopyCommandTest_T2B, CopyFromMultisampledTexture) { dawn::Texture source = Create2DTexture(2, 2, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc, 4); uint32_t bufferSize = BufferSizeForTextureCopy(16, 16, 1); dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, {2, 2, 1}); } // Test T2B copies with buffer or texture in error state cause errors. TEST_F(CopyCommandTest_T2B, BufferOrTextureInErrorState) { dawn::BufferDescriptor errorBufferDescriptor; errorBufferDescriptor.size = 4; errorBufferDescriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferSrc; ASSERT_DEVICE_ERROR(dawn::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); dawn::TextureDescriptor errorTextureDescriptor; errorTextureDescriptor.arrayLayerCount = 0; ASSERT_DEVICE_ERROR(dawn::Texture errorTexture = device.CreateTexture(&errorTextureDescriptor)); dawn::BufferCopyView errorBufferCopyView = utils::CreateBufferCopyView(errorBuffer, 0, 0, 0); dawn::TextureCopyView errorTextureCopyView = utils::CreateTextureCopyView(errorTexture, 0, 0, {1, 1, 1}); dawn::Extent3D extent3D = {1, 1, 1}; { uint32_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(source, 0, 0, 0); dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&errorTextureCopyView, &bufferCopyView, &extent3D); ASSERT_DEVICE_ERROR(encoder.Finish()); } { dawn::Texture destination = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst); dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(destination, 0, 0, {1, 1, 1}); dawn::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&textureCopyView, &errorBufferCopyView, &extent3D); ASSERT_DEVICE_ERROR(encoder.Finish()); } }