Source code
Revision control
Copy as Markdown
Other Tools
use crate::{BufferAddress, Extent3d, TexelCopyBufferLayout, TextureAspect, TextureFormat};
impl TexelCopyBufferLayout {
/// Extract a variety of information about the given copy operation.
#[inline(always)]
pub fn get_buffer_texture_copy_info(
&self,
format: TextureFormat,
aspect: TextureAspect,
copy_size: &Extent3d,
) -> BufferTextureCopyInfo {
// Convert all inputs to BufferAddress (u64) to avoid some of the overflow issues
// Note: u64 is not always enough to prevent overflow, especially when multiplying
// something with a potentially large depth value, so it is preferable to validate
// the copy size before calling this function (for example via `validate_texture_copy_range`).
let copy_width = copy_size.width as BufferAddress;
let copy_height = copy_size.height as BufferAddress;
let depth_or_array_layers = copy_size.depth_or_array_layers as BufferAddress;
let block_size_bytes = format.block_copy_size(Some(aspect)).unwrap() as BufferAddress;
let (block_width, block_height) = format.block_dimensions();
let block_width_texels = block_width as BufferAddress;
let block_height_texels = block_height as BufferAddress;
let width_blocks = copy_width.div_ceil(block_width_texels);
let height_blocks = copy_height.div_ceil(block_height_texels);
let row_bytes_dense = width_blocks * block_size_bytes;
let row_stride_bytes = self.bytes_per_row.map_or(row_bytes_dense, u64::from);
let image_stride_rows = self.rows_per_image.map_or(height_blocks, u64::from);
let image_stride_bytes = row_stride_bytes * image_stride_rows;
let image_rows_dense = height_blocks;
let image_bytes_dense =
image_rows_dense.saturating_sub(1) * row_stride_bytes + row_bytes_dense;
let mut bytes_in_copy = image_stride_bytes * depth_or_array_layers.saturating_sub(1);
if height_blocks > 0 {
bytes_in_copy += row_stride_bytes * (height_blocks - 1) + row_bytes_dense;
}
BufferTextureCopyInfo {
copy_width,
copy_height,
depth_or_array_layers,
offset: self.offset,
block_size_bytes,
block_width_texels,
block_height_texels,
width_blocks,
height_blocks,
row_bytes_dense,
row_stride_bytes,
image_stride_rows,
image_stride_bytes,
image_rows_dense,
image_bytes_dense,
bytes_in_copy,
}
}
}
/// Information about a copy between a buffer and a texture.
///
/// Mostly used for internal calculations, but useful nonetheless.
/// Generated by [`TexelCopyBufferLayout::get_buffer_texture_copy_info`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BufferTextureCopyInfo {
/// The width of the copy region in pixels.
pub copy_width: u64,
/// The height of the copy region in pixels.
pub copy_height: u64,
/// The depth of the copy region in pixels.
pub depth_or_array_layers: u64,
/// The offset in the buffer where the copy starts.
pub offset: u64,
/// The size of a single texture texel block in bytes.
pub block_size_bytes: u64,
/// The number of texel in a texel block in the x direction.
pub block_width_texels: u64,
/// The number of texel in a texel block in the y direction.
pub block_height_texels: u64,
/// The width of the copy region in blocks.
pub width_blocks: u64,
/// The height of the copy region in blocks.
pub height_blocks: u64,
/// The number of bytes in the last row of the copy region.
pub row_bytes_dense: u64,
/// The stride in bytes between the start of one row in an image and the next row in the same image.
///
/// This includes any padding between one row and the next row.
pub row_stride_bytes: u64,
/// The stride in rows between the start of one image and the next image.
pub image_stride_rows: u64,
/// The stride in bytes between the start of one image and the next image.
pub image_stride_bytes: u64,
/// The number of rows in a densely packed list of images.
///
/// This is the number of rows in the image that are actually used for texel data,
/// and does not include any padding rows, unlike `image_stride_rows`.
pub image_rows_dense: u64,
/// The number of bytes in a densely packed list of images.
///
/// This is the number of bytes in the image that are actually used for texel data,
/// or are used for padding between _rows_. Padding at the end of the last row and
/// between _images_ is not included.
pub image_bytes_dense: u64,
/// The total number of bytes in the copy region.
///
/// This includes all padding except the padding after the last row in the copy.
pub bytes_in_copy: u64,
}
#[cfg(test)]
mod tests {
use super::*;
struct LTDTest {
layout: TexelCopyBufferLayout,
format: TextureFormat,
aspect: TextureAspect,
copy_size: Extent3d,
expected_result: BufferTextureCopyInfo,
}
impl LTDTest {
#[track_caller]
fn run(&self) {
let linear_texture_data =
self.layout
.get_buffer_texture_copy_info(self.format, self.aspect, &self.copy_size);
assert_eq!(linear_texture_data, self.expected_result);
}
}
#[test]
fn linear_texture_data_1d_copy() {
let mut test = LTDTest {
layout: TexelCopyBufferLayout {
offset: 0,
bytes_per_row: None,
rows_per_image: None,
},
format: TextureFormat::Rgba8Unorm,
aspect: TextureAspect::All,
copy_size: Extent3d {
width: 4,
height: 1,
depth_or_array_layers: 1,
},
expected_result: BufferTextureCopyInfo {
copy_width: 4,
copy_height: 1,
depth_or_array_layers: 1,
offset: 0,
block_size_bytes: 4,
block_width_texels: 1,
block_height_texels: 1,
width_blocks: 4,
height_blocks: 1,
row_bytes_dense: 16,
row_stride_bytes: 16,
image_stride_rows: 1,
image_stride_bytes: 16,
image_rows_dense: 1,
image_bytes_dense: 16,
bytes_in_copy: 16,
},
};
test.run();
// Changing bytes_per_row should only change the bytes_per_row, not the bytes_in_copy
// as that is only affected by the last row size.
test.layout.bytes_per_row = Some(32);
test.expected_result.row_stride_bytes = 32;
test.expected_result.image_stride_bytes = 32;
test.run();
// Changing rows_per_image should only change the rows_per_image and bytes_per_image, nothing else
test.layout.rows_per_image = Some(4);
test.expected_result.image_stride_bytes = 128; // 32 * 4
test.expected_result.image_stride_rows = 4;
test.run();
// Changing the offset should change nothing.
test.layout.offset = 4;
test.expected_result.offset = 4;
test.run();
}
#[test]
fn linear_texture_data_2d_3d_copy() {
let mut test = LTDTest {
layout: TexelCopyBufferLayout {
offset: 0,
bytes_per_row: None,
rows_per_image: None,
},
format: TextureFormat::Rgba8Unorm,
aspect: TextureAspect::All,
copy_size: Extent3d {
width: 7,
height: 12,
depth_or_array_layers: 1,
},
expected_result: BufferTextureCopyInfo {
copy_width: 7,
copy_height: 12,
depth_or_array_layers: 1,
offset: 0,
block_size_bytes: 4,
block_width_texels: 1,
block_height_texels: 1,
width_blocks: 7,
height_blocks: 12,
row_bytes_dense: 4 * 7,
row_stride_bytes: 4 * 7,
image_stride_rows: 12,
image_stride_bytes: 4 * 7 * 12,
image_rows_dense: 12,
image_bytes_dense: 4 * 7 * 12,
bytes_in_copy: 4 * 7 * 12,
},
};
test.run();
// Changing bytes_per_row changes a number of other properties.
test.layout.bytes_per_row = Some(48);
test.expected_result.row_stride_bytes = 48;
test.expected_result.image_stride_bytes = 48 * 12;
test.expected_result.image_bytes_dense = 48 * 11 + (4 * 7);
test.expected_result.bytes_in_copy = 48 * 11 + (4 * 7);
// Making this a 3D copy only changes the depth_or_array_layers and the bytes_in_copy.
test.copy_size.depth_or_array_layers = 4;
test.expected_result.depth_or_array_layers = 4;
test.expected_result.bytes_in_copy = 48 * 12 * 3 + 48 * 11 + (4 * 7); // 4 layers
test.run();
}
#[test]
fn linear_texture_data_2d_3d_compressed_copy() {
let mut test = LTDTest {
layout: TexelCopyBufferLayout {
offset: 0,
bytes_per_row: None,
rows_per_image: None,
},
format: TextureFormat::Bc1RgbaUnorm,
aspect: TextureAspect::All,
copy_size: Extent3d {
width: 7,
height: 13,
depth_or_array_layers: 1,
},
expected_result: BufferTextureCopyInfo {
copy_width: 7,
copy_height: 13,
depth_or_array_layers: 1,
offset: 0,
block_size_bytes: 8,
block_width_texels: 4,
block_height_texels: 4,
width_blocks: 2,
height_blocks: 4,
row_bytes_dense: 8 * 2, // block size * width_blocks
row_stride_bytes: 8 * 2,
image_stride_rows: 4,
image_stride_bytes: 8 * 2 * 4, // block size * width_blocks * height_blocks
image_rows_dense: 4,
image_bytes_dense: 8 * 2 * 4,
bytes_in_copy: 8 * 2 * 4,
},
};
test.run();
// Changing bytes_per_row.
test.layout.bytes_per_row = Some(48);
test.expected_result.row_stride_bytes = 48;
test.expected_result.image_stride_bytes = 48 * 4;
test.expected_result.image_bytes_dense = 48 * 3 + (8 * 2);
test.expected_result.bytes_in_copy = 48 * 3 + (8 * 2);
test.run();
// Changing rows_per_image.
test.layout.rows_per_image = Some(8);
test.expected_result.image_stride_bytes = 48 * 8;
test.expected_result.image_stride_rows = 8;
test.run();
// Making this a 3D copy only changes the depth_or_array_layers and the bytes_in_copy.
test.copy_size.depth_or_array_layers = 4;
test.expected_result.depth_or_array_layers = 4;
test.expected_result.bytes_in_copy = 48 * 8 * 3 + 48 * 3 + (8 * 2); // 4 layers
test.run();
}
}