use super::image::Pixel;
use crate::wgpu;
use std::ops::Deref;
#[derive(Debug)]
pub struct RowPaddedBuffer {
width: u32,
row_padding: u32,
height: u32,
buffer: wgpu::Buffer,
buffer_descriptor: wgpu::BufferDescriptor<'static>,
}
impl RowPaddedBuffer {
pub fn new(
device: &wgpu::Device,
width: u32,
height: u32,
usage: wgpu::BufferUsage,
) -> RowPaddedBuffer {
let row_padding = RowPaddedBuffer::compute_row_padding(width);
let mapped_at_creation = usage.contains(wgpu::BufferUsage::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);
RowPaddedBuffer {
width,
row_padding,
height,
buffer,
buffer_descriptor,
}
}
pub fn from_image_buffer<P, Container>(
device: &wgpu::Device,
image_buffer: &image::ImageBuffer<P, Container>,
) -> RowPaddedBuffer
where
P: 'static + Pixel,
Container: std::ops::Deref<Target = [P::Subpixel]>,
{
let result = RowPaddedBuffer::new(
device,
image_buffer.width() * P::COLOR_TYPE.bytes_per_pixel() as u32,
image_buffer.height(),
wgpu::BufferUsage::MAP_WRITE | wgpu::BufferUsage::COPY_SRC,
);
result.write(unsafe { wgpu::bytes::from_slice(&*image_buffer) });
result
}
pub fn for_texture(
device: &wgpu::Device,
texture: &wgpu::Texture,
usage: wgpu::BufferUsage,
) -> RowPaddedBuffer {
RowPaddedBuffer::new(
device,
texture.extent().width * wgpu::texture_format_size_bytes(texture.format()),
texture.extent().height,
usage,
)
}
fn compute_row_padding(width: u32) -> u32 {
wgpu::COPY_BYTES_PER_ROW_ALIGNMENT - (width % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT)
}
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::BufferUsage::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 async fn read<'b>(&'b self) -> Result<ImageReadMapping<'b>, wgpu::BufferAsyncError> {
let slice = self.buffer.slice(..);
slice.map_async(wgpu::MapMode::Read).await?;
Ok(wgpu::ImageReadMapping {
buffer: self,
view: slice.get_mapped_range(),
})
}
pub fn encode_copy_into(
&self,
encoder: &mut wgpu::CommandEncoder,
destination: &wgpu::Texture,
) {
assert_eq!(
destination.extent().depth,
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,
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::BufferCopyView<'s>,
wgpu::TextureCopyView<'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, "texture not deep enough");
let mut copy_size = texture.extent();
copy_size.depth = 1;
let buffer_view = wgpu::BufferCopyView {
buffer: &self.buffer,
layout: wgpu::TextureDataLayout {
offset: 0,
bytes_per_row: self.padded_width(),
rows_per_image: self.height,
},
};
let texture_view = wgpu::TextureCopyView {
texture,
mip_level: 0,
origin: wgpu::Origin3d {
x: 0,
y: 0,
z: depth,
},
};
(buffer_view, texture_view, copy_size)
}
}
pub struct ImageReadMapping<'buffer> {
buffer: &'buffer wgpu::RowPaddedBuffer,
view: wgpu::BufferView<'buffer>,
}
impl<'buffer> ImageReadMapping<'buffer> {
pub unsafe fn as_image<P>(&self) -> image::SubImage<ImageHolder<P>>
where
P: Pixel + 'static,
{
let subpixel_size = std::mem::size_of::<P::Subpixel>() as u32;
let pixel_size = subpixel_size * P::CHANNEL_COUNT as u32;
assert_eq!(pixel_size, P::COLOR_TYPE.bytes_per_pixel() as u32);
assert_eq!(
self.buffer.padded_width() % pixel_size,
0,
"buffer padded width not an even multiple of primitive size"
);
assert_eq!(
self.buffer.width() % pixel_size,
0,
"buffer row width not an even multiple of primitive size"
);
let width_pixels = self.buffer.width() / pixel_size;
let padded_width_pixels = self.buffer.padded_width() / pixel_size;
let container = wgpu::bytes::to_slice::<P::Subpixel>(&self.view[..]);
let full_image =
image::ImageBuffer::from_raw(padded_width_pixels, self.buffer.height(), container)
.expect("nannou internal error: incorrect buffer size");
image::SubImage::new(
ImageHolder(full_image),
0,
0,
width_pixels,
self.buffer.height(),
)
}
}
pub struct ImageHolder<'b, P: Pixel>(image::ImageBuffer<P, &'b [P::Subpixel]>);
impl<'b, P: Pixel> Deref for ImageHolder<'b, P> {
type Target = image::ImageBuffer<P, &'b [P::Subpixel]>;
fn deref(&self) -> &Self::Target {
&self.0
}
}