1use crate::{BufferAddress, Extent3d, TexelCopyBufferLayout, TextureAspect, TextureFormat};
2
3impl TexelCopyBufferLayout {
4 #[doc(hidden)]
17 #[inline(always)]
18 pub fn get_buffer_texture_copy_info(
19 &self,
20 format: TextureFormat,
21 aspect: TextureAspect,
22 copy_size: &Extent3d,
23 ) -> Result<BufferTextureCopyInfo, Error> {
24 let copy_width = BufferAddress::from(copy_size.width);
25 let copy_height = BufferAddress::from(copy_size.height);
26 let depth_or_array_layers = BufferAddress::from(copy_size.depth_or_array_layers);
27
28 let block_size_bytes = BufferAddress::from(format.block_copy_size(Some(aspect)).unwrap());
29 let (block_width, block_height) = format.block_dimensions();
30 let block_width_texels = BufferAddress::from(block_width);
31 let block_height_texels = BufferAddress::from(block_height);
32
33 let width_blocks = copy_width.div_ceil(block_width_texels);
34 let height_blocks = copy_height.div_ceil(block_height_texels);
35
36 let row_bytes_dense = width_blocks * block_size_bytes;
38 let row_stride_bytes = match self.bytes_per_row.map(BufferAddress::from) {
39 Some(bytes_per_row) if bytes_per_row >= row_bytes_dense => bytes_per_row,
40 Some(_) => return Err(Error::InvalidBytesPerRow),
41 None => row_bytes_dense,
42 };
43
44 let image_rows_dense = height_blocks;
45 let image_stride_rows = match self.rows_per_image.map(BufferAddress::from) {
46 Some(rows_per_image) if rows_per_image >= image_rows_dense => rows_per_image,
47 Some(_) => return Err(Error::InvalidRowsPerImage),
48 None => image_rows_dense,
49 };
50
51 let image_bytes_dense = match image_rows_dense.checked_sub(1) {
52 Some(rows_minus_one) => rows_minus_one
53 .checked_mul(row_stride_bytes)
54 .ok_or(Error::ImageBytesOverflow(false))?
55 .checked_add(row_bytes_dense)
56 .ok_or(Error::ImageBytesOverflow(true))?,
57 None => 0,
58 };
59
60 let image_stride_bytes = row_stride_bytes
66 .checked_mul(image_stride_rows)
67 .ok_or(Error::ImageStrideOverflow)?;
68
69 let bytes_in_copy = if depth_or_array_layers <= 1 {
70 depth_or_array_layers * image_bytes_dense
71 } else {
72 (depth_or_array_layers - 1)
73 .checked_mul(image_stride_bytes)
74 .ok_or(Error::ArraySizeOverflow(false))?
75 .checked_add(image_bytes_dense)
76 .ok_or(Error::ArraySizeOverflow(true))?
77 };
78
79 Ok(BufferTextureCopyInfo {
80 copy_width,
81 copy_height,
82 depth_or_array_layers,
83
84 offset: self.offset,
85
86 block_size_bytes,
87 block_width_texels,
88 block_height_texels,
89
90 width_blocks,
91 height_blocks,
92
93 row_bytes_dense,
94 row_stride_bytes,
95
96 image_stride_rows,
97 image_stride_bytes,
98
99 image_rows_dense,
100 image_bytes_dense,
101
102 bytes_in_copy,
103 })
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub struct BufferTextureCopyInfo {
113 pub copy_width: u64,
115 pub copy_height: u64,
117 pub depth_or_array_layers: u64,
119
120 pub offset: u64,
122
123 pub block_size_bytes: u64,
125 pub block_width_texels: u64,
127 pub block_height_texels: u64,
129
130 pub width_blocks: u64,
132 pub height_blocks: u64,
134
135 pub row_bytes_dense: u64,
137 pub row_stride_bytes: u64,
141
142 pub image_stride_rows: u64,
144 pub image_stride_bytes: u64,
146
147 pub image_rows_dense: u64,
152 pub image_bytes_dense: u64,
158
159 pub bytes_in_copy: u64,
163}
164
165#[derive(Clone, Copy, Debug, Eq, PartialEq)]
178pub enum BufferTextureCopyInfoError {
179 InvalidBytesPerRow,
181 InvalidRowsPerImage,
183 ImageStrideOverflow,
185 ImageBytesOverflow(bool),
190 ArraySizeOverflow(bool),
195}
196type Error = BufferTextureCopyInfoError;
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[derive(Clone)]
203 struct LTDTest {
204 layout: TexelCopyBufferLayout,
205 format: TextureFormat,
206 aspect: TextureAspect,
207 copy_size: Extent3d,
208 expected_result: BufferTextureCopyInfo,
209 expected_error: Option<Error>,
214 }
215
216 impl LTDTest {
217 #[track_caller]
218 fn run(&self) {
219 let linear_texture_data =
220 self.layout
221 .get_buffer_texture_copy_info(self.format, self.aspect, &self.copy_size);
222 let expected = match self.expected_error {
223 Some(err) => Err(err),
224 None => Ok(self.expected_result),
225 };
226 assert_eq!(linear_texture_data, expected);
227 }
228 }
229
230 #[test]
231 fn linear_texture_data_1d_copy() {
232 let mut test = LTDTest {
233 layout: TexelCopyBufferLayout {
234 offset: 0,
235 bytes_per_row: None,
236 rows_per_image: None,
237 },
238 format: TextureFormat::Rgba8Unorm,
239 aspect: TextureAspect::All,
240 copy_size: Extent3d {
241 width: 4,
242 height: 1,
243 depth_or_array_layers: 1,
244 },
245 expected_result: BufferTextureCopyInfo {
246 copy_width: 4,
247 copy_height: 1,
248 depth_or_array_layers: 1,
249 offset: 0,
250 block_size_bytes: 4,
251 block_width_texels: 1,
252 block_height_texels: 1,
253 width_blocks: 4,
254 height_blocks: 1,
255 row_bytes_dense: 16,
256 row_stride_bytes: 16,
257 image_stride_rows: 1,
258 image_stride_bytes: 16,
259 image_rows_dense: 1,
260 image_bytes_dense: 16,
261 bytes_in_copy: 16,
262 },
263 expected_error: None,
264 };
265
266 test.run();
267
268 test.layout.bytes_per_row = Some(32);
271 test.expected_result.row_stride_bytes = 32;
272 test.expected_result.image_stride_bytes = 32;
273
274 test.run();
275
276 test.layout.rows_per_image = Some(4);
278 test.expected_result.image_stride_bytes = 128; test.expected_result.image_stride_rows = 4;
280
281 test.run();
282
283 test.layout.offset = 4;
286 test.expected_result.offset = 4;
287
288 test.run();
289 }
290
291 #[test]
292 fn linear_texture_data_2d_3d_copy() {
293 let template = LTDTest {
294 layout: TexelCopyBufferLayout {
295 offset: 0,
296 bytes_per_row: None,
297 rows_per_image: None,
298 },
299 format: TextureFormat::Rgba8Unorm,
300 aspect: TextureAspect::All,
301 copy_size: Extent3d {
302 width: 7,
303 height: 12,
304 depth_or_array_layers: 1,
305 },
306 expected_result: BufferTextureCopyInfo {
307 copy_width: 7,
308 copy_height: 12,
309 depth_or_array_layers: 1,
310 offset: 0,
311 block_size_bytes: 4,
312 block_width_texels: 1,
313 block_height_texels: 1,
314 width_blocks: 7,
315 height_blocks: 12,
316 row_bytes_dense: 4 * 7,
317 row_stride_bytes: 4 * 7,
318 image_stride_rows: 12,
319 image_stride_bytes: 4 * 7 * 12,
320 image_rows_dense: 12,
321 image_bytes_dense: 4 * 7 * 12,
322 bytes_in_copy: 4 * 7 * 12,
323 },
324 expected_error: None,
325 };
326
327 let mut test = template.clone();
328 test.run();
329
330 test.layout.bytes_per_row = Some(48);
332 test.expected_result.row_stride_bytes = 48;
333 test.expected_result.image_stride_bytes = 48 * 12;
334 test.expected_result.image_bytes_dense = 48 * 11 + (4 * 7);
335 test.expected_result.bytes_in_copy = 48 * 11 + (4 * 7);
336 test.run();
337
338 test.copy_size.depth_or_array_layers = 4;
340 test.expected_result.depth_or_array_layers = 4;
341 test.expected_result.bytes_in_copy = 48 * 12 * 3 + 48 * 11 + (4 * 7); test.run();
343
344 test.layout.rows_per_image = Some(20);
346 test.expected_result.image_stride_rows = 20;
347 test.expected_result.image_stride_bytes = 20 * test.expected_result.row_stride_bytes;
348 test.expected_result.bytes_in_copy = 48 * 20 * 3 + 48 * 11 + (4 * 7); test.run();
350
351 let mut test = template.clone();
353 test.layout.bytes_per_row = Some(20);
354 test.expected_error = Some(Error::InvalidBytesPerRow);
355 test.run();
356
357 let mut test = template.clone();
359 test.layout.rows_per_image = Some(8);
360 test.expected_error = Some(Error::InvalidRowsPerImage);
361 test.run();
362
363 let mut test = template.clone();
365 test.copy_size.width = u32::MAX;
366 test.copy_size.height = u32::MAX;
367 test.expected_error = Some(Error::ImageBytesOverflow(false));
368 test.run();
369
370 let mut test = template.clone();
373 test.copy_size.width = 0x8000_0000;
374 test.copy_size.height = 0x8000_0000;
375 test.expected_error = Some(Error::ImageBytesOverflow(true));
376 test.run();
377
378 let mut test = template.clone();
380 test.copy_size.width = 0x8000_0000;
381 test.layout.rows_per_image = Some(0x8000_0000);
382 test.expected_result.image_stride_rows = 0x8000_0000;
383 test.expected_error = Some(Error::ImageStrideOverflow);
384 test.run();
385
386 let mut test = template.clone();
388 test.copy_size.depth_or_array_layers = 0x8000_0000;
389 test.copy_size.width = 0x1_0000;
390 test.copy_size.height = 0x1_0000;
391 test.expected_error = Some(Error::ArraySizeOverflow(false));
392 test.run();
393
394 let mut test = template.clone();
397 test.copy_size.depth_or_array_layers = 0x3fff_8001;
398 test.copy_size.width = 0x1_0001;
399 test.copy_size.height = 0x1_0001;
400 test.expected_error = Some(Error::ArraySizeOverflow(true));
401 test.run();
402 }
403
404 #[test]
405 fn linear_texture_data_2d_3d_compressed_copy() {
406 let mut test = LTDTest {
407 layout: TexelCopyBufferLayout {
408 offset: 0,
409 bytes_per_row: None,
410 rows_per_image: None,
411 },
412 format: TextureFormat::Bc1RgbaUnorm,
413 aspect: TextureAspect::All,
414 copy_size: Extent3d {
415 width: 7,
416 height: 13,
417 depth_or_array_layers: 1,
418 },
419 expected_result: BufferTextureCopyInfo {
420 copy_width: 7,
421 copy_height: 13,
422 depth_or_array_layers: 1,
423 offset: 0,
424 block_size_bytes: 8,
425 block_width_texels: 4,
426 block_height_texels: 4,
427 width_blocks: 2,
428 height_blocks: 4,
429 row_bytes_dense: 8 * 2, row_stride_bytes: 8 * 2,
431 image_stride_rows: 4,
432 image_stride_bytes: 8 * 2 * 4, image_rows_dense: 4,
434 image_bytes_dense: 8 * 2 * 4,
435 bytes_in_copy: 8 * 2 * 4,
436 },
437 expected_error: None,
438 };
439
440 test.run();
441
442 test.layout.bytes_per_row = Some(48);
444 test.expected_result.row_stride_bytes = 48;
445 test.expected_result.image_stride_bytes = 48 * 4;
446 test.expected_result.image_bytes_dense = 48 * 3 + (8 * 2);
447 test.expected_result.bytes_in_copy = 48 * 3 + (8 * 2);
448
449 test.run();
450
451 test.layout.rows_per_image = Some(8);
453 test.expected_result.image_stride_bytes = 48 * 8;
454 test.expected_result.image_stride_rows = 8;
455
456 test.run();
457
458 test.copy_size.depth_or_array_layers = 4;
460 test.expected_result.depth_or_array_layers = 4;
461 test.expected_result.bytes_in_copy = 48 * 8 * 3 + 48 * 3 + (8 * 2); test.run();
464 }
465}