1 // Copyright 2020 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "tests/unittests/validation/ValidationTest.h" 16 17 #include "common/Math.h" 18 #include "utils/TestUtils.h" 19 #include "utils/TextureUtils.h" 20 #include "utils/WGPUHelpers.h" 21 22 namespace { 23 24 class QueueWriteTextureValidationTest : public ValidationTest { 25 private: SetUp()26 void SetUp() override { 27 ValidationTest::SetUp(); 28 queue = device.GetQueue(); 29 } 30 31 protected: Create2DTexture(wgpu::Extent3D size,uint32_t mipLevelCount,wgpu::TextureFormat format,wgpu::TextureUsage usage,uint32_t sampleCount=1)32 wgpu::Texture Create2DTexture(wgpu::Extent3D size, 33 uint32_t mipLevelCount, 34 wgpu::TextureFormat format, 35 wgpu::TextureUsage usage, 36 uint32_t sampleCount = 1) { 37 wgpu::TextureDescriptor descriptor; 38 descriptor.dimension = wgpu::TextureDimension::e2D; 39 descriptor.size.width = size.width; 40 descriptor.size.height = size.height; 41 descriptor.size.depthOrArrayLayers = size.depthOrArrayLayers; 42 descriptor.sampleCount = sampleCount; 43 descriptor.format = format; 44 descriptor.mipLevelCount = mipLevelCount; 45 descriptor.usage = usage; 46 wgpu::Texture tex = device.CreateTexture(&descriptor); 47 return tex; 48 } 49 TestWriteTexture(size_t dataSize,uint32_t dataOffset,uint32_t dataBytesPerRow,uint32_t dataRowsPerImage,wgpu::Texture texture,uint32_t texLevel,wgpu::Origin3D texOrigin,wgpu::Extent3D size,wgpu::TextureAspect aspect=wgpu::TextureAspect::All)50 void TestWriteTexture(size_t dataSize, 51 uint32_t dataOffset, 52 uint32_t dataBytesPerRow, 53 uint32_t dataRowsPerImage, 54 wgpu::Texture texture, 55 uint32_t texLevel, 56 wgpu::Origin3D texOrigin, 57 wgpu::Extent3D size, 58 wgpu::TextureAspect aspect = wgpu::TextureAspect::All) { 59 std::vector<uint8_t> data(dataSize); 60 61 wgpu::TextureDataLayout textureDataLayout; 62 textureDataLayout.offset = dataOffset; 63 textureDataLayout.bytesPerRow = dataBytesPerRow; 64 textureDataLayout.rowsPerImage = dataRowsPerImage; 65 66 wgpu::ImageCopyTexture imageCopyTexture = 67 utils::CreateImageCopyTexture(texture, texLevel, texOrigin, aspect); 68 69 queue.WriteTexture(&imageCopyTexture, data.data(), dataSize, &textureDataLayout, &size); 70 } 71 TestWriteTextureExactDataSize(uint32_t bytesPerRow,uint32_t rowsPerImage,wgpu::Texture texture,wgpu::TextureFormat textureFormat,wgpu::Origin3D origin,wgpu::Extent3D extent3D)72 void TestWriteTextureExactDataSize(uint32_t bytesPerRow, 73 uint32_t rowsPerImage, 74 wgpu::Texture texture, 75 wgpu::TextureFormat textureFormat, 76 wgpu::Origin3D origin, 77 wgpu::Extent3D extent3D) { 78 // Check the minimal valid dataSize. 79 uint64_t dataSize = 80 utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, extent3D, textureFormat); 81 TestWriteTexture(dataSize, 0, bytesPerRow, rowsPerImage, texture, 0, origin, extent3D); 82 83 // Check dataSize was indeed minimal. 84 uint64_t invalidSize = dataSize - 1; 85 ASSERT_DEVICE_ERROR(TestWriteTexture(invalidSize, 0, bytesPerRow, rowsPerImage, texture, 86 0, origin, extent3D)); 87 } 88 89 wgpu::Queue queue; 90 }; 91 92 // Test the success case for WriteTexture TEST_F(QueueWriteTextureValidationTest,Success)93 TEST_F(QueueWriteTextureValidationTest, Success) { 94 const uint64_t dataSize = 95 utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); 96 wgpu::Texture destination = Create2DTexture({16, 16, 4}, 5, wgpu::TextureFormat::RGBA8Unorm, 97 wgpu::TextureUsage::CopyDst); 98 99 // Different copies, including some that touch the OOB condition 100 { 101 // Copy 4x4 block in corner of first mip. 102 TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {0, 0, 0}, {4, 4, 1}); 103 // Copy 4x4 block in opposite corner of first mip. 104 TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {12, 12, 0}, {4, 4, 1}); 105 // Copy 4x4 block in the 4x4 mip. 106 TestWriteTexture(dataSize, 0, 256, 4, destination, 2, {0, 0, 0}, {4, 4, 1}); 107 // Copy with a data offset 108 TestWriteTexture(dataSize, dataSize - 4, 256, 1, destination, 0, {0, 0, 0}, {1, 1, 1}); 109 TestWriteTexture(dataSize, dataSize - 4, 256, wgpu::kCopyStrideUndefined, destination, 110 0, {0, 0, 0}, {1, 1, 1}); 111 } 112 113 // Copies with a 256-byte aligned bytes per row but unaligned texture region 114 { 115 // Unaligned region 116 TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {0, 0, 0}, {3, 4, 1}); 117 // Unaligned region with texture offset 118 TestWriteTexture(dataSize, 0, 256, 3, destination, 0, {5, 7, 0}, {2, 3, 1}); 119 // Unaligned region, with data offset 120 TestWriteTexture(dataSize, 31 * 4, 256, 3, destination, 0, {0, 0, 0}, {3, 3, 1}); 121 } 122 123 // Empty copies are valid 124 { 125 // An empty copy 126 TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 1}); 127 TestWriteTexture(dataSize, 0, 0, wgpu::kCopyStrideUndefined, destination, 0, {0, 0, 0}, 128 {0, 0, 1}); 129 // An empty copy with depth = 0 130 TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 0}); 131 TestWriteTexture(dataSize, 0, 0, wgpu::kCopyStrideUndefined, destination, 0, {0, 0, 0}, 132 {0, 0, 0}); 133 // An empty copy touching the end of the data 134 TestWriteTexture(dataSize, dataSize, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 1}); 135 TestWriteTexture(dataSize, dataSize, 0, wgpu::kCopyStrideUndefined, destination, 0, 136 {0, 0, 0}, {0, 0, 1}); 137 // An empty copy touching the side of the texture 138 TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {16, 16, 0}, {0, 0, 1}); 139 TestWriteTexture(dataSize, 0, 0, wgpu::kCopyStrideUndefined, destination, 0, 140 {16, 16, 0}, {0, 0, 1}); 141 // An empty copy with depth = 1 and bytesPerRow > 0 142 TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {0, 0, 1}); 143 TestWriteTexture(dataSize, 0, 256, wgpu::kCopyStrideUndefined, destination, 0, 144 {0, 0, 0}, {0, 0, 1}); 145 // An empty copy with height > 0, depth = 0, bytesPerRow > 0 and rowsPerImage > 0 146 TestWriteTexture(dataSize, 0, 256, wgpu::kCopyStrideUndefined, destination, 0, 147 {0, 0, 0}, {0, 1, 0}); 148 TestWriteTexture(dataSize, 0, 256, 1, destination, 0, {0, 0, 0}, {0, 1, 0}); 149 TestWriteTexture(dataSize, 0, 256, 16, destination, 0, {0, 0, 0}, {0, 1, 0}); 150 } 151 } 152 153 // Test OOB conditions on the data TEST_F(QueueWriteTextureValidationTest,OutOfBoundsOnData)154 TEST_F(QueueWriteTextureValidationTest, OutOfBoundsOnData) { 155 const uint64_t dataSize = 156 utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); 157 wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, 158 wgpu::TextureUsage::CopyDst); 159 160 // OOB on the data because we copy too many pixels 161 ASSERT_DEVICE_ERROR( 162 TestWriteTexture(dataSize, 0, 256, 5, destination, 0, {0, 0, 0}, {4, 5, 1})); 163 164 // OOB on the data because of the offset 165 ASSERT_DEVICE_ERROR( 166 TestWriteTexture(dataSize, 4, 256, 4, destination, 0, {0, 0, 0}, {4, 4, 1})); 167 168 // OOB on the data because utils::RequiredBytesInCopy overflows 169 ASSERT_DEVICE_ERROR( 170 TestWriteTexture(dataSize, 0, 512, 3, destination, 0, {0, 0, 0}, {4, 3, 1})); 171 172 // Not OOB on the data although bytes per row * height overflows 173 // but utils::RequiredBytesInCopy * depth does not overflow 174 { 175 uint32_t sourceDataSize = 176 utils::RequiredBytesInCopy(256, 0, {7, 3, 1}, wgpu::TextureFormat::RGBA8Unorm); 177 ASSERT_TRUE(256 * 3 > sourceDataSize) << "bytes per row * height should overflow data"; 178 179 TestWriteTexture(sourceDataSize, 0, 256, 3, destination, 0, {0, 0, 0}, {7, 3, 1}); 180 } 181 } 182 183 // Test OOB conditions on the texture TEST_F(QueueWriteTextureValidationTest,OutOfBoundsOnTexture)184 TEST_F(QueueWriteTextureValidationTest, OutOfBoundsOnTexture) { 185 const uint64_t dataSize = 186 utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); 187 wgpu::Texture destination = Create2DTexture({16, 16, 2}, 5, wgpu::TextureFormat::RGBA8Unorm, 188 wgpu::TextureUsage::CopyDst); 189 190 // OOB on the texture because x + width overflows 191 ASSERT_DEVICE_ERROR( 192 TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {13, 12, 0}, {4, 4, 1})); 193 194 // OOB on the texture because y + width overflows 195 ASSERT_DEVICE_ERROR( 196 TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {12, 13, 0}, {4, 4, 1})); 197 198 // OOB on the texture because we overflow a non-zero mip 199 ASSERT_DEVICE_ERROR( 200 TestWriteTexture(dataSize, 0, 256, 4, destination, 2, {1, 0, 0}, {4, 4, 1})); 201 202 // OOB on the texture even on an empty copy when we copy to a non-existent mip. 203 ASSERT_DEVICE_ERROR( 204 TestWriteTexture(dataSize, 0, 0, 0, destination, 5, {0, 0, 0}, {0, 0, 1})); 205 206 // OOB on the texture because slice overflows 207 ASSERT_DEVICE_ERROR( 208 TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 2}, {0, 0, 1})); 209 } 210 211 // Test that we force Depth=1 on writes to 2D textures TEST_F(QueueWriteTextureValidationTest,DepthConstraintFor2DTextures)212 TEST_F(QueueWriteTextureValidationTest, DepthConstraintFor2DTextures) { 213 const uint64_t dataSize = 214 utils::RequiredBytesInCopy(0, 0, {0, 0, 2}, wgpu::TextureFormat::RGBA8Unorm); 215 wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, 216 wgpu::TextureUsage::CopyDst); 217 218 // Depth > 1 on an empty copy still errors 219 ASSERT_DEVICE_ERROR( 220 TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 2})); 221 } 222 223 // Test WriteTexture with incorrect texture usage TEST_F(QueueWriteTextureValidationTest,IncorrectUsage)224 TEST_F(QueueWriteTextureValidationTest, IncorrectUsage) { 225 const uint64_t dataSize = 226 utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); 227 wgpu::Texture sampled = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, 228 wgpu::TextureUsage::TextureBinding); 229 230 // Incorrect destination usage 231 ASSERT_DEVICE_ERROR( 232 TestWriteTexture(dataSize, 0, 256, 4, sampled, 0, {0, 0, 0}, {4, 4, 1})); 233 } 234 235 // Test incorrect values of bytesPerRow and that values not divisible by 256 are allowed. TEST_F(QueueWriteTextureValidationTest,BytesPerRowConstraints)236 TEST_F(QueueWriteTextureValidationTest, BytesPerRowConstraints) { 237 wgpu::Texture destination = Create2DTexture({3, 7, 2}, 1, wgpu::TextureFormat::RGBA8Unorm, 238 wgpu::TextureUsage::CopyDst); 239 240 // bytesPerRow = 0 or wgpu::kCopyStrideUndefined 241 { 242 // copyHeight > 1 243 ASSERT_DEVICE_ERROR( 244 TestWriteTexture(128, 0, 0, 7, destination, 0, {0, 0, 0}, {3, 7, 1})); 245 TestWriteTexture(128, 0, 0, 7, destination, 0, {0, 0, 0}, {0, 7, 1}); 246 ASSERT_DEVICE_ERROR(TestWriteTexture(128, 0, wgpu::kCopyStrideUndefined, 7, destination, 247 0, {0, 0, 0}, {0, 7, 1})); 248 249 // copyDepth > 1 250 ASSERT_DEVICE_ERROR( 251 TestWriteTexture(128, 0, 0, 1, destination, 0, {0, 0, 0}, {3, 1, 2})); 252 TestWriteTexture(128, 0, 0, 1, destination, 0, {0, 0, 0}, {0, 1, 2}); 253 ASSERT_DEVICE_ERROR(TestWriteTexture(128, 0, wgpu::kCopyStrideUndefined, 1, destination, 254 0, {0, 0, 0}, {0, 1, 2})); 255 256 // copyHeight = 1 and copyDepth = 1 257 ASSERT_DEVICE_ERROR( 258 TestWriteTexture(128, 0, 0, 1, destination, 0, {0, 0, 0}, {3, 1, 1})); 259 TestWriteTexture(128, 0, wgpu::kCopyStrideUndefined, 1, destination, 0, {0, 0, 0}, 260 {3, 1, 1}); 261 } 262 263 // bytesPerRow = 11 is invalid since a row takes 12 bytes. 264 { 265 // copyHeight > 1 266 ASSERT_DEVICE_ERROR( 267 TestWriteTexture(128, 0, 11, 7, destination, 0, {0, 0, 0}, {3, 7, 1})); 268 // copyHeight == 0 269 ASSERT_DEVICE_ERROR( 270 TestWriteTexture(128, 0, 11, 0, destination, 0, {0, 0, 0}, {3, 0, 1})); 271 272 // copyDepth > 1 273 ASSERT_DEVICE_ERROR( 274 TestWriteTexture(128, 0, 11, 1, destination, 0, {0, 0, 0}, {3, 1, 2})); 275 // copyDepth == 0 276 ASSERT_DEVICE_ERROR( 277 TestWriteTexture(128, 0, 11, 1, destination, 0, {0, 0, 0}, {3, 1, 0})); 278 279 // copyHeight = 1 and copyDepth = 1 280 ASSERT_DEVICE_ERROR( 281 TestWriteTexture(128, 0, 11, 1, destination, 0, {0, 0, 0}, {3, 1, 1})); 282 } 283 284 // bytesPerRow = 12 is valid since a row takes 12 bytes. 285 TestWriteTexture(128, 0, 12, 7, destination, 0, {0, 0, 0}, {3, 7, 1}); 286 287 // bytesPerRow = 13 is valid since a row takes 12 bytes. 288 TestWriteTexture(128, 0, 13, 7, destination, 0, {0, 0, 0}, {3, 7, 1}); 289 } 290 291 // Test that if rowsPerImage is greater than 0, it must be at least copy height. TEST_F(QueueWriteTextureValidationTest,RowsPerImageConstraints)292 TEST_F(QueueWriteTextureValidationTest, RowsPerImageConstraints) { 293 uint64_t dataSize = 294 utils::RequiredBytesInCopy(256, 5, {4, 4, 2}, wgpu::TextureFormat::RGBA8Unorm); 295 wgpu::Texture destination = Create2DTexture({16, 16, 2}, 1, wgpu::TextureFormat::RGBA8Unorm, 296 wgpu::TextureUsage::CopyDst); 297 298 // rowsPerImage is wgpu::kCopyStrideUndefined 299 TestWriteTexture(dataSize, 0, 256, wgpu::kCopyStrideUndefined, destination, 0, {0, 0, 0}, 300 {4, 4, 1}); 301 302 // rowsPerImage is equal to copy height (Valid) 303 TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {0, 0, 0}, {4, 4, 1}); 304 305 // rowsPerImage is larger than copy height (Valid) 306 TestWriteTexture(dataSize, 0, 256, 5, destination, 0, {0, 0, 0}, {4, 4, 1}); 307 TestWriteTexture(dataSize, 0, 256, 5, destination, 0, {0, 0, 0}, {4, 4, 2}); 308 309 // rowsPerImage is less than copy height (Invalid) 310 ASSERT_DEVICE_ERROR( 311 TestWriteTexture(dataSize, 0, 256, 3, destination, 0, {0, 0, 0}, {4, 4, 1})); 312 ASSERT_DEVICE_ERROR( 313 TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1})); 314 } 315 316 // Test WriteTexture with data offset TEST_F(QueueWriteTextureValidationTest,DataOffset)317 TEST_F(QueueWriteTextureValidationTest, DataOffset) { 318 uint64_t dataSize = 319 utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); 320 wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, 321 wgpu::TextureUsage::CopyDst); 322 323 // Offset aligned 324 TestWriteTexture(dataSize, dataSize - 4, 256, 1, destination, 0, {0, 0, 0}, {1, 1, 1}); 325 // Offset not aligned 326 TestWriteTexture(dataSize, dataSize - 5, 256, 1, destination, 0, {0, 0, 0}, {1, 1, 1}); 327 // Offset+size too large 328 ASSERT_DEVICE_ERROR( 329 TestWriteTexture(dataSize, dataSize - 3, 256, 1, destination, 0, {0, 0, 0}, {1, 1, 1})); 330 } 331 332 // Test multisampled textures can be used in WriteTexture. TEST_F(QueueWriteTextureValidationTest,WriteToMultisampledTexture)333 TEST_F(QueueWriteTextureValidationTest, WriteToMultisampledTexture) { 334 uint64_t dataSize = 335 utils::RequiredBytesInCopy(256, 0, {2, 2, 1}, wgpu::TextureFormat::RGBA8Unorm); 336 wgpu::Texture destination = Create2DTexture({2, 2, 1}, 1, wgpu::TextureFormat::RGBA8Unorm, 337 wgpu::TextureUsage::CopyDst, 4); 338 339 ASSERT_DEVICE_ERROR( 340 TestWriteTexture(dataSize, 0, 256, 2, destination, 0, {0, 0, 0}, {2, 2, 1})); 341 } 342 343 // Test that WriteTexture cannot be run with a destroyed texture. TEST_F(QueueWriteTextureValidationTest,DestroyedTexture)344 TEST_F(QueueWriteTextureValidationTest, DestroyedTexture) { 345 const uint64_t dataSize = 346 utils::RequiredBytesInCopy(256, 4, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); 347 wgpu::Texture destination = Create2DTexture({16, 16, 4}, 5, wgpu::TextureFormat::RGBA8Unorm, 348 wgpu::TextureUsage::CopyDst); 349 destination.Destroy(); 350 351 ASSERT_DEVICE_ERROR( 352 TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {0, 0, 0}, {4, 4, 1})); 353 } 354 355 // Test WriteTexture with texture in error state causes errors. TEST_F(QueueWriteTextureValidationTest,TextureInErrorState)356 TEST_F(QueueWriteTextureValidationTest, TextureInErrorState) { 357 wgpu::TextureDescriptor errorTextureDescriptor; 358 errorTextureDescriptor.size.depthOrArrayLayers = 0; 359 ASSERT_DEVICE_ERROR(wgpu::Texture errorTexture = 360 device.CreateTexture(&errorTextureDescriptor)); 361 wgpu::ImageCopyTexture errorImageCopyTexture = 362 utils::CreateImageCopyTexture(errorTexture, 0, {0, 0, 0}); 363 364 wgpu::Extent3D extent3D = {0, 0, 0}; 365 366 { 367 std::vector<uint8_t> data(4); 368 wgpu::TextureDataLayout textureDataLayout = utils::CreateTextureDataLayout(0, 0, 0); 369 370 ASSERT_DEVICE_ERROR(queue.WriteTexture(&errorImageCopyTexture, data.data(), 4, 371 &textureDataLayout, &extent3D)); 372 } 373 } 374 375 // Test that WriteTexture throws an error when requiredBytesInCopy overflows uint64_t TEST_F(QueueWriteTextureValidationTest,RequiredBytesInCopyOverflow)376 TEST_F(QueueWriteTextureValidationTest, RequiredBytesInCopyOverflow) { 377 wgpu::Texture destination = Create2DTexture({1, 1, 16}, 1, wgpu::TextureFormat::RGBA8Unorm, 378 wgpu::TextureUsage::CopyDst); 379 380 // success because depth = 1. 381 TestWriteTexture(10000, 0, (1 << 31), (1 << 31), destination, 0, {0, 0, 0}, {1, 1, 1}); 382 // failure because bytesPerImage * (depth - 1) overflows. 383 ASSERT_DEVICE_ERROR(TestWriteTexture(10000, 0, (1 << 31), (1 << 31), destination, 0, 384 {0, 0, 0}, {1, 1, 16})); 385 } 386 387 // Regression tests for a bug in the computation of texture data size in Dawn. TEST_F(QueueWriteTextureValidationTest,TextureWriteDataSizeLastRowComputation)388 TEST_F(QueueWriteTextureValidationTest, TextureWriteDataSizeLastRowComputation) { 389 constexpr uint32_t kBytesPerRow = 256; 390 constexpr uint32_t kWidth = 4; 391 constexpr uint32_t kHeight = 4; 392 393 constexpr std::array<wgpu::TextureFormat, 2> kFormats = {wgpu::TextureFormat::RGBA8Unorm, 394 wgpu::TextureFormat::RG8Unorm}; 395 396 { 397 // kBytesPerRow * (kHeight - 1) + kWidth is not large enough to be the valid data size 398 // in this test because the data sizes in WriteTexture are not in texels but in bytes. 399 constexpr uint32_t kInvalidDataSize = kBytesPerRow * (kHeight - 1) + kWidth; 400 401 for (wgpu::TextureFormat format : kFormats) { 402 wgpu::Texture destination = 403 Create2DTexture({kWidth, kHeight, 1}, 1, format, wgpu::TextureUsage::CopyDst); 404 ASSERT_DEVICE_ERROR(TestWriteTexture(kInvalidDataSize, 0, kBytesPerRow, kHeight, 405 destination, 0, {0, 0, 0}, 406 {kWidth, kHeight, 1})); 407 } 408 } 409 410 { 411 for (wgpu::TextureFormat format : kFormats) { 412 uint32_t validDataSize = 413 utils::RequiredBytesInCopy(kBytesPerRow, 0, {kWidth, kHeight, 1}, format); 414 wgpu::Texture destination = 415 Create2DTexture({kWidth, kHeight, 1}, 1, format, wgpu::TextureUsage::CopyDst); 416 417 // Verify the return value of RequiredBytesInCopy() is exactly the minimum valid 418 // data size in this test. 419 { 420 uint32_t invalidDataSize = validDataSize - 1; 421 ASSERT_DEVICE_ERROR(TestWriteTexture(invalidDataSize, 0, kBytesPerRow, kHeight, 422 destination, 0, {0, 0, 0}, 423 {kWidth, kHeight, 1})); 424 } 425 426 { 427 TestWriteTexture(validDataSize, 0, kBytesPerRow, kHeight, destination, 0, 428 {0, 0, 0}, {kWidth, kHeight, 1}); 429 } 430 } 431 } 432 } 433 434 // Test write from data to mip map of non square texture TEST_F(QueueWriteTextureValidationTest,WriteToMipmapOfNonSquareTexture)435 TEST_F(QueueWriteTextureValidationTest, WriteToMipmapOfNonSquareTexture) { 436 uint64_t dataSize = 437 utils::RequiredBytesInCopy(256, 0, {4, 2, 1}, wgpu::TextureFormat::RGBA8Unorm); 438 uint32_t maxMipmapLevel = 3; 439 wgpu::Texture destination = 440 Create2DTexture({4, 2, 1}, maxMipmapLevel, wgpu::TextureFormat::RGBA8Unorm, 441 wgpu::TextureUsage::CopyDst); 442 443 // Copy to top level mip map 444 TestWriteTexture(dataSize, 0, 256, 1, destination, maxMipmapLevel - 1, {0, 0, 0}, 445 {1, 1, 1}); 446 // Copy to high level mip map 447 TestWriteTexture(dataSize, 0, 256, 1, destination, maxMipmapLevel - 2, {0, 0, 0}, 448 {2, 1, 1}); 449 // Mip level out of range 450 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 1, destination, maxMipmapLevel, 451 {0, 0, 0}, {1, 1, 1})); 452 // Copy origin out of range 453 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 1, destination, maxMipmapLevel - 2, 454 {1, 0, 0}, {2, 1, 1})); 455 // Copy size out of range 456 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 2, destination, maxMipmapLevel - 2, 457 {0, 0, 0}, {2, 2, 1})); 458 } 459 460 // Test writes to multiple array layers of an uncompressed texture TEST_F(QueueWriteTextureValidationTest,WriteToMultipleArrayLayers)461 TEST_F(QueueWriteTextureValidationTest, WriteToMultipleArrayLayers) { 462 wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( 463 {4, 2, 5}, 1, wgpu::TextureFormat::RGBA8Unorm, 464 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); 465 466 // Write to all array layers 467 TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, 468 {0, 0, 0}, {4, 2, 5}); 469 470 // Write to the highest array layer 471 TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, 472 {0, 0, 4}, {4, 2, 1}); 473 474 // Write to array layers in the middle 475 TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, 476 {0, 0, 1}, {4, 2, 3}); 477 478 // Copy with a non-packed rowsPerImage 479 TestWriteTextureExactDataSize(256, 3, destination, wgpu::TextureFormat::RGBA8Unorm, 480 {0, 0, 0}, {4, 2, 5}); 481 482 // Copy with bytesPerRow = 500 483 TestWriteTextureExactDataSize(500, 2, destination, wgpu::TextureFormat::RGBA8Unorm, 484 {0, 0, 1}, {4, 2, 3}); 485 } 486 487 // Test it is invalid to write into a depth texture. TEST_F(QueueWriteTextureValidationTest,WriteToDepthAspect)488 TEST_F(QueueWriteTextureValidationTest, WriteToDepthAspect) { 489 uint32_t bytesPerRow = sizeof(float) * 4; 490 const uint64_t dataSize = utils::RequiredBytesInCopy(bytesPerRow, 0, {4, 4, 1}, 491 wgpu::TextureFormat::Depth32Float); 492 493 // Invalid to write into depth32float 494 { 495 wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( 496 {4, 4, 1}, 1, wgpu::TextureFormat::Depth32Float, wgpu::TextureUsage::CopyDst); 497 498 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 4, destination, 0, 499 {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All)); 500 501 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 4, destination, 0, 502 {0, 0, 0}, {4, 4, 1}, 503 wgpu::TextureAspect::DepthOnly)); 504 } 505 506 // Invalid to write into depth24plus 507 { 508 wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( 509 {4, 4, 1}, 1, wgpu::TextureFormat::Depth24Plus, wgpu::TextureUsage::CopyDst); 510 511 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 4, destination, 0, 512 {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::All)); 513 514 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 4, destination, 0, 515 {0, 0, 0}, {4, 4, 1}, 516 wgpu::TextureAspect::DepthOnly)); 517 } 518 } 519 520 // Test write texture to the stencil aspect TEST_F(QueueWriteTextureValidationTest,WriteToStencilAspect)521 TEST_F(QueueWriteTextureValidationTest, WriteToStencilAspect) { 522 uint32_t bytesPerRow = 4; 523 const uint64_t dataSize = 524 utils::RequiredBytesInCopy(bytesPerRow, 0, {4, 4, 1}, wgpu::TextureFormat::R8Uint); 525 526 // It is valid to write into the stencil aspect of depth24plus-stencil8 527 { 528 wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( 529 {4, 4, 1}, 1, wgpu::TextureFormat::Depth24PlusStencil8, 530 wgpu::TextureUsage::CopyDst); 531 532 TestWriteTexture(dataSize, 0, bytesPerRow, wgpu::kCopyStrideUndefined, destination, 0, 533 {0, 0, 0}, {4, 4, 1}, wgpu::TextureAspect::StencilOnly); 534 535 // And that it fails if the buffer is one byte too small 536 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize - 1, 0, bytesPerRow, 4, destination, 0, 537 {0, 0, 0}, {4, 4, 1}, 538 wgpu::TextureAspect::StencilOnly)); 539 540 // It is invalid to write just part of the subresource size 541 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 3, destination, 0, 542 {0, 0, 0}, {3, 3, 1}, 543 wgpu::TextureAspect::StencilOnly)); 544 } 545 546 // It is invalid to write into the stencil aspect of depth24plus (no stencil) 547 { 548 wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( 549 {4, 4, 1}, 1, wgpu::TextureFormat::Depth24Plus, wgpu::TextureUsage::CopyDst); 550 551 ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, bytesPerRow, 4, destination, 0, 552 {0, 0, 0}, {4, 4, 1}, 553 wgpu::TextureAspect::StencilOnly)); 554 } 555 } 556 557 class WriteTextureTest_CompressedTextureFormats : public QueueWriteTextureValidationTest { 558 protected: CreateTestDevice()559 WGPUDevice CreateTestDevice() override { 560 dawn_native::DawnDeviceDescriptor descriptor; 561 descriptor.requiredFeatures = {"texture-compression-bc", "texture-compression-etc2", 562 "texture-compression-astc"}; 563 return adapter.CreateDevice(&descriptor); 564 } 565 Create2DTexture(wgpu::TextureFormat format,uint32_t mipmapLevels=1,uint32_t width=kWidth,uint32_t height=kHeight)566 wgpu::Texture Create2DTexture(wgpu::TextureFormat format, 567 uint32_t mipmapLevels = 1, 568 uint32_t width = kWidth, 569 uint32_t height = kHeight) { 570 constexpr wgpu::TextureUsage kUsage = wgpu::TextureUsage::CopyDst; 571 constexpr uint32_t kArrayLayers = 1; 572 return QueueWriteTextureValidationTest::Create2DTexture( 573 {width, height, kArrayLayers}, mipmapLevels, format, kUsage, 1); 574 } 575 TestWriteTexture(size_t dataSize,uint32_t dataOffset,uint32_t dataBytesPerRow,uint32_t dataRowsPerImage,wgpu::Texture texture,uint32_t textLevel,wgpu::Origin3D textOrigin,wgpu::Extent3D size)576 void TestWriteTexture(size_t dataSize, 577 uint32_t dataOffset, 578 uint32_t dataBytesPerRow, 579 uint32_t dataRowsPerImage, 580 wgpu::Texture texture, 581 uint32_t textLevel, 582 wgpu::Origin3D textOrigin, 583 wgpu::Extent3D size) { 584 QueueWriteTextureValidationTest::TestWriteTexture(dataSize, dataOffset, dataBytesPerRow, 585 dataRowsPerImage, texture, textLevel, 586 textOrigin, size); 587 } 588 589 static constexpr uint32_t kWidth = 120; 590 static constexpr uint32_t kHeight = 120; 591 }; 592 593 // Tests to verify that data offset may not be a multiple of the compressed texture block size TEST_F(WriteTextureTest_CompressedTextureFormats,DataOffset)594 TEST_F(WriteTextureTest_CompressedTextureFormats, DataOffset) { 595 for (wgpu::TextureFormat format : utils::kCompressedFormats) { 596 wgpu::Texture texture = Create2DTexture(format); 597 uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format); 598 uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format); 599 600 // Valid if aligned. 601 { 602 uint32_t kAlignedOffset = utils::GetTexelBlockSizeInBytes(format); 603 TestWriteTexture(1024, kAlignedOffset, 256, 4, texture, 0, {0, 0, 0}, 604 {blockWidth, blockHeight, 1}); 605 } 606 607 // Still valid if not aligned. 608 { 609 uint32_t kUnalignedOffset = utils::GetTexelBlockSizeInBytes(format) - 1; 610 TestWriteTexture(1024, kUnalignedOffset, 256, 4, texture, 0, {0, 0, 0}, 611 {blockWidth, blockHeight, 1}); 612 } 613 } 614 } 615 616 // Tests to verify that bytesPerRow must not be less than (width / blockWidth) * 617 // blockSizeInBytes and that it doesn't have to be a multiple of the compressed 618 // texture block width. TEST_F(WriteTextureTest_CompressedTextureFormats,BytesPerRow)619 TEST_F(WriteTextureTest_CompressedTextureFormats, BytesPerRow) { 620 // Used to compute test width and height. 621 constexpr uint32_t kTestBytesPerRow = 320; 622 623 for (wgpu::TextureFormat format : utils::kCompressedFormats) { 624 uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format); 625 uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format); 626 uint32_t blockByteSize = utils::GetTexelBlockSizeInBytes(format); 627 uint32_t testWidth = kTestBytesPerRow * blockWidth / blockByteSize; 628 uint32_t testHeight = kTestBytesPerRow * blockHeight / blockByteSize; 629 wgpu::Texture texture = Create2DTexture(format, 1, testWidth, testHeight); 630 631 // Failures on the BytesPerRow that is not large enough. 632 { 633 uint32_t kSmallBytesPerRow = kTestBytesPerRow - blockByteSize; 634 ASSERT_DEVICE_ERROR(TestWriteTexture(1024, 0, kSmallBytesPerRow, 4, texture, 0, 635 {0, 0, 0}, {testWidth, blockHeight, 1})); 636 } 637 638 // Test it is valid to use a BytesPerRow that is not a multiple of 256. 639 { 640 TestWriteTexture(1024, 0, kTestBytesPerRow, 4, texture, 0, {0, 0, 0}, 641 {testWidth, blockHeight, 1}); 642 } 643 644 // Valid usage of bytesPerRow in WriteTexture with compressed texture formats. 645 { 646 TestWriteTexture(512, 0, blockByteSize, 4, texture, 0, {0, 0, 0}, 647 {blockWidth, blockHeight, 1}); 648 } 649 650 // Valid usage of bytesPerRow in WriteTexture with compressed texture formats. Note that 651 // BytesPerRow is not a multiple of the blockByteSize (but is greater than it). 652 { 653 TestWriteTexture(512, 0, blockByteSize + 1, 4, texture, 0, {0, 0, 0}, 654 {blockWidth, blockHeight, 1}); 655 } 656 } 657 } 658 659 // rowsPerImage must be >= heightInBlocks. TEST_F(WriteTextureTest_CompressedTextureFormats,RowsPerImage)660 TEST_F(WriteTextureTest_CompressedTextureFormats, RowsPerImage) { 661 for (wgpu::TextureFormat format : utils::kCompressedFormats) { 662 wgpu::Texture texture = Create2DTexture(format); 663 uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format); 664 uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format); 665 666 // Valid usages of rowsPerImage in WriteTexture with compressed texture formats. 667 { 668 constexpr uint32_t kValidRowsPerImage = 5; 669 TestWriteTexture(1024, 0, 256, kValidRowsPerImage, texture, 0, {0, 0, 0}, 670 {blockWidth, blockHeight * 4, 1}); 671 } 672 { 673 constexpr uint32_t kValidRowsPerImage = 4; 674 TestWriteTexture(1024, 0, 256, kValidRowsPerImage, texture, 0, {0, 0, 0}, 675 {blockWidth, blockHeight * 4, 1}); 676 } 677 678 // rowsPerImage is smaller than height. 679 { 680 constexpr uint32_t kInvalidRowsPerImage = 3; 681 ASSERT_DEVICE_ERROR(TestWriteTexture(1024, 0, 256, kInvalidRowsPerImage, texture, 0, 682 {0, 0, 0}, {blockWidth, blockWidth * 4, 1})); 683 } 684 } 685 } 686 687 // Tests to verify that ImageOffset.x must be a multiple of the compressed texture block width 688 // and ImageOffset.y must be a multiple of the compressed texture block height TEST_F(WriteTextureTest_CompressedTextureFormats,ImageOffset)689 TEST_F(WriteTextureTest_CompressedTextureFormats, ImageOffset) { 690 for (wgpu::TextureFormat format : utils::kCompressedFormats) { 691 wgpu::Texture texture = Create2DTexture(format); 692 wgpu::Texture texture2 = Create2DTexture(format); 693 uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format); 694 uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format); 695 696 wgpu::Origin3D smallestValidOrigin3D = {blockWidth, blockHeight, 0}; 697 698 // Valid usages of ImageOffset in WriteTexture with compressed texture formats. 699 { 700 TestWriteTexture(512, 0, 256, 4, texture, 0, smallestValidOrigin3D, 701 {blockWidth, blockHeight, 1}); 702 } 703 704 // Failures on invalid ImageOffset.x. 705 { 706 wgpu::Origin3D invalidOrigin3D = {smallestValidOrigin3D.x - 1, 707 smallestValidOrigin3D.y, 0}; 708 ASSERT_DEVICE_ERROR(TestWriteTexture(512, 0, 256, 4, texture, 0, invalidOrigin3D, 709 {blockWidth, blockHeight, 1})); 710 } 711 712 // Failures on invalid ImageOffset.y. 713 { 714 wgpu::Origin3D invalidOrigin3D = {smallestValidOrigin3D.x, 715 smallestValidOrigin3D.y - 1, 0}; 716 ASSERT_DEVICE_ERROR(TestWriteTexture(512, 0, 256, 4, texture, 0, invalidOrigin3D, 717 {blockWidth, blockHeight, 1})); 718 } 719 } 720 } 721 722 // Tests to verify that ImageExtent.x must be a multiple of the compressed texture block width 723 // and ImageExtent.y must be a multiple of the compressed texture block height TEST_F(WriteTextureTest_CompressedTextureFormats,ImageExtent)724 TEST_F(WriteTextureTest_CompressedTextureFormats, ImageExtent) { 725 constexpr uint32_t kMipmapLevels = 3; 726 // We choose a prime that is greater than the current max texel dimension size as a 727 // multiplier to compute the test texture size so that we can be certain that its level 2 728 // mipmap (x4) cannot be a multiple of the dimension. This is useful for testing padding at 729 // the edges of the mipmaps. 730 constexpr uint32_t kBlockPerDim = 13; 731 732 for (wgpu::TextureFormat format : utils::kCompressedFormats) { 733 uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format); 734 uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format); 735 uint32_t testWidth = blockWidth * kBlockPerDim; 736 uint32_t testHeight = blockHeight * kBlockPerDim; 737 wgpu::Texture texture = Create2DTexture(format, kMipmapLevels, testWidth, testHeight); 738 wgpu::Texture texture2 = Create2DTexture(format, kMipmapLevels, testWidth, testHeight); 739 740 wgpu::Extent3D smallestValidExtent3D = {blockWidth, blockHeight, 1}; 741 742 // Valid usages of ImageExtent in WriteTexture with compressed texture formats. 743 { TestWriteTexture(512, 0, 256, 4, texture, 0, {0, 0, 0}, smallestValidExtent3D); } 744 745 // Valid usages of ImageExtent in WriteTexture with compressed texture formats 746 // and non-zero mipmap levels. 747 { 748 constexpr uint32_t kTestMipmapLevel = 2; 749 wgpu::Origin3D testOrigin = { 750 ((testWidth >> kTestMipmapLevel) / blockWidth) * blockWidth, 751 ((testHeight >> kTestMipmapLevel) / blockHeight) * blockHeight, 0}; 752 753 TestWriteTexture(512, 0, 256, 4, texture, kTestMipmapLevel, testOrigin, 754 smallestValidExtent3D); 755 } 756 757 // Failures on invalid ImageExtent.x. 758 { 759 wgpu::Extent3D inValidExtent3D = {smallestValidExtent3D.width - 1, 760 smallestValidExtent3D.height, 1}; 761 ASSERT_DEVICE_ERROR( 762 TestWriteTexture(512, 0, 256, 4, texture, 0, {0, 0, 0}, inValidExtent3D)); 763 } 764 765 // Failures on invalid ImageExtent.y. 766 { 767 wgpu::Extent3D inValidExtent3D = {smallestValidExtent3D.width, 768 smallestValidExtent3D.height - 1, 1}; 769 ASSERT_DEVICE_ERROR( 770 TestWriteTexture(512, 0, 256, 4, texture, 0, {0, 0, 0}, inValidExtent3D)); 771 } 772 } 773 } 774 775 // Test writes to multiple array layers of a compressed texture TEST_F(WriteTextureTest_CompressedTextureFormats,WriteToMultipleArrayLayers)776 TEST_F(WriteTextureTest_CompressedTextureFormats, WriteToMultipleArrayLayers) { 777 constexpr uint32_t kWidthMultiplier = 3; 778 constexpr uint32_t kHeightMultiplier = 4; 779 for (wgpu::TextureFormat format : utils::kCompressedFormats) { 780 uint32_t blockWidth = utils::GetTextureFormatBlockWidth(format); 781 uint32_t blockHeight = utils::GetTextureFormatBlockHeight(format); 782 uint32_t testWidth = kWidthMultiplier * blockWidth; 783 uint32_t testHeight = kHeightMultiplier * blockHeight; 784 wgpu::Texture texture = QueueWriteTextureValidationTest::Create2DTexture( 785 {testWidth, testHeight, 20}, 1, format, 786 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); 787 788 // Write to all array layers 789 TestWriteTextureExactDataSize(256, 4, texture, format, {0, 0, 0}, 790 {testWidth, testHeight, 20}); 791 792 // Write to the highest array layer 793 TestWriteTextureExactDataSize(256, 4, texture, format, {0, 0, 19}, 794 {testWidth, testHeight, 1}); 795 796 // Write to array layers in the middle 797 TestWriteTextureExactDataSize(256, 4, texture, format, {0, 0, 1}, 798 {testWidth, testHeight, 18}); 799 800 // Write touching the texture corners with a non-packed rowsPerImage 801 TestWriteTextureExactDataSize(256, 6, texture, format, {blockWidth, blockHeight, 4}, 802 {testWidth - blockWidth, testHeight - blockHeight, 16}); 803 } 804 } 805 806 } // anonymous namespace 807