use crate as wgpu;
#[derive(Debug)]
pub struct RowPaddedBuffer {
width: u32,
row_padding: u32,
height: u32,
pub(crate) buffer: wgpu::Buffer,
buffer_descriptor: wgpu::BufferDescriptor<'static>,
}
impl RowPaddedBuffer {
pub fn new(device: &wgpu::Device, width: u32, height: u32, usage: wgpu::BufferUsages) -> Self {
let row_padding = wgpu::compute_row_padding(width);
let mapped_at_creation = usage.contains(wgpu::BufferUsages::MAP_WRITE);
let buffer_descriptor = wgpu::BufferDescriptor {
label: Some("nannou::RowPaddedBuffer"),
size: ((width + row_padding) * height) as u64,
usage,
mapped_at_creation,
};
let buffer = device.create_buffer(&buffer_descriptor);
Self {
width,
row_padding,
height,
buffer,
buffer_descriptor,
}
}
pub fn for_texture(
device: &wgpu::Device,
texture: &wgpu::Texture,
usage: wgpu::BufferUsages,
) -> RowPaddedBuffer {
Self::new(
device,
texture.extent().width * wgpu::texture_format_size_bytes(texture.format()),
texture.extent().height,
usage,
)
}
pub fn width(&self) -> u32 {
self.width
}
pub fn row_padding(&self) -> u32 {
self.row_padding
}
pub fn padded_width(&self) -> u32 {
self.width + self.row_padding
}
pub fn height(&self) -> u32 {
self.height
}
pub fn write(&self, buf: &[u8]) {
assert_eq!(
(self.width * self.height) as usize,
buf.len(),
"Incorrect input slice size"
);
assert!(
self.buffer_descriptor
.usage
.contains(wgpu::BufferUsages::MAP_WRITE),
"Wrapped buffer cannot be mapped for writing"
);
let mut mapped = self.buffer.slice(..).get_mapped_range_mut();
let mapped = &mut mapped[..];
let width = self.width as usize;
let padded_width = width + self.row_padding as usize;
let height = self.height as usize;
for row in 0..height {
let in_start = row * width;
let out_start = row * padded_width;
mapped[out_start..out_start + width].copy_from_slice(&buf[in_start..in_start + width]);
}
}
pub fn encode_copy_into(
&self,
encoder: &mut wgpu::CommandEncoder,
destination: &wgpu::Texture,
) {
assert_eq!(
destination.extent().depth_or_array_layers,
1,
"use encode_copy_into_at for 3d textures"
);
self.encode_copy_into_at(encoder, destination, 0);
}
pub fn encode_copy_into_at(
&self,
encoder: &mut wgpu::CommandEncoder,
destination: &wgpu::Texture,
depth: u32,
) {
let (source, destination, copy_size) = self.copy_views(destination, depth);
encoder.copy_buffer_to_texture(source, destination, copy_size);
}
pub fn encode_copy_from(&self, encoder: &mut wgpu::CommandEncoder, source: &wgpu::Texture) {
assert_eq!(
source.extent().depth_or_array_layers,
1,
"use encode_copy_from_at for 3d textures"
);
self.encode_copy_from_at(encoder, source, 0);
}
pub fn encode_copy_from_at(
&self,
encoder: &mut wgpu::CommandEncoder,
source: &wgpu::Texture,
depth: u32,
) {
let (destination, source, copy_size) = self.copy_views(source, depth);
encoder.copy_texture_to_buffer(source, destination, copy_size);
}
fn copy_views<'s, 't>(
&'s self,
texture: &'t wgpu::Texture,
depth: u32,
) -> (
wgpu::ImageCopyBuffer<'s>,
wgpu::ImageCopyTexture<'t>,
wgpu::Extent3d,
) {
let format_size_bytes = wgpu::texture_format_size_bytes(texture.format());
assert_eq!(
self.width % format_size_bytes,
0,
"buffer rows do not map evenly onto texture rows"
);
assert_eq!(
texture.extent().width,
self.width / format_size_bytes,
"buffer rows are the wrong width"
);
assert_eq!(
texture.extent().height,
self.height,
"buffer is the wrong height"
);
assert!(
depth <= texture.extent().depth_or_array_layers,
"texture not deep enough"
);
let mut copy_size = texture.extent();
copy_size.depth_or_array_layers = 1;
let buffer_view = wgpu::ImageCopyBuffer {
buffer: &self.buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(self.padded_width()),
rows_per_image: Some(self.height),
},
};
let texture_view = texture.as_image_copy();
(buffer_view, texture_view, copy_size)
}
}