• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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