diff --git a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp index 831b934d0e..1cd9e281ed 100644 --- a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp +++ b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp @@ -16,6 +16,7 @@ #include "common/Constants.h" #include "common/Math.h" #include "tests/unittests/validation/ValidationTest.h" +#include "utils/TextureFormatUtils.h" #include "utils/WGPUHelpers.h" class CopyCommandTest : public ValidationTest { @@ -53,7 +54,7 @@ class CopyCommandTest : public ValidationTest { uint32_t height, uint32_t depth, wgpu::TextureFormat format = wgpu::TextureFormat::RGBA8Unorm) { - uint32_t bytesPerPixel = utils::TextureFormatPixelSize(format); + uint32_t bytesPerPixel = utils::GetTexelBlockSizeInBytes(format); uint32_t bytesPerRow = Align(width * bytesPerPixel, kTextureBytesPerRowAlignment); return (bytesPerRow * (height - 1) + width * bytesPerPixel) * depth; } @@ -124,6 +125,55 @@ class CopyCommandTest : public ValidationTest { ValidateExpectation(encoder, expectation); } + + void TestBothTBCopies(utils::Expectation expectation, + wgpu::Buffer buffer, + uint64_t bufferOffset, + uint32_t bufferBytesPerRow, + uint32_t rowsPerImage, + wgpu::Texture texture, + uint32_t level, + wgpu::Origin3D origin, + wgpu::Extent3D extent3D) { + TestB2TCopy(expectation, buffer, bufferOffset, bufferBytesPerRow, rowsPerImage, texture, + level, origin, extent3D); + TestT2BCopy(expectation, texture, level, origin, buffer, bufferOffset, bufferBytesPerRow, + rowsPerImage, extent3D); + } + + void TestBothT2TCopies(utils::Expectation expectation, + wgpu::Texture texture1, + uint32_t level1, + wgpu::Origin3D origin1, + wgpu::Texture texture2, + uint32_t level2, + wgpu::Origin3D origin2, + wgpu::Extent3D extent3D) { + TestT2TCopy(expectation, texture1, level1, origin1, texture2, level2, origin2, extent3D); + TestT2TCopy(expectation, texture2, level2, origin2, texture1, level1, origin1, extent3D); + } + + void TestBothTBCopiesExactBufferSize(uint32_t bufferBytesPerRow, + uint32_t rowsPerImage, + wgpu::Texture texture, + wgpu::TextureFormat textureFormat, + wgpu::Origin3D origin, + wgpu::Extent3D extent3D) { + // Check the minimal valid bufferSize. + uint64_t bufferSize = + utils::RequiredBytesInCopy(bufferBytesPerRow, rowsPerImage, extent3D, textureFormat); + wgpu::Buffer source = + CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + TestBothTBCopies(utils::Expectation::Success, source, 0, bufferBytesPerRow, rowsPerImage, + texture, 0, origin, extent3D); + + // Check bufferSize was indeed minimal. + uint64_t invalidSize = bufferSize - 1; + wgpu::Buffer invalidSource = + CreateBuffer(invalidSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + TestBothTBCopies(utils::Expectation::Failure, invalidSource, 0, bufferBytesPerRow, + rowsPerImage, texture, 0, origin, extent3D); + } }; class CopyCommandTest_B2B : public CopyCommandTest {}; @@ -1344,33 +1394,6 @@ class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest { kUsage, 1); } - void TestBothTBCopies(utils::Expectation expectation, - wgpu::Buffer buffer, - uint64_t bufferOffset, - uint32_t bufferBytesPerRow, - uint32_t rowsPerImage, - wgpu::Texture texture, - uint32_t level, - wgpu::Origin3D origin, - wgpu::Extent3D extent3D) { - TestB2TCopy(expectation, buffer, bufferOffset, bufferBytesPerRow, rowsPerImage, texture, - level, origin, extent3D); - TestT2BCopy(expectation, texture, level, origin, buffer, bufferOffset, bufferBytesPerRow, - rowsPerImage, extent3D); - } - - void TestBothT2TCopies(utils::Expectation expectation, - wgpu::Texture texture1, - uint32_t level1, - wgpu::Origin3D origin1, - wgpu::Texture texture2, - uint32_t level2, - wgpu::Origin3D origin2, - wgpu::Extent3D extent3D) { - TestT2TCopy(expectation, texture1, level1, origin1, texture2, level2, origin2, extent3D); - TestT2TCopy(expectation, texture2, level2, origin2, texture1, level1, origin1, extent3D); - } - static constexpr uint32_t kWidth = 16; static constexpr uint32_t kHeight = 16; }; @@ -1386,14 +1409,14 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, BufferOffset) { // Valid usages of BufferOffset in B2T and T2B copies with compressed texture formats. { - uint32_t validBufferOffset = utils::CompressedFormatBlockSizeInBytes(bcFormat); + uint32_t validBufferOffset = utils::GetTexelBlockSizeInBytes(bcFormat); TestBothTBCopies(utils::Expectation::Success, buffer, validBufferOffset, 256, 4, texture, 0, {0, 0, 0}, {4, 4, 1}); } // Failures on invalid bufferOffset. { - uint32_t kInvalidBufferOffset = utils::CompressedFormatBlockSizeInBytes(bcFormat) / 2; + uint32_t kInvalidBufferOffset = utils::GetTexelBlockSizeInBytes(bcFormat) / 2; TestBothTBCopies(utils::Expectation::Failure, buffer, kInvalidBufferOffset, 256, 4, texture, 0, {0, 0, 0}, {4, 4, 1}); } @@ -1426,7 +1449,7 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, BytesPerRow) { for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight); uint32_t inValidBytesPerRow = - kTestWidth / 4 * utils::CompressedFormatBlockSizeInBytes(bcFormat); + kTestWidth / 4 * utils::GetTexelBlockSizeInBytes(bcFormat); ASSERT_NE(0u, inValidBytesPerRow % 256); TestBothTBCopies(utils::Expectation::Failure, buffer, 0, inValidBytesPerRow, 4, texture, 0, {0, 0, 0}, {kTestWidth, 4, 1}); @@ -1438,7 +1461,7 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, BytesPerRow) { for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight); uint32_t smallestValidBytesPerRow = - Align(kTestWidth / 4 * utils::CompressedFormatBlockSizeInBytes(bcFormat), 256); + Align(kTestWidth / 4 * utils::GetTexelBlockSizeInBytes(bcFormat), 256); TestBothTBCopies(utils::Expectation::Success, buffer, 0, smallestValidBytesPerRow, 4, texture, 0, {0, 0, 0}, {kTestWidth, 4, 1}); } @@ -1574,3 +1597,66 @@ TEST_F(CopyCommandTest_CompressedTextureFormats, ImageExtent) { } } } + +// Test copies between buffer and multiple array layers of an uncompressed texture +TEST_F(CopyCommandTest, CopyToMultipleArrayLayers) { + wgpu::Texture destination = + CopyCommandTest::Create2DTexture(4, 2, 1, 5, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); + + // Copy to all array layers + TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 0}, + {4, 2, 5}); + + // Copy to the highest array layer + TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 4}, + {4, 2, 1}); + + // Copy to array layers in the middle + TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 1}, + {4, 2, 3}); + + // Copy with a non-packed rowsPerImage + TestBothTBCopiesExactBufferSize(256, 3, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 0}, + {4, 2, 5}); + + // Copy with bytesPerRow = 512 + TestBothTBCopiesExactBufferSize(512, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 1}, + {4, 2, 3}); +} + +// Test copies between buffer and multiple array layers of a compressed texture +TEST_F(CopyCommandTest_CompressedTextureFormats, CopyToMultipleArrayLayers) { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = CopyCommandTest::Create2DTexture( + 12, 16, 1, 20, bcFormat, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); + + // Copy to all array layers + TestBothTBCopiesExactBufferSize(256, 16, texture, bcFormat, {0, 0, 0}, {12, 16, 20}); + + // Copy to the highest array layer + TestBothTBCopiesExactBufferSize(256, 16, texture, bcFormat, {0, 0, 19}, {12, 16, 1}); + + // Copy to array layers in the middle + TestBothTBCopiesExactBufferSize(256, 16, texture, bcFormat, {0, 0, 1}, {12, 16, 18}); + + // Copy touching the texture corners with a non-packed rowsPerImage + TestBothTBCopiesExactBufferSize(256, 24, texture, bcFormat, {4, 4, 4}, {8, 12, 16}); + + // rowsPerImage needs to be a multiple of blockHeight + { + wgpu::Buffer source = + CreateBuffer(8192, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + TestBothTBCopies(utils::Expectation::Failure, source, 0, 256, 6, texture, 0, {0, 0, 0}, + {4, 4, 1}); + } + + // rowsPerImage must be a multiple of blockHeight even with an empty copy + { + wgpu::Buffer source = + CreateBuffer(0, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + TestBothTBCopies(utils::Expectation::Failure, source, 0, 256, 2, texture, 0, {0, 0, 0}, + {0, 0, 0}); + } + } +} \ No newline at end of file diff --git a/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp b/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp index 74e4edabd5..039da1128f 100644 --- a/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp +++ b/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp @@ -15,6 +15,7 @@ #include "tests/unittests/validation/ValidationTest.h" #include "common/Math.h" +#include "utils/TextureFormatUtils.h" #include "utils/WGPUHelpers.h" namespace { @@ -45,21 +46,6 @@ namespace { return tex; } - uint64_t RequiredBytesInCopy(uint32_t bytesPerRow, - uint32_t rowsPerImage, - wgpu::Extent3D copyExtent, - wgpu::TextureFormat format = wgpu::TextureFormat::RGBA8Unorm) { - if (copyExtent.width == 0 || copyExtent.height == 0 || copyExtent.depth == 0) { - return 0; - } else { - uint64_t bytesPerImage = bytesPerRow * rowsPerImage; - uint64_t bytesInLastSlice = - bytesPerRow * (copyExtent.height - 1) + - (copyExtent.width * utils::TextureFormatPixelSize(format)); - return bytesPerImage * (copyExtent.depth - 1) + bytesInLastSlice; - } - } - void TestWriteTexture(size_t dataSize, uint32_t dataOffset, uint32_t dataBytesPerRow, @@ -81,12 +67,30 @@ namespace { queue.WriteTexture(&textureCopyView, data.data(), dataSize, &textureDataLayout, &size); } + void TestWriteTextureExactDataSize(uint32_t bytesPerRow, + uint32_t rowsPerImage, + wgpu::Texture texture, + wgpu::TextureFormat textureFormat, + wgpu::Origin3D origin, + wgpu::Extent3D extent3D) { + // Check the minimal valid dataSize. + uint64_t dataSize = + utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, extent3D, textureFormat); + TestWriteTexture(dataSize, 0, bytesPerRow, rowsPerImage, texture, 0, origin, extent3D); + + // Check dataSize was indeed minimal. + uint64_t invalidSize = dataSize - 1; + ASSERT_DEVICE_ERROR(TestWriteTexture(invalidSize, 0, bytesPerRow, rowsPerImage, texture, + 0, origin, extent3D)); + } + wgpu::Queue queue; }; // Test the success case for WriteTexture TEST_F(QueueWriteTextureValidationTest, Success) { - const uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1}); + const uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); wgpu::Texture destination = Create2DTexture({16, 16, 4}, 5, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); @@ -131,7 +135,8 @@ namespace { // Test OOB conditions on the data TEST_F(QueueWriteTextureValidationTest, OutOfBoundsOnData) { - const uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1}); + const uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); @@ -143,14 +148,15 @@ namespace { ASSERT_DEVICE_ERROR( TestWriteTexture(dataSize, 4, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1})); - // OOB on the data because RequiredBytesInCopy overflows + // OOB on the data because utils::RequiredBytesInCopy overflows ASSERT_DEVICE_ERROR( TestWriteTexture(dataSize, 0, 512, 0, destination, 0, {0, 0, 0}, {4, 3, 1})); // Not OOB on the data although bytes per row * height overflows - // but RequiredBytesInCopy * depth does not overflow + // but utils::RequiredBytesInCopy * depth does not overflow { - uint32_t sourceDataSize = RequiredBytesInCopy(256, 0, {7, 3, 1}); + uint32_t sourceDataSize = + utils::RequiredBytesInCopy(256, 0, {7, 3, 1}, wgpu::TextureFormat::RGBA8Unorm); ASSERT_TRUE(256 * 3 > sourceDataSize) << "bytes per row * height should overflow data"; TestWriteTexture(sourceDataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {7, 3, 1}); @@ -159,7 +165,8 @@ namespace { // Test OOB conditions on the texture TEST_F(QueueWriteTextureValidationTest, OutOfBoundsOnTexture) { - const uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1}); + const uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); wgpu::Texture destination = Create2DTexture({16, 16, 2}, 5, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); @@ -186,7 +193,8 @@ namespace { // Test that we force Depth=1 on writes to 2D textures TEST_F(QueueWriteTextureValidationTest, DepthConstraintFor2DTextures) { - const uint64_t dataSize = RequiredBytesInCopy(0, 0, {0, 0, 2}); + const uint64_t dataSize = + utils::RequiredBytesInCopy(0, 0, {0, 0, 2}, wgpu::TextureFormat::RGBA8Unorm); wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); @@ -197,7 +205,8 @@ namespace { // Test WriteTexture with incorrect texture usage TEST_F(QueueWriteTextureValidationTest, IncorrectUsage) { - const uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1}); + const uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); wgpu::Texture sampled = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::Sampled); @@ -226,7 +235,8 @@ namespace { // Test that if rowsPerImage is greater than 0, it must be at least copy height. TEST_F(QueueWriteTextureValidationTest, ImageHeightConstraint) { - uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1}); + uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); wgpu::Texture destination = Create2DTexture({16, 16, 1}, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); @@ -246,7 +256,8 @@ namespace { // Test WriteTexture with incorrect data offset usage TEST_F(QueueWriteTextureValidationTest, IncorrectDataOffset) { - uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 4, 1}); + uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); @@ -259,7 +270,8 @@ namespace { // Test multisampled textures can be used in WriteTexture. TEST_F(QueueWriteTextureValidationTest, WriteToMultisampledTexture) { - uint64_t dataSize = RequiredBytesInCopy(256, 0, {2, 2, 1}); + uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {2, 2, 1}, wgpu::TextureFormat::RGBA8Unorm); wgpu::Texture destination = Create2DTexture({2, 2, 1}, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst, 4); @@ -316,11 +328,11 @@ namespace { { for (wgpu::TextureFormat format : kFormats) { uint32_t validDataSize = - RequiredBytesInCopy(kBytesPerRow, 0, {kWidth, kHeight, 1}, format); + utils::RequiredBytesInCopy(kBytesPerRow, 0, {kWidth, kHeight, 1}, format); wgpu::Texture destination = Create2DTexture({kWidth, kHeight, 1}, 1, format, wgpu::TextureUsage::CopyDst); - // Verify the return value of RequiredBytesInCopu() is exactly the minimum valid + // Verify the return value of RequiredBytesInCopy() is exactly the minimum valid // data size in this test. { uint32_t invalidDataSize = validDataSize - 1; @@ -339,7 +351,8 @@ namespace { // Test write from data to mip map of non square texture TEST_F(QueueWriteTextureValidationTest, WriteToMipmapOfNonSquareTexture) { - uint64_t dataSize = RequiredBytesInCopy(256, 0, {4, 2, 1}); + uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 2, 1}, wgpu::TextureFormat::RGBA8Unorm); uint32_t maxMipmapLevel = 3; wgpu::Texture destination = Create2DTexture({4, 2, 1}, maxMipmapLevel, wgpu::TextureFormat::RGBA8Unorm, @@ -362,6 +375,33 @@ namespace { {0, 0, 0}, {2, 2, 1})); } + // Test writes to multiple array layers of an uncompressed texture + TEST_F(QueueWriteTextureValidationTest, WriteToMultipleArrayLayers) { + wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( + {4, 2, 5}, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); + + // Write to all array layers + TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 0}, {4, 2, 5}); + + // Write to the highest array layer + TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 4}, {4, 2, 1}); + + // Write to array layers in the middle + TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 1}, {4, 2, 3}); + + // Copy with a non-packed rowsPerImage + TestWriteTextureExactDataSize(256, 3, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 0}, {4, 2, 5}); + + // Copy with bytesPerRow = 500 + TestWriteTextureExactDataSize(500, 2, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 1}, {4, 2, 3}); + } + class WriteTextureTest_CompressedTextureFormats : public QueueWriteTextureValidationTest { public: WriteTextureTest_CompressedTextureFormats() : QueueWriteTextureValidationTest() { @@ -403,14 +443,14 @@ namespace { // Valid usages of data offset. { - uint32_t validDataOffset = utils::CompressedFormatBlockSizeInBytes(bcFormat); + uint32_t validDataOffset = utils::GetTexelBlockSizeInBytes(bcFormat); QueueWriteTextureValidationTest::TestWriteTexture(512, validDataOffset, 256, 4, texture, 0, {0, 0, 0}, {4, 4, 1}); } // Failures on invalid data offset. { - uint32_t kInvalidDataOffset = utils::CompressedFormatBlockSizeInBytes(bcFormat) / 2; + uint32_t kInvalidDataOffset = utils::GetTexelBlockSizeInBytes(bcFormat) / 2; ASSERT_DEVICE_ERROR(TestWriteTexture(512, kInvalidDataOffset, 256, 4, texture, 0, {0, 0, 0}, {4, 4, 1})); } @@ -439,7 +479,7 @@ namespace { for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight); uint32_t ValidBytesPerRow = - kTestWidth / 4 * utils::CompressedFormatBlockSizeInBytes(bcFormat); + kTestWidth / 4 * utils::GetTexelBlockSizeInBytes(bcFormat); ASSERT_NE(0u, ValidBytesPerRow % 256); TestWriteTexture(1024, 0, ValidBytesPerRow, 4, texture, 0, {0, 0, 0}, {kTestWidth, 4, 1}); @@ -568,4 +608,32 @@ namespace { } } + // Test writes to multiple array layers of a compressed texture + TEST_F(WriteTextureTest_CompressedTextureFormats, WriteToMultipleArrayLayers) { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = QueueWriteTextureValidationTest::Create2DTexture( + {12, 16, 20}, 1, bcFormat, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); + + // Write to all array layers + TestWriteTextureExactDataSize(256, 16, texture, bcFormat, {0, 0, 0}, {12, 16, 20}); + + // Write to the highest array layer + TestWriteTextureExactDataSize(256, 16, texture, bcFormat, {0, 0, 19}, {12, 16, 1}); + + // Write to array layers in the middle + TestWriteTextureExactDataSize(256, 16, texture, bcFormat, {0, 0, 1}, {12, 16, 18}); + + // Write touching the texture corners with a non-packed rowsPerImage + TestWriteTextureExactDataSize(256, 24, texture, bcFormat, {4, 4, 4}, {8, 12, 16}); + + // rowsPerImage needs to be a multiple of blockHeight + ASSERT_DEVICE_ERROR( + TestWriteTexture(8192, 0, 256, 6, texture, 0, {0, 0, 0}, {4, 4, 1})); + + // rowsPerImage must be a multiple of blockHeight even with an empty write + ASSERT_DEVICE_ERROR(TestWriteTexture(0, 0, 256, 2, texture, 0, {0, 0, 0}, {0, 0, 0})); + } + } + } // anonymous namespace \ No newline at end of file diff --git a/src/utils/TextureFormatUtils.cpp b/src/utils/TextureFormatUtils.cpp index 795d1f948a..9a577798df 100644 --- a/src/utils/TextureFormatUtils.cpp +++ b/src/utils/TextureFormatUtils.cpp @@ -119,6 +119,7 @@ namespace utils { case wgpu::TextureFormat::BGRA8UnormSrgb: case wgpu::TextureFormat::RGB10A2Unorm: case wgpu::TextureFormat::RG11B10Float: + case wgpu::TextureFormat::Depth32Float: return 4u; case wgpu::TextureFormat::RG32Float: @@ -152,9 +153,138 @@ namespace utils { case wgpu::TextureFormat::BC7RGBAUnormSrgb: return 16u; + case wgpu::TextureFormat::Depth24Plus: + case wgpu::TextureFormat::Depth24PlusStencil8: + case wgpu::TextureFormat::Undefined: + default: + UNREACHABLE(); + return 0u; + } + } + + uint32_t GetTextureFormatBlockWidth(wgpu::TextureFormat textureFormat) { + switch (textureFormat) { + case wgpu::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Snorm: + case wgpu::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Sint: + case wgpu::TextureFormat::R16Uint: + case wgpu::TextureFormat::R16Sint: + case wgpu::TextureFormat::R16Float: + case wgpu::TextureFormat::RG8Unorm: + case wgpu::TextureFormat::RG8Snorm: + case wgpu::TextureFormat::RG8Uint: + case wgpu::TextureFormat::RG8Sint: + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::RG16Uint: + case wgpu::TextureFormat::RG16Sint: + case wgpu::TextureFormat::RG16Float: + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8UnormSrgb: + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + case wgpu::TextureFormat::RGB10A2Unorm: + case wgpu::TextureFormat::RG11B10Float: + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA16Float: + case wgpu::TextureFormat::RGBA32Float: + case wgpu::TextureFormat::RGBA32Uint: + case wgpu::TextureFormat::RGBA32Sint: case wgpu::TextureFormat::Depth32Float: case wgpu::TextureFormat::Depth24Plus: case wgpu::TextureFormat::Depth24PlusStencil8: + return 1u; + + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + case wgpu::TextureFormat::BC4RUnorm: + case wgpu::TextureFormat::BC4RSnorm: + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + case wgpu::TextureFormat::BC3RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + case wgpu::TextureFormat::BC5RGUnorm: + case wgpu::TextureFormat::BC5RGSnorm: + case wgpu::TextureFormat::BC6HRGBUfloat: + case wgpu::TextureFormat::BC6HRGBSfloat: + case wgpu::TextureFormat::BC7RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return 4u; + + case wgpu::TextureFormat::Undefined: + default: + UNREACHABLE(); + return 0u; + } + } + + uint32_t GetTextureFormatBlockHeight(wgpu::TextureFormat textureFormat) { + switch (textureFormat) { + case wgpu::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Snorm: + case wgpu::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Sint: + case wgpu::TextureFormat::R16Uint: + case wgpu::TextureFormat::R16Sint: + case wgpu::TextureFormat::R16Float: + case wgpu::TextureFormat::RG8Unorm: + case wgpu::TextureFormat::RG8Snorm: + case wgpu::TextureFormat::RG8Uint: + case wgpu::TextureFormat::RG8Sint: + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::RG16Uint: + case wgpu::TextureFormat::RG16Sint: + case wgpu::TextureFormat::RG16Float: + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8UnormSrgb: + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + case wgpu::TextureFormat::RGB10A2Unorm: + case wgpu::TextureFormat::RG11B10Float: + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA16Float: + case wgpu::TextureFormat::RGBA32Float: + case wgpu::TextureFormat::RGBA32Uint: + case wgpu::TextureFormat::RGBA32Sint: + case wgpu::TextureFormat::Depth32Float: + case wgpu::TextureFormat::Depth24Plus: + case wgpu::TextureFormat::Depth24PlusStencil8: + return 1u; + + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + case wgpu::TextureFormat::BC4RUnorm: + case wgpu::TextureFormat::BC4RSnorm: + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + case wgpu::TextureFormat::BC3RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + case wgpu::TextureFormat::BC5RGUnorm: + case wgpu::TextureFormat::BC5RGSnorm: + case wgpu::TextureFormat::BC6HRGBUfloat: + case wgpu::TextureFormat::BC6HRGBSfloat: + case wgpu::TextureFormat::BC7RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return 4u; + case wgpu::TextureFormat::Undefined: default: UNREACHABLE(); diff --git a/src/utils/TextureFormatUtils.h b/src/utils/TextureFormatUtils.h index 2976e94d3f..cdd8942583 100644 --- a/src/utils/TextureFormatUtils.h +++ b/src/utils/TextureFormatUtils.h @@ -55,6 +55,8 @@ namespace utils { bool TextureFormatSupportsStorageTexture(wgpu::TextureFormat format); uint32_t GetTexelBlockSizeInBytes(wgpu::TextureFormat textureFormat); + uint32_t GetTextureFormatBlockWidth(wgpu::TextureFormat textureFormat); + uint32_t GetTextureFormatBlockHeight(wgpu::TextureFormat textureFormat); const char* GetGLSLImageFormatQualifier(wgpu::TextureFormat textureFormat); } // namespace utils diff --git a/src/utils/WGPUHelpers.cpp b/src/utils/WGPUHelpers.cpp index 3280586285..3a6b45fb44 100644 --- a/src/utils/WGPUHelpers.cpp +++ b/src/utils/WGPUHelpers.cpp @@ -418,19 +418,6 @@ namespace utils { return layout; } - // TODO(jiawei.shao@intel.com): support more pixel formats - uint32_t TextureFormatPixelSize(wgpu::TextureFormat format) { - switch (format) { - case wgpu::TextureFormat::RG8Unorm: - return 2; - case wgpu::TextureFormat::RGBA8Unorm: - return 4; - default: - UNREACHABLE(); - return 0; - } - } - const std::array kBCFormats = { wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb, wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb, @@ -440,27 +427,22 @@ namespace utils { wgpu::TextureFormat::BC6HRGBUfloat, wgpu::TextureFormat::BC6HRGBSfloat, wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb}; - uint32_t CompressedFormatBlockSizeInBytes(wgpu::TextureFormat format) { - switch (format) { - case wgpu::TextureFormat::BC1RGBAUnorm: - case wgpu::TextureFormat::BC1RGBAUnormSrgb: - case wgpu::TextureFormat::BC4RSnorm: - case wgpu::TextureFormat::BC4RUnorm: - return 8; - case wgpu::TextureFormat::BC2RGBAUnorm: - case wgpu::TextureFormat::BC2RGBAUnormSrgb: - case wgpu::TextureFormat::BC3RGBAUnorm: - case wgpu::TextureFormat::BC3RGBAUnormSrgb: - case wgpu::TextureFormat::BC5RGSnorm: - case wgpu::TextureFormat::BC5RGUnorm: - case wgpu::TextureFormat::BC6HRGBSfloat: - case wgpu::TextureFormat::BC6HRGBUfloat: - case wgpu::TextureFormat::BC7RGBAUnorm: - case wgpu::TextureFormat::BC7RGBAUnormSrgb: - return 16; - default: - UNREACHABLE(); - return 0; + uint64_t RequiredBytesInCopy(uint64_t bytesPerRow, + uint64_t rowsPerImage, + wgpu::Extent3D copyExtent, + wgpu::TextureFormat textureFormat) { + if (copyExtent.width == 0 || copyExtent.height == 0 || copyExtent.depth == 0) { + return 0; + } else { + uint32_t blockSize = utils::GetTexelBlockSizeInBytes(textureFormat); + uint32_t blockWidth = utils::GetTextureFormatBlockWidth(textureFormat); + uint32_t blockHeight = utils::GetTextureFormatBlockHeight(textureFormat); + + uint64_t texelBlockRowsPerImage = rowsPerImage / blockHeight; + uint64_t bytesPerImage = bytesPerRow * texelBlockRowsPerImage; + uint64_t bytesInLastSlice = bytesPerRow * (copyExtent.height / blockHeight - 1) + + (copyExtent.width / blockWidth * blockSize); + return bytesPerImage * (copyExtent.depth - 1) + bytesInLastSlice; } } diff --git a/src/utils/WGPUHelpers.h b/src/utils/WGPUHelpers.h index 6c7825ff50..a01cee67c7 100644 --- a/src/utils/WGPUHelpers.h +++ b/src/utils/WGPUHelpers.h @@ -22,6 +22,7 @@ #include #include "common/Constants.h" +#include "utils/TextureFormatUtils.h" namespace utils { @@ -151,9 +152,12 @@ namespace utils { uint32_t mipmapLevel, uint32_t rowsPerImage); - uint32_t TextureFormatPixelSize(wgpu::TextureFormat format); extern const std::array kBCFormats; - uint32_t CompressedFormatBlockSizeInBytes(wgpu::TextureFormat format); + + uint64_t RequiredBytesInCopy(uint64_t bytesPerRow, + uint64_t rowsPerImage, + wgpu::Extent3D copyExtent, + wgpu::TextureFormat textureFormat); } // namespace utils