• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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 <gtest/gtest.h>
16 
17 #include "common/Assert.h"
18 #include "common/Constants.h"
19 #include "common/Math.h"
20 #include "dawn/webgpu_cpp_print.h"
21 #include "dawn_native/Format.h"
22 #include "dawn_native/d3d12/TextureCopySplitter.h"
23 #include "dawn_native/d3d12/d3d12_platform.h"
24 #include "utils/TestUtils.h"
25 
26 using namespace dawn_native::d3d12;
27 
28 namespace {
29 
30     struct TextureSpec {
31         uint32_t x;
32         uint32_t y;
33         uint32_t z;
34         uint32_t width;
35         uint32_t height;
36         uint32_t depthOrArrayLayers;
37         uint32_t texelBlockSizeInBytes;
38         uint32_t blockWidth = 1;
39         uint32_t blockHeight = 1;
40     };
41 
42     struct BufferSpec {
43         uint64_t offset;
44         uint32_t bytesPerRow;
45         uint32_t rowsPerImage;
46     };
47 
48     // Check that each copy region fits inside the buffer footprint
ValidateFootprints(const TextureSpec & textureSpec,const BufferSpec & bufferSpec,const TextureCopySubresource & copySplit,wgpu::TextureDimension dimension)49     void ValidateFootprints(const TextureSpec& textureSpec,
50                             const BufferSpec& bufferSpec,
51                             const TextureCopySubresource& copySplit,
52                             wgpu::TextureDimension dimension) {
53         for (uint32_t i = 0; i < copySplit.count; ++i) {
54             const auto& copy = copySplit.copies[i];
55             ASSERT_LE(copy.bufferOffset.x + copy.copySize.width, copy.bufferSize.width);
56             ASSERT_LE(copy.bufferOffset.y + copy.copySize.height, copy.bufferSize.height);
57             ASSERT_LE(copy.bufferOffset.z + copy.copySize.depthOrArrayLayers,
58                       copy.bufferSize.depthOrArrayLayers);
59 
60             // If there are multiple layers, 2D texture splitter actually splits each layer
61             // independently. See the details in Compute2DTextureCopySplits(). As a result,
62             // if we simply expand a copy region generated by 2D texture splitter to all
63             // layers, the copy region might be OOB. But that is not the approach that the current
64             // 2D texture splitter is doing, although Compute2DTextureCopySubresource forwards
65             // "copySize.depthOrArrayLayers" to the copy region it generated. So skip the test
66             // below for 2D textures with multiple layers.
67             if (textureSpec.depthOrArrayLayers <= 1 || dimension == wgpu::TextureDimension::e3D) {
68                 uint32_t widthInBlocks = textureSpec.width / textureSpec.blockWidth;
69                 uint32_t heightInBlocks = textureSpec.height / textureSpec.blockHeight;
70                 uint64_t minimumRequiredBufferSize =
71                     bufferSpec.offset +
72                     utils::RequiredBytesInCopy(bufferSpec.bytesPerRow, bufferSpec.rowsPerImage,
73                                                widthInBlocks, heightInBlocks,
74                                                textureSpec.depthOrArrayLayers,
75                                                textureSpec.texelBlockSizeInBytes);
76 
77                 // The last pixel (buffer footprint) of each copy region depends on its bufferOffset
78                 // and copySize. It is not the last pixel where the bufferSize ends.
79                 ASSERT_EQ(copy.bufferOffset.x % textureSpec.blockWidth, 0u);
80                 ASSERT_EQ(copy.copySize.width % textureSpec.blockWidth, 0u);
81                 uint32_t footprintWidth = copy.bufferOffset.x + copy.copySize.width;
82                 ASSERT_EQ(footprintWidth % textureSpec.blockWidth, 0u);
83                 uint32_t footprintWidthInBlocks = footprintWidth / textureSpec.blockWidth;
84 
85                 ASSERT_EQ(copy.bufferOffset.y % textureSpec.blockHeight, 0u);
86                 ASSERT_EQ(copy.copySize.height % textureSpec.blockHeight, 0u);
87                 uint32_t footprintHeight = copy.bufferOffset.y + copy.copySize.height;
88                 ASSERT_EQ(footprintHeight % textureSpec.blockHeight, 0u);
89                 uint32_t footprintHeightInBlocks = footprintHeight / textureSpec.blockHeight;
90 
91                 uint64_t bufferSizeForFootprint =
92                     copy.alignedOffset +
93                     utils::RequiredBytesInCopy(bufferSpec.bytesPerRow, copy.bufferSize.height,
94                                                footprintWidthInBlocks, footprintHeightInBlocks,
95                                                copy.bufferSize.depthOrArrayLayers,
96                                                textureSpec.texelBlockSizeInBytes);
97 
98                 // The buffer footprint of each copy region should not exceed the minimum required
99                 // buffer size. Otherwise, pixels accessed by copy may be OOB.
100                 ASSERT_LE(bufferSizeForFootprint, minimumRequiredBufferSize);
101             }
102         }
103     }
104 
105     // Check that the offset is aligned
ValidateOffset(const TextureCopySubresource & copySplit)106     void ValidateOffset(const TextureCopySubresource& copySplit) {
107         for (uint32_t i = 0; i < copySplit.count; ++i) {
108             ASSERT_TRUE(
109                 Align(copySplit.copies[i].alignedOffset, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT) ==
110                 copySplit.copies[i].alignedOffset);
111         }
112     }
113 
InclusiveRangesOverlap(uint32_t minA,uint32_t maxA,uint32_t minB,uint32_t maxB)114     bool InclusiveRangesOverlap(uint32_t minA, uint32_t maxA, uint32_t minB, uint32_t maxB) {
115         return (minA <= minB && minB <= maxA) || (minB <= minA && minA <= maxB);
116     }
117 
118     // Check that no pair of copy regions intersect each other
ValidateDisjoint(const TextureCopySubresource & copySplit)119     void ValidateDisjoint(const TextureCopySubresource& copySplit) {
120         for (uint32_t i = 0; i < copySplit.count; ++i) {
121             const auto& a = copySplit.copies[i];
122             for (uint32_t j = i + 1; j < copySplit.count; ++j) {
123                 const auto& b = copySplit.copies[j];
124                 // If textureOffset.x is 0, and copySize.width is 2, we are copying pixel 0 and
125                 // 1. We never touch pixel 2 on x-axis. So the copied range on x-axis should be
126                 // [textureOffset.x, textureOffset.x + copySize.width - 1] and both ends are
127                 // included.
128                 bool overlapX = InclusiveRangesOverlap(
129                     a.textureOffset.x, a.textureOffset.x + a.copySize.width - 1, b.textureOffset.x,
130                     b.textureOffset.x + b.copySize.width - 1);
131                 bool overlapY = InclusiveRangesOverlap(
132                     a.textureOffset.y, a.textureOffset.y + a.copySize.height - 1, b.textureOffset.y,
133                     b.textureOffset.y + b.copySize.height - 1);
134                 bool overlapZ = InclusiveRangesOverlap(
135                     a.textureOffset.z, a.textureOffset.z + a.copySize.depthOrArrayLayers - 1,
136                     b.textureOffset.z, b.textureOffset.z + b.copySize.depthOrArrayLayers - 1);
137                 ASSERT_TRUE(!overlapX || !overlapY || !overlapZ);
138             }
139         }
140     }
141 
142     // Check that the union of the copy regions exactly covers the texture region
ValidateTextureBounds(const TextureSpec & textureSpec,const TextureCopySubresource & copySplit)143     void ValidateTextureBounds(const TextureSpec& textureSpec,
144                                const TextureCopySubresource& copySplit) {
145         ASSERT_TRUE(copySplit.count > 0);
146 
147         uint32_t minX = copySplit.copies[0].textureOffset.x;
148         uint32_t minY = copySplit.copies[0].textureOffset.y;
149         uint32_t minZ = copySplit.copies[0].textureOffset.z;
150         uint32_t maxX = copySplit.copies[0].textureOffset.x + copySplit.copies[0].copySize.width;
151         uint32_t maxY = copySplit.copies[0].textureOffset.y + copySplit.copies[0].copySize.height;
152         uint32_t maxZ =
153             copySplit.copies[0].textureOffset.z + copySplit.copies[0].copySize.depthOrArrayLayers;
154 
155         for (uint32_t i = 1; i < copySplit.count; ++i) {
156             const auto& copy = copySplit.copies[i];
157             minX = std::min(minX, copy.textureOffset.x);
158             minY = std::min(minY, copy.textureOffset.y);
159             minZ = std::min(minZ, copy.textureOffset.z);
160             maxX = std::max(maxX, copy.textureOffset.x + copy.copySize.width);
161             maxY = std::max(maxY, copy.textureOffset.y + copy.copySize.height);
162             maxZ = std::max(maxZ, copy.textureOffset.z + copy.copySize.depthOrArrayLayers);
163         }
164 
165         ASSERT_EQ(minX, textureSpec.x);
166         ASSERT_EQ(minY, textureSpec.y);
167         ASSERT_EQ(minZ, textureSpec.z);
168         ASSERT_EQ(maxX, textureSpec.x + textureSpec.width);
169         ASSERT_EQ(maxY, textureSpec.y + textureSpec.height);
170         ASSERT_EQ(maxZ, textureSpec.z + textureSpec.depthOrArrayLayers);
171     }
172 
173     // Validate that the number of pixels copied is exactly equal to the number of pixels in the
174     // texture region
ValidatePixelCount(const TextureSpec & textureSpec,const TextureCopySubresource & copySplit)175     void ValidatePixelCount(const TextureSpec& textureSpec,
176                             const TextureCopySubresource& copySplit) {
177         uint32_t count = 0;
178         for (uint32_t i = 0; i < copySplit.count; ++i) {
179             const auto& copy = copySplit.copies[i];
180             uint32_t copiedPixels =
181                 copy.copySize.width * copy.copySize.height * copy.copySize.depthOrArrayLayers;
182             ASSERT_GT(copiedPixels, 0u);
183             count += copiedPixels;
184         }
185         ASSERT_EQ(count, textureSpec.width * textureSpec.height * textureSpec.depthOrArrayLayers);
186     }
187 
188     // Check that every buffer offset is at the correct pixel location
ValidateBufferOffset(const TextureSpec & textureSpec,const BufferSpec & bufferSpec,const TextureCopySubresource & copySplit,wgpu::TextureDimension dimension)189     void ValidateBufferOffset(const TextureSpec& textureSpec,
190                               const BufferSpec& bufferSpec,
191                               const TextureCopySubresource& copySplit,
192                               wgpu::TextureDimension dimension) {
193         ASSERT_TRUE(copySplit.count > 0);
194 
195         uint32_t texelsPerBlock = textureSpec.blockWidth * textureSpec.blockHeight;
196         for (uint32_t i = 0; i < copySplit.count; ++i) {
197             const auto& copy = copySplit.copies[i];
198 
199             uint32_t bytesPerRowInTexels =
200                 bufferSpec.bytesPerRow / textureSpec.texelBlockSizeInBytes * texelsPerBlock;
201             uint32_t slicePitchInTexels =
202                 bytesPerRowInTexels * (bufferSpec.rowsPerImage / textureSpec.blockHeight);
203             uint32_t absoluteTexelOffset =
204                 copy.alignedOffset / textureSpec.texelBlockSizeInBytes * texelsPerBlock +
205                 copy.bufferOffset.x / textureSpec.blockWidth * texelsPerBlock +
206                 copy.bufferOffset.y / textureSpec.blockHeight * bytesPerRowInTexels;
207 
208             // There is one empty row at most in a 2D copy region. However, it is not true for
209             // a 3D texture copy region when we are copying the last row of each slice. We may
210             // need to offset a lot rows and copy.bufferOffset.y may be big.
211             if (dimension == wgpu::TextureDimension::e2D) {
212                 ASSERT_LE(copy.bufferOffset.y, textureSpec.blockHeight);
213             }
214             ASSERT_EQ(copy.bufferOffset.z, 0u);
215 
216             ASSERT_GE(absoluteTexelOffset,
217                       bufferSpec.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock);
218             uint32_t relativeTexelOffset =
219                 absoluteTexelOffset -
220                 bufferSpec.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock;
221 
222             uint32_t z = relativeTexelOffset / slicePitchInTexels;
223             uint32_t y = (relativeTexelOffset % slicePitchInTexels) / bytesPerRowInTexels;
224             uint32_t x = relativeTexelOffset % bytesPerRowInTexels;
225 
226             ASSERT_EQ(copy.textureOffset.x - textureSpec.x, x);
227             ASSERT_EQ(copy.textureOffset.y - textureSpec.y, y);
228             ASSERT_EQ(copy.textureOffset.z - textureSpec.z, z);
229         }
230     }
231 
ValidateCopySplit(const TextureSpec & textureSpec,const BufferSpec & bufferSpec,const TextureCopySubresource & copySplit,wgpu::TextureDimension dimension)232     void ValidateCopySplit(const TextureSpec& textureSpec,
233                            const BufferSpec& bufferSpec,
234                            const TextureCopySubresource& copySplit,
235                            wgpu::TextureDimension dimension) {
236         ValidateFootprints(textureSpec, bufferSpec, copySplit, dimension);
237         ValidateOffset(copySplit);
238         ValidateDisjoint(copySplit);
239         ValidateTextureBounds(textureSpec, copySplit);
240         ValidatePixelCount(textureSpec, copySplit);
241         ValidateBufferOffset(textureSpec, bufferSpec, copySplit, dimension);
242     }
243 
operator <<(std::ostream & os,const TextureSpec & textureSpec)244     std::ostream& operator<<(std::ostream& os, const TextureSpec& textureSpec) {
245         os << "TextureSpec("
246            << "[(" << textureSpec.x << ", " << textureSpec.y << ", " << textureSpec.z << "), ("
247            << textureSpec.width << ", " << textureSpec.height << ", "
248            << textureSpec.depthOrArrayLayers << ")], " << textureSpec.texelBlockSizeInBytes << ")";
249         return os;
250     }
251 
operator <<(std::ostream & os,const BufferSpec & bufferSpec)252     std::ostream& operator<<(std::ostream& os, const BufferSpec& bufferSpec) {
253         os << "BufferSpec(" << bufferSpec.offset << ", " << bufferSpec.bytesPerRow << ", "
254            << bufferSpec.rowsPerImage << ")";
255         return os;
256     }
257 
operator <<(std::ostream & os,const TextureCopySubresource & copySplit)258     std::ostream& operator<<(std::ostream& os, const TextureCopySubresource& copySplit) {
259         os << "CopySplit" << std::endl;
260         for (uint32_t i = 0; i < copySplit.count; ++i) {
261             const auto& copy = copySplit.copies[i];
262             os << "  " << i << ": Texture at (" << copy.textureOffset.x << ", "
263                << copy.textureOffset.y << ", " << copy.textureOffset.z << "), size ("
264                << copy.copySize.width << ", " << copy.copySize.height << ", "
265                << copy.copySize.depthOrArrayLayers << ")" << std::endl;
266             os << "  " << i << ": Buffer at (" << copy.bufferOffset.x << ", " << copy.bufferOffset.y
267                << ", " << copy.bufferOffset.z << "), footprint (" << copy.bufferSize.width << ", "
268                << copy.bufferSize.height << ", " << copy.bufferSize.depthOrArrayLayers << ")"
269                << std::endl;
270         }
271         return os;
272     }
273 
274     // Define base texture sizes and offsets to test with: some aligned, some unaligned
275     constexpr TextureSpec kBaseTextureSpecs[] = {
276         {0, 0, 0, 1, 1, 1, 4},
277         {0, 0, 0, 64, 1, 1, 4},
278         {0, 0, 0, 128, 1, 1, 4},
279         {0, 0, 0, 192, 1, 1, 4},
280         {31, 16, 0, 1, 1, 1, 4},
281         {64, 16, 0, 1, 1, 1, 4},
282         {64, 16, 8, 1, 1, 1, 4},
283 
284         {0, 0, 0, 64, 2, 1, 4},
285         {0, 0, 0, 64, 1, 2, 4},
286         {0, 0, 0, 64, 2, 2, 4},
287         {0, 0, 0, 128, 2, 1, 4},
288         {0, 0, 0, 128, 1, 2, 4},
289         {0, 0, 0, 128, 2, 2, 4},
290         {0, 0, 0, 192, 2, 1, 4},
291         {0, 0, 0, 192, 1, 2, 4},
292         {0, 0, 0, 192, 2, 2, 4},
293 
294         {0, 0, 0, 1024, 1024, 1, 4},
295         {256, 512, 0, 1024, 1024, 1, 4},
296         {64, 48, 0, 1024, 1024, 1, 4},
297         {64, 48, 16, 1024, 1024, 1024, 4},
298 
299         {0, 0, 0, 257, 31, 1, 4},
300         {0, 0, 0, 17, 93, 1, 4},
301         {59, 13, 0, 257, 31, 1, 4},
302         {17, 73, 0, 17, 93, 1, 4},
303         {17, 73, 59, 17, 93, 99, 4},
304 
305         {0, 0, 0, 4, 4, 1, 8, 4, 4},
306         {64, 16, 0, 4, 4, 1, 8, 4, 4},
307         {64, 16, 8, 4, 4, 1, 8, 4, 4},
308         {0, 0, 0, 4, 4, 1, 16, 4, 4},
309         {64, 16, 0, 4, 4, 1, 16, 4, 4},
310         {64, 16, 8, 4, 4, 1, 16, 4, 4},
311 
312         {0, 0, 0, 1024, 1024, 1, 8, 4, 4},
313         {256, 512, 0, 1024, 1024, 1, 8, 4, 4},
314         {64, 48, 0, 1024, 1024, 1, 8, 4, 4},
315         {64, 48, 16, 1024, 1024, 1, 8, 4, 4},
316         {0, 0, 0, 1024, 1024, 1, 16, 4, 4},
317         {256, 512, 0, 1024, 1024, 1, 16, 4, 4},
318         {64, 48, 0, 1024, 1024, 1, 4, 16, 4},
319         {64, 48, 16, 1024, 1024, 1, 16, 4, 4},
320     };
321 
322     // Define base buffer sizes to work with: some offsets aligned, some unaligned. bytesPerRow is
323     // the minimum required
BaseBufferSpecs(const TextureSpec & textureSpec)324     std::array<BufferSpec, 15> BaseBufferSpecs(const TextureSpec& textureSpec) {
325         uint32_t bytesPerRow = Align(textureSpec.texelBlockSizeInBytes * textureSpec.width,
326                                      kTextureBytesPerRowAlignment);
327 
328         auto alignNonPow2 = [](uint32_t value, uint32_t size) -> uint32_t {
329             return value == 0 ? 0 : ((value - 1) / size + 1) * size;
330         };
331 
332         return {
333             BufferSpec{alignNonPow2(0, textureSpec.texelBlockSizeInBytes), bytesPerRow,
334                        textureSpec.height},
335             BufferSpec{alignNonPow2(256, textureSpec.texelBlockSizeInBytes), bytesPerRow,
336                        textureSpec.height},
337             BufferSpec{alignNonPow2(512, textureSpec.texelBlockSizeInBytes), bytesPerRow,
338                        textureSpec.height},
339             BufferSpec{alignNonPow2(1024, textureSpec.texelBlockSizeInBytes), bytesPerRow,
340                        textureSpec.height},
341             BufferSpec{alignNonPow2(1024, textureSpec.texelBlockSizeInBytes), bytesPerRow,
342                        textureSpec.height * 2},
343 
344             BufferSpec{alignNonPow2(32, textureSpec.texelBlockSizeInBytes), bytesPerRow,
345                        textureSpec.height},
346             BufferSpec{alignNonPow2(64, textureSpec.texelBlockSizeInBytes), bytesPerRow,
347                        textureSpec.height},
348             BufferSpec{alignNonPow2(64, textureSpec.texelBlockSizeInBytes), bytesPerRow,
349                        textureSpec.height * 2},
350 
351             BufferSpec{alignNonPow2(31, textureSpec.texelBlockSizeInBytes), bytesPerRow,
352                        textureSpec.height},
353             BufferSpec{alignNonPow2(257, textureSpec.texelBlockSizeInBytes), bytesPerRow,
354                        textureSpec.height},
355             BufferSpec{alignNonPow2(384, textureSpec.texelBlockSizeInBytes), bytesPerRow,
356                        textureSpec.height},
357             BufferSpec{alignNonPow2(511, textureSpec.texelBlockSizeInBytes), bytesPerRow,
358                        textureSpec.height},
359             BufferSpec{alignNonPow2(513, textureSpec.texelBlockSizeInBytes), bytesPerRow,
360                        textureSpec.height},
361             BufferSpec{alignNonPow2(1023, textureSpec.texelBlockSizeInBytes), bytesPerRow,
362                        textureSpec.height},
363             BufferSpec{alignNonPow2(1023, textureSpec.texelBlockSizeInBytes), bytesPerRow,
364                        textureSpec.height * 2},
365         };
366     }
367 
368     // Define a list of values to set properties in the spec structs
369     constexpr uint32_t kCheckValues[] = {1,  2,  3,  4,   5,   6,   7,    8,     // small values
370                                          16, 32, 64, 128, 256, 512, 1024, 2048,  // powers of 2
371                                          15, 31, 63, 127, 257, 511, 1023, 2047,  // misalignments
372                                          17, 33, 65, 129, 257, 513, 1025, 2049};
373 
374 }  // namespace
375 
376 class CopySplitTest : public testing::TestWithParam<wgpu::TextureDimension> {
377   protected:
DoTest(const TextureSpec & textureSpec,const BufferSpec & bufferSpec)378     void DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) {
379         ASSERT(textureSpec.width % textureSpec.blockWidth == 0 &&
380                textureSpec.height % textureSpec.blockHeight == 0);
381 
382         wgpu::TextureDimension dimension = GetParam();
383         TextureCopySubresource copySplit;
384         switch (dimension) {
385             case wgpu::TextureDimension::e2D: {
386                 copySplit = Compute2DTextureCopySubresource(
387                     {textureSpec.x, textureSpec.y, textureSpec.z},
388                     {textureSpec.width, textureSpec.height, textureSpec.depthOrArrayLayers},
389                     {textureSpec.texelBlockSizeInBytes, textureSpec.blockWidth,
390                      textureSpec.blockHeight},
391                     bufferSpec.offset, bufferSpec.bytesPerRow);
392                 break;
393             }
394             case wgpu::TextureDimension::e3D: {
395                 copySplit = Compute3DTextureCopySplits(
396                     {textureSpec.x, textureSpec.y, textureSpec.z},
397                     {textureSpec.width, textureSpec.height, textureSpec.depthOrArrayLayers},
398                     {textureSpec.texelBlockSizeInBytes, textureSpec.blockWidth,
399                      textureSpec.blockHeight},
400                     bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage);
401                 break;
402             }
403             default:
404                 UNREACHABLE();
405                 break;
406         }
407 
408         ValidateCopySplit(textureSpec, bufferSpec, copySplit, dimension);
409 
410         if (HasFatalFailure()) {
411             std::ostringstream message;
412             message << "Failed generating splits: " << textureSpec << ", " << bufferSpec
413                     << std::endl
414                     << dimension << " " << copySplit << std::endl;
415             FAIL() << message.str();
416         }
417     }
418 };
419 
TEST_P(CopySplitTest,General)420 TEST_P(CopySplitTest, General) {
421     for (TextureSpec textureSpec : kBaseTextureSpecs) {
422         for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
423             DoTest(textureSpec, bufferSpec);
424         }
425     }
426 }
427 
TEST_P(CopySplitTest,TextureWidth)428 TEST_P(CopySplitTest, TextureWidth) {
429     for (TextureSpec textureSpec : kBaseTextureSpecs) {
430         for (uint32_t val : kCheckValues) {
431             if (val % textureSpec.blockWidth != 0) {
432                 continue;
433             }
434             textureSpec.width = val;
435             for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
436                 DoTest(textureSpec, bufferSpec);
437             }
438         }
439     }
440 }
441 
TEST_P(CopySplitTest,TextureHeight)442 TEST_P(CopySplitTest, TextureHeight) {
443     for (TextureSpec textureSpec : kBaseTextureSpecs) {
444         for (uint32_t val : kCheckValues) {
445             if (val % textureSpec.blockHeight != 0) {
446                 continue;
447             }
448             textureSpec.height = val;
449             for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
450                 DoTest(textureSpec, bufferSpec);
451             }
452         }
453     }
454 }
455 
TEST_P(CopySplitTest,TextureX)456 TEST_P(CopySplitTest, TextureX) {
457     for (TextureSpec textureSpec : kBaseTextureSpecs) {
458         for (uint32_t val : kCheckValues) {
459             textureSpec.x = val;
460             for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
461                 DoTest(textureSpec, bufferSpec);
462             }
463         }
464     }
465 }
466 
TEST_P(CopySplitTest,TextureY)467 TEST_P(CopySplitTest, TextureY) {
468     for (TextureSpec textureSpec : kBaseTextureSpecs) {
469         for (uint32_t val : kCheckValues) {
470             textureSpec.y = val;
471             for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
472                 DoTest(textureSpec, bufferSpec);
473             }
474         }
475     }
476 }
477 
TEST_P(CopySplitTest,TexelSize)478 TEST_P(CopySplitTest, TexelSize) {
479     for (TextureSpec textureSpec : kBaseTextureSpecs) {
480         for (uint32_t texelSize : {4, 8, 16, 32, 64}) {
481             textureSpec.texelBlockSizeInBytes = texelSize;
482             for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
483                 DoTest(textureSpec, bufferSpec);
484             }
485         }
486     }
487 }
488 
TEST_P(CopySplitTest,BufferOffset)489 TEST_P(CopySplitTest, BufferOffset) {
490     for (TextureSpec textureSpec : kBaseTextureSpecs) {
491         for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
492             for (uint32_t val : kCheckValues) {
493                 bufferSpec.offset = textureSpec.texelBlockSizeInBytes * val;
494 
495                 DoTest(textureSpec, bufferSpec);
496             }
497         }
498     }
499 }
500 
TEST_P(CopySplitTest,RowPitch)501 TEST_P(CopySplitTest, RowPitch) {
502     for (TextureSpec textureSpec : kBaseTextureSpecs) {
503         for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
504             uint32_t baseRowPitch = bufferSpec.bytesPerRow;
505             for (uint32_t i = 0; i < 5; ++i) {
506                 bufferSpec.bytesPerRow = baseRowPitch + i * 256;
507 
508                 DoTest(textureSpec, bufferSpec);
509             }
510         }
511     }
512 }
513 
TEST_P(CopySplitTest,ImageHeight)514 TEST_P(CopySplitTest, ImageHeight) {
515     for (TextureSpec textureSpec : kBaseTextureSpecs) {
516         for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) {
517             uint32_t baseImageHeight = bufferSpec.rowsPerImage;
518             for (uint32_t i = 0; i < 5; ++i) {
519                 bufferSpec.rowsPerImage = baseImageHeight + i * 256;
520 
521                 DoTest(textureSpec, bufferSpec);
522             }
523         }
524     }
525 }
526 
527 INSTANTIATE_TEST_SUITE_P(,
528                          CopySplitTest,
529                          testing::Values(wgpu::TextureDimension::e2D, wgpu::TextureDimension::e3D));
530