• 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 "dawn_native/d3d12/TextureCopySplitter.h"
16 
17 #include "common/Assert.h"
18 #include "dawn_native/Format.h"
19 #include "dawn_native/d3d12/d3d12_platform.h"
20 
21 namespace dawn_native { namespace d3d12 {
22 
23     namespace {
ComputeTexelOffsets(const TexelBlockInfo & blockInfo,uint32_t offset,uint32_t bytesPerRow)24         Origin3D ComputeTexelOffsets(const TexelBlockInfo& blockInfo,
25                                      uint32_t offset,
26                                      uint32_t bytesPerRow) {
27             ASSERT(bytesPerRow != 0);
28             uint32_t byteOffsetX = offset % bytesPerRow;
29             uint32_t byteOffsetY = offset - byteOffsetX;
30 
31             return {byteOffsetX / blockInfo.byteSize * blockInfo.width,
32                     byteOffsetY / bytesPerRow * blockInfo.height, 0};
33         }
34 
OffsetToFirstCopiedTexel(const TexelBlockInfo & blockInfo,uint32_t bytesPerRow,uint64_t alignedOffset,Origin3D bufferOffset)35         uint64_t OffsetToFirstCopiedTexel(const TexelBlockInfo& blockInfo,
36                                           uint32_t bytesPerRow,
37                                           uint64_t alignedOffset,
38                                           Origin3D bufferOffset) {
39             ASSERT(bufferOffset.z == 0);
40             return alignedOffset + bufferOffset.x * blockInfo.byteSize / blockInfo.width +
41                    bufferOffset.y * bytesPerRow / blockInfo.height;
42         }
43 
AlignDownForDataPlacement(uint32_t offset)44         uint64_t AlignDownForDataPlacement(uint32_t offset) {
45             return offset & ~static_cast<uint64_t>(D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT - 1);
46         }
47     }  // namespace
48 
AddCopy()49     TextureCopySubresource::CopyInfo* TextureCopySubresource::AddCopy() {
50         ASSERT(this->count < kMaxTextureCopyRegions);
51         return &this->copies[this->count++];
52     }
53 
Compute2DTextureCopySubresource(Origin3D origin,Extent3D copySize,const TexelBlockInfo & blockInfo,uint64_t offset,uint32_t bytesPerRow)54     TextureCopySubresource Compute2DTextureCopySubresource(Origin3D origin,
55                                                            Extent3D copySize,
56                                                            const TexelBlockInfo& blockInfo,
57                                                            uint64_t offset,
58                                                            uint32_t bytesPerRow) {
59         TextureCopySubresource copy;
60 
61         ASSERT(bytesPerRow % blockInfo.byteSize == 0);
62 
63         // The copies must be 512-aligned. To do this, we calculate the first 512-aligned address
64         // preceding our data.
65         uint64_t alignedOffset = AlignDownForDataPlacement(offset);
66 
67         // If the provided offset to the data was already 512-aligned, we can simply copy the data
68         // without further translation.
69         if (offset == alignedOffset) {
70             copy.count = 1;
71 
72             copy.copies[0].alignedOffset = alignedOffset;
73             copy.copies[0].textureOffset = origin;
74             copy.copies[0].copySize = copySize;
75             copy.copies[0].bufferOffset = {0, 0, 0};
76             copy.copies[0].bufferSize = copySize;
77 
78             return copy;
79         }
80 
81         ASSERT(alignedOffset < offset);
82         ASSERT(offset - alignedOffset < D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT);
83 
84         // We must reinterpret our aligned offset into X and Y offsets with respect to the row
85         // pitch.
86         //
87         // You can visualize the data in the buffer like this:
88         // |-----------------------++++++++++++++++++++++++++++++++|
89         // ^ 512-aligned address   ^ Aligned offset               ^ End of copy data
90         //
91         // Now when you consider the row pitch, you can visualize the data like this:
92         // |~~~~~~~~~~~~~~~~|
93         // |~~~~~+++++++++++|
94         // |++++++++++++++++|
95         // |+++++~~~~~~~~~~~|
96         // |<---row pitch-->|
97         //
98         // The X and Y offsets calculated in ComputeTexelOffsets can be visualized like this:
99         // |YYYYYYYYYYYYYYYY|
100         // |XXXXXX++++++++++|
101         // |++++++++++++++++|
102         // |++++++~~~~~~~~~~|
103         // |<---row pitch-->|
104         Origin3D texelOffset = ComputeTexelOffsets(
105             blockInfo, static_cast<uint32_t>(offset - alignedOffset), bytesPerRow);
106 
107         ASSERT(texelOffset.y <= blockInfo.height);
108         ASSERT(texelOffset.z == 0);
109 
110         uint32_t copyBytesPerRowPitch = copySize.width / blockInfo.width * blockInfo.byteSize;
111         uint32_t byteOffsetInRowPitch = texelOffset.x / blockInfo.width * blockInfo.byteSize;
112         if (copyBytesPerRowPitch + byteOffsetInRowPitch <= bytesPerRow) {
113             // The region's rows fit inside the bytes per row. In this case, extend the width of the
114             // PlacedFootprint and copy the buffer with an offset location
115             //  |<------------- bytes per row ------------->|
116             //
117             //  |-------------------------------------------|
118             //  |                                           |
119             //  |                 +++++++++++++++++~~~~~~~~~|
120             //  |~~~~~~~~~~~~~~~~~+++++++++++++++++~~~~~~~~~|
121             //  |~~~~~~~~~~~~~~~~~+++++++++++++++++~~~~~~~~~|
122             //  |~~~~~~~~~~~~~~~~~+++++++++++++++++~~~~~~~~~|
123             //  |~~~~~~~~~~~~~~~~~+++++++++++++++++         |
124             //  |-------------------------------------------|
125 
126             // Copy 0:
127             //  |----------------------------------|
128             //  |                                  |
129             //  |                 +++++++++++++++++|
130             //  |~~~~~~~~~~~~~~~~~+++++++++++++++++|
131             //  |~~~~~~~~~~~~~~~~~+++++++++++++++++|
132             //  |~~~~~~~~~~~~~~~~~+++++++++++++++++|
133             //  |~~~~~~~~~~~~~~~~~+++++++++++++++++|
134             //  |----------------------------------|
135 
136             copy.count = 1;
137 
138             copy.copies[0].alignedOffset = alignedOffset;
139             copy.copies[0].textureOffset = origin;
140             copy.copies[0].copySize = copySize;
141             copy.copies[0].bufferOffset = texelOffset;
142 
143             copy.copies[0].bufferSize.width = copySize.width + texelOffset.x;
144             copy.copies[0].bufferSize.height = copySize.height + texelOffset.y;
145             copy.copies[0].bufferSize.depthOrArrayLayers = copySize.depthOrArrayLayers;
146 
147             return copy;
148         }
149 
150         // The region's rows straddle the bytes per row. Split the copy into two copies
151         //  |<------------- bytes per row ------------->|
152         //
153         //  |-------------------------------------------|
154         //  |                                           |
155         //  |                                   ++++++++|
156         //  |+++++++++~~~~~~~~~~~~~~~~~~~~~~~~~~++++++++|
157         //  |+++++++++~~~~~~~~~~~~~~~~~~~~~~~~~~++++++++|
158         //  |+++++++++~~~~~~~~~~~~~~~~~~~~~~~~~~++++++++|
159         //  |+++++++++~~~~~~~~~~~~~~~~~~~~~~~~~~++++++++|
160         //  |+++++++++                                  |
161         //  |-------------------------------------------|
162 
163         //  Copy 0:
164         //  |-------------------------------------------|
165         //  |                                           |
166         //  |                                   ++++++++|
167         //  |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++++++++|
168         //  |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++++++++|
169         //  |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++++++++|
170         //  |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++++++++|
171         //  |-------------------------------------------|
172 
173         //  Copy 1:
174         //  |---------|
175         //  |         |
176         //  |         |
177         //  |+++++++++|
178         //  |+++++++++|
179         //  |+++++++++|
180         //  |+++++++++|
181         //  |+++++++++|
182         //  |---------|
183 
184         copy.count = 2;
185 
186         copy.copies[0].alignedOffset = alignedOffset;
187         copy.copies[0].textureOffset = origin;
188 
189         ASSERT(bytesPerRow > byteOffsetInRowPitch);
190         uint32_t texelsPerRow = bytesPerRow / blockInfo.byteSize * blockInfo.width;
191         copy.copies[0].copySize.width = texelsPerRow - texelOffset.x;
192         copy.copies[0].copySize.height = copySize.height;
193         copy.copies[0].copySize.depthOrArrayLayers = copySize.depthOrArrayLayers;
194 
195         copy.copies[0].bufferOffset = texelOffset;
196         copy.copies[0].bufferSize.width = texelsPerRow;
197         copy.copies[0].bufferSize.height = copySize.height + texelOffset.y;
198         copy.copies[0].bufferSize.depthOrArrayLayers = copySize.depthOrArrayLayers;
199 
200         uint64_t offsetForCopy1 =
201             offset + copy.copies[0].copySize.width / blockInfo.width * blockInfo.byteSize;
202         uint64_t alignedOffsetForCopy1 = AlignDownForDataPlacement(offsetForCopy1);
203         Origin3D texelOffsetForCopy1 = ComputeTexelOffsets(
204             blockInfo, static_cast<uint32_t>(offsetForCopy1 - alignedOffsetForCopy1), bytesPerRow);
205 
206         ASSERT(texelOffsetForCopy1.y <= blockInfo.height);
207         ASSERT(texelOffsetForCopy1.z == 0);
208 
209         copy.copies[1].alignedOffset = alignedOffsetForCopy1;
210         copy.copies[1].textureOffset.x = origin.x + copy.copies[0].copySize.width;
211         copy.copies[1].textureOffset.y = origin.y;
212         copy.copies[1].textureOffset.z = origin.z;
213 
214         ASSERT(copySize.width > copy.copies[0].copySize.width);
215         copy.copies[1].copySize.width = copySize.width - copy.copies[0].copySize.width;
216         copy.copies[1].copySize.height = copySize.height;
217         copy.copies[1].copySize.depthOrArrayLayers = copySize.depthOrArrayLayers;
218 
219         copy.copies[1].bufferOffset = texelOffsetForCopy1;
220         copy.copies[1].bufferSize.width = copy.copies[1].copySize.width + texelOffsetForCopy1.x;
221         copy.copies[1].bufferSize.height = copySize.height + texelOffsetForCopy1.y;
222         copy.copies[1].bufferSize.depthOrArrayLayers = copySize.depthOrArrayLayers;
223 
224         return copy;
225     }
226 
Compute2DTextureCopySplits(Origin3D origin,Extent3D copySize,const TexelBlockInfo & blockInfo,uint64_t offset,uint32_t bytesPerRow,uint32_t rowsPerImage)227     TextureCopySplits Compute2DTextureCopySplits(Origin3D origin,
228                                                  Extent3D copySize,
229                                                  const TexelBlockInfo& blockInfo,
230                                                  uint64_t offset,
231                                                  uint32_t bytesPerRow,
232                                                  uint32_t rowsPerImage) {
233         TextureCopySplits copies;
234 
235         const uint64_t bytesPerLayer = bytesPerRow * rowsPerImage;
236 
237         // The function Compute2DTextureCopySubresource() decides how to split the copy based on:
238         // - the alignment of the buffer offset with D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512)
239         // - the alignment of the buffer offset with D3D12_TEXTURE_DATA_PITCH_ALIGNMENT (256)
240         // Each layer of a 2D array might need to be split, but because of the WebGPU
241         // constraint that "bytesPerRow" must be a multiple of 256, all odd (resp. all even) layers
242         // will be at an offset multiple of 512 of each other, which means they will all result in
243         // the same 2D split. Thus we can just compute the copy splits for the first and second
244         // layers, and reuse them for the remaining layers by adding the related offset of each
245         // layer. Moreover, if "rowsPerImage" is even, both the first and second copy layers can
246         // share the same copy split, so in this situation we just need to compute copy split once
247         // and reuse it for all the layers.
248         Extent3D copyOneLayerSize = copySize;
249         Origin3D copyFirstLayerOrigin = origin;
250         copyOneLayerSize.depthOrArrayLayers = 1;
251         copyFirstLayerOrigin.z = 0;
252 
253         copies.copySubresources[0] = Compute2DTextureCopySubresource(
254             copyFirstLayerOrigin, copyOneLayerSize, blockInfo, offset, bytesPerRow);
255 
256         // When the copy only refers one texture 2D array layer,
257         // copies.copySubresources[1] will never be used so we can safely early return here.
258         if (copySize.depthOrArrayLayers == 1) {
259             return copies;
260         }
261 
262         if (bytesPerLayer % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT == 0) {
263             copies.copySubresources[1] = copies.copySubresources[0];
264             copies.copySubresources[1].copies[0].alignedOffset += bytesPerLayer;
265             copies.copySubresources[1].copies[1].alignedOffset += bytesPerLayer;
266         } else {
267             const uint64_t bufferOffsetNextLayer = offset + bytesPerLayer;
268             copies.copySubresources[1] =
269                 Compute2DTextureCopySubresource(copyFirstLayerOrigin, copyOneLayerSize, blockInfo,
270                                                 bufferOffsetNextLayer, bytesPerRow);
271         }
272 
273         return copies;
274     }
275 
Recompute3DTextureCopyRegionWithEmptyFirstRowAndEvenCopyHeight(Origin3D origin,Extent3D copySize,const TexelBlockInfo & blockInfo,uint32_t bytesPerRow,uint32_t rowsPerImage,TextureCopySubresource & copy,uint32_t i)276     void Recompute3DTextureCopyRegionWithEmptyFirstRowAndEvenCopyHeight(
277         Origin3D origin,
278         Extent3D copySize,
279         const TexelBlockInfo& blockInfo,
280         uint32_t bytesPerRow,
281         uint32_t rowsPerImage,
282         TextureCopySubresource& copy,
283         uint32_t i) {
284         // Let's assign data and show why copy region generated by ComputeTextureCopySubresource
285         // is incorrect if there is an empty row at the beginning of the copy block.
286         // Assuming that bytesPerRow is 256 and we are doing a B2T copy, and copy size is {width: 2,
287         // height: 4, depthOrArrayLayers: 3}. Then the data layout in buffer is demonstrated
288         // as below:
289         //
290         //               |<----- bytes per row ------>|
291         //
292         //               |----------------------------|
293         //  row (N - 1)  |                            |
294         //  row N        |                 ++~~~~~~~~~|
295         //  row (N + 1)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
296         //  row (N + 2)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
297         //  row (N + 3)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
298         //  row (N + 4)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
299         //  row (N + 5)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
300         //  row (N + 6)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
301         //  row (N + 7)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
302         //  row (N + 8)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
303         //  row (N + 9)  |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
304         //  row (N + 10) |~~~~~~~~~~~~~~~~~++~~~~~~~~~|
305         //  row (N + 11) |~~~~~~~~~~~~~~~~~++         |
306         //               |----------------------------|
307 
308         // The copy we mean to do is the following:
309         //
310         //   - image 0: row N to row (N + 3),
311         //   - image 1: row (N + 4) to row (N + 7),
312         //   - image 2: row (N + 8) to row (N + 11).
313         //
314         // Note that alignedOffset is at the beginning of row (N - 1), while buffer offset makes
315         // the copy start at row N. Row (N - 1) is the empty row between alignedOffset and offset.
316         //
317         // The 2D copy region of image 0 we received from Compute2DTextureCopySubresource() is
318         // the following:
319         //
320         //              |-------------------|
321         //  row (N - 1) |                   |
322         //  row N       |                 ++|
323         //  row (N + 1) |~~~~~~~~~~~~~~~~~++|
324         //  row (N + 2) |~~~~~~~~~~~~~~~~~++|
325         //  row (N + 3) |~~~~~~~~~~~~~~~~~++|
326         //              |-------------------|
327         //
328         // However, if we simply expand the copy region of image 0 to all depth ranges of a 3D
329         // texture, we will copy 5 rows every time, and every first row of each slice will be
330         // skipped. As a result, the copied data will be:
331         //
332         //   - image 0: row N to row (N + 3), which is correct. Row (N - 1) is skipped.
333         //   - image 1: row (N + 5) to row (N + 8) because row (N + 4) is skipped. It is incorrect.
334         //
335         // Likewise, all other image followed will be incorrect because we wrongly keep skipping
336         // one row for each depth slice.
337         //
338         // Solution: split the copy region to two copies: copy 3 (rowsPerImage - 1) rows in and
339         // expand to all depth slices in the first copy. 3 rows + one skipped rows = 4 rows, which
340         // equals to rowsPerImage. Then copy the last row in the second copy. However, the copy
341         // block of the last row of the last image may out-of-bound (see the details below), so
342         // we need an extra copy for the very last row.
343 
344         // Copy 0: copy 3 rows, not 4 rows.
345         //                _____________________
346         //               /                    /|
347         //              /                    / |
348         //              |-------------------|  |
349         //  row (N - 1) |                   |  |
350         //  row N       |                 ++|  |
351         //  row (N + 1) |~~~~~~~~~~~~~~~~~++| /
352         //  row (N + 2) |~~~~~~~~~~~~~~~~~++|/
353         //              |-------------------|
354 
355         // Copy 1: move down two rows and copy the last row on image 0, and expand to
356         // copySize.depthOrArrayLayers - 1 depth slices. Note that if we expand it to all depth
357         // slices, the last copy block will be row (N + 9) to row (N + 12). Row (N + 11) might
358         // be the last row of the entire buffer. Then row (N + 12) will be out-of-bound.
359         //                _____________________
360         //               /                    /|
361         //              /                    / |
362         //              |-------------------|  |
363         //  row (N + 1) |                   |  |
364         //  row (N + 2) |                   |  |
365         //  row (N + 3) |                 ++| /
366         //  row (N + 4) |~~~~~~~~~~~~~~~~~~~|/
367         //              |-------------------|
368         //
369         //  copy 2: copy the last row of the last image.
370         //              |-------------------|
371         //  row (N + 11)|                 ++|
372         //              |-------------------|
373 
374         // Copy 0: copy copySize.height - 1 rows
375         TextureCopySubresource::CopyInfo& copy0 = copy.copies[i];
376         copy0.copySize.height = copySize.height - blockInfo.height;
377         copy0.bufferSize.height = rowsPerImage * blockInfo.height;  // rowsPerImageInTexels
378 
379         // Copy 1: move down 2 rows and copy the last row on image 0, and expand to all depth slices
380         // but the last one.
381         TextureCopySubresource::CopyInfo* copy1 = copy.AddCopy();
382         *copy1 = copy0;
383         copy1->alignedOffset += 2 * bytesPerRow;
384         copy1->textureOffset.y += copySize.height - blockInfo.height;
385         // Offset two rows from the copy height for the bufferOffset (See the figure above):
386         //   - one for the row we advanced in the buffer: row (N + 4).
387         //   - one for the last row we want to copy: row (N + 3) itself.
388         copy1->bufferOffset.y = copySize.height - 2 * blockInfo.height;
389         copy1->copySize.height = blockInfo.height;
390         copy1->copySize.depthOrArrayLayers--;
391         copy1->bufferSize.depthOrArrayLayers--;
392 
393         // Copy 2: copy the last row of the last image.
394         uint64_t offsetForCopy0 = OffsetToFirstCopiedTexel(blockInfo, bytesPerRow,
395                                                            copy0.alignedOffset, copy0.bufferOffset);
396         uint64_t offsetForLastRowOfLastImage =
397             offsetForCopy0 + bytesPerRow * (copy0.copySize.height +
398                                             rowsPerImage * (copySize.depthOrArrayLayers - 1));
399         uint64_t alignedOffsetForLastRowOfLastImage =
400             AlignDownForDataPlacement(offsetForLastRowOfLastImage);
401         Origin3D texelOffsetForLastRowOfLastImage = ComputeTexelOffsets(
402             blockInfo,
403             static_cast<uint32_t>(offsetForLastRowOfLastImage - alignedOffsetForLastRowOfLastImage),
404             bytesPerRow);
405 
406         TextureCopySubresource::CopyInfo* copy2 = copy.AddCopy();
407         copy2->alignedOffset = alignedOffsetForLastRowOfLastImage;
408         copy2->textureOffset = copy1->textureOffset;
409         copy2->textureOffset.z = origin.z + copySize.depthOrArrayLayers - 1;
410         copy2->copySize = copy1->copySize;
411         copy2->copySize.depthOrArrayLayers = 1;
412         copy2->bufferOffset = texelOffsetForLastRowOfLastImage;
413         copy2->bufferSize.width = copy1->bufferSize.width;
414         ASSERT(copy2->copySize.height == 1);
415         copy2->bufferSize.height = copy2->bufferOffset.y + copy2->copySize.height;
416         copy2->bufferSize.depthOrArrayLayers = 1;
417     }
418 
Recompute3DTextureCopyRegionWithEmptyFirstRowAndOddCopyHeight(Extent3D copySize,uint32_t bytesPerRow,TextureCopySubresource & copy,uint32_t i)419     void Recompute3DTextureCopyRegionWithEmptyFirstRowAndOddCopyHeight(Extent3D copySize,
420                                                                        uint32_t bytesPerRow,
421                                                                        TextureCopySubresource& copy,
422                                                                        uint32_t i) {
423         // Read the comments of Recompute3DTextureCopyRegionWithEmptyFirstRowAndEvenCopyHeight() for
424         // the reason why it is incorrect if we simply extend the copy region to all depth slices
425         // when there is an empty first row at the copy region.
426         //
427         // If the copy height is odd, we can use two copies to make it correct:
428         //   - copy 0: only copy the first depth slice. Keep other arguments the same.
429         //   - copy 1: copy all rest depth slices because it will start without an empty row if
430         //     copy height is odd. Odd height + one (empty row) is even. An even row number times
431         //     bytesPerRow (256) will be aligned to D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512)
432 
433         // Copy 0: copy the first depth slice (image 0)
434         TextureCopySubresource::CopyInfo& copy0 = copy.copies[i];
435         copy0.copySize.depthOrArrayLayers = 1;
436         copy0.bufferSize.depthOrArrayLayers = 1;
437 
438         // Copy 1: copy the rest depth slices in one shot
439         TextureCopySubresource::CopyInfo* copy1 = copy.AddCopy();
440         *copy1 = copy0;
441         ASSERT(copySize.height % 2 == 1);
442         copy1->alignedOffset += (copySize.height + 1) * bytesPerRow;
443         ASSERT(copy1->alignedOffset % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT == 0);
444         // textureOffset.z should add one because the first slice has already been copied in copy0.
445         copy1->textureOffset.z++;
446         // bufferOffset.y should be 0 because we skipped the first depth slice and there is no empty
447         // row in this copy region.
448         copy1->bufferOffset.y = 0;
449         copy1->copySize.height = copySize.height;
450         copy1->copySize.depthOrArrayLayers = copySize.depthOrArrayLayers - 1;
451         copy1->bufferSize.height = copySize.height;
452         copy1->bufferSize.depthOrArrayLayers = copySize.depthOrArrayLayers - 1;
453     }
454 
Compute3DTextureCopySplits(Origin3D origin,Extent3D copySize,const TexelBlockInfo & blockInfo,uint64_t offset,uint32_t bytesPerRow,uint32_t rowsPerImage)455     TextureCopySubresource Compute3DTextureCopySplits(Origin3D origin,
456                                                       Extent3D copySize,
457                                                       const TexelBlockInfo& blockInfo,
458                                                       uint64_t offset,
459                                                       uint32_t bytesPerRow,
460                                                       uint32_t rowsPerImage) {
461         // To compute the copy region(s) for 3D textures, we call Compute2DTextureCopySubresource
462         // and get copy region(s) for the first slice of the copy, then extend to all depth slices
463         // and become a 3D copy. However, this doesn't work as easily as that due to some corner
464         // cases.
465         //
466         // For example, if bufferSize.height is greater than rowsPerImage in the generated copy
467         // region and we simply extend the 2D copy region to all copied depth slices, copied data
468         // will be incorrectly offset for each depth slice except the first one.
469         //
470         // For these special cases, we need to recompute the copy regions for 3D textures via
471         // split the incorrect copy region to a couple more copy regions.
472 
473         // Call Compute2DTextureCopySubresource and get copy regions. This function has already
474         // forwarded "copySize.depthOrArrayLayers" to all depth slices.
475         TextureCopySubresource copySubresource =
476             Compute2DTextureCopySubresource(origin, copySize, blockInfo, offset, bytesPerRow);
477 
478         ASSERT(copySubresource.count <= 2);
479         // If copySize.depth is 1, we can return copySubresource. Because we don't need to extend
480         // the copy region(s) to other depth slice(s).
481         if (copySize.depthOrArrayLayers == 1) {
482             return copySubresource;
483         }
484 
485         uint32_t rowsPerImageInTexels = rowsPerImage * blockInfo.height;
486         // The copy region(s) generated by Compute2DTextureCopySubresource might be incorrect.
487         // However, we may append a couple more copy regions in the for loop below. We don't need
488         // to revise these new added copy regions.
489         uint32_t originalCopyCount = copySubresource.count;
490         for (uint32_t i = 0; i < originalCopyCount; ++i) {
491             // There can be one empty row at most in a copy region.
492             ASSERT(copySubresource.copies[i].bufferSize.height <=
493                    rowsPerImageInTexels + blockInfo.height);
494             Extent3D& bufferSize = copySubresource.copies[i].bufferSize;
495 
496             if (bufferSize.height == rowsPerImageInTexels) {
497                 // If the copy region's bufferSize.height equals to rowsPerImageInTexels, we can use
498                 // this copy region without any modification.
499                 continue;
500             }
501 
502             if (bufferSize.height < rowsPerImageInTexels) {
503                 // If we are copying multiple depth slices, we should skip rowsPerImageInTexels rows
504                 // for each slice even though we only copy partial rows in each slice sometimes.
505                 bufferSize.height = rowsPerImageInTexels;
506             } else {
507                 // bufferSize.height > rowsPerImageInTexels. There is an empty row in this copy
508                 // region due to alignment adjustment.
509 
510                 // bytesPerRow is definitely 256, and it is definitely a full copy on height.
511                 // Otherwise, bufferSize.height wount be greater than rowsPerImageInTexels and
512                 // there won't be an empty row at the beginning of this copy region.
513                 ASSERT(bytesPerRow == D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
514                 ASSERT(copySize.height == rowsPerImageInTexels);
515 
516                 if (copySize.height % 2 == 0) {
517                     // If copySize.height is even and there is an empty row at the beginning of the
518                     // first slice of the copy region, the offset of all depth slices will never be
519                     // aligned to D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512) and there is always
520                     // an empty row at each depth slice. We need a totally different approach to
521                     // split the copy region.
522                     Recompute3DTextureCopyRegionWithEmptyFirstRowAndEvenCopyHeight(
523                         origin, copySize, blockInfo, bytesPerRow, rowsPerImage, copySubresource, i);
524                 } else {
525                     // If copySize.height is odd and there is an empty row at the beginning of the
526                     // first slice of the copy region, we can split the copy region into two copies:
527                     // copy0 to copy the first slice, copy1 to copy the rest slices because the
528                     // offset of slice 1 is aligned to D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512)
529                     // without an empty row. This is an easier case relative to cases with even copy
530                     // height.
531                     Recompute3DTextureCopyRegionWithEmptyFirstRowAndOddCopyHeight(
532                         copySize, bytesPerRow, copySubresource, i);
533                 }
534             }
535         }
536 
537         return copySubresource;
538     }
539 }}  // namespace dawn_native::d3d12
540