use crate::{DeviceHandle, block_on_wgpu, util::create_texture};
use wgpu::{
BufferDescriptor, BufferUsages, CommandEncoderDescriptor, Device, Extent3d, Queue,
TexelCopyBufferInfo, TexelCopyBufferLayout, TextureFormat, TextureUsages, TextureView,
};
#[derive(Clone, Debug)]
pub struct BufferRendererConfig {
pub width: u32,
pub height: u32,
pub usage: TextureUsages,
}
pub struct BufferRenderer {
pub dev_id: usize,
pub device_handle: DeviceHandle,
config: BufferRendererConfig,
texture_view: wgpu::TextureView,
gpu_buffer: wgpu::Buffer,
}
impl std::fmt::Debug for BufferRenderer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SurfaceRenderer")
.field("dev_id", &self.dev_id)
.field("config", &self.config)
.finish()
}
}
impl BufferRenderer {
pub fn new(config: BufferRendererConfig, device_handle: DeviceHandle, dev_id: usize) -> Self {
let texture_view = create_texture(
config.width,
config.height,
TextureFormat::Rgba8Unorm,
config.usage | TextureUsages::COPY_SRC,
&device_handle.device,
);
let padded_byte_width = (config.width * 4).next_multiple_of(256);
let buffer_size = padded_byte_width as u64 * config.height as u64;
let gpu_buffer = device_handle.device.create_buffer(&BufferDescriptor {
label: None,
size: buffer_size,
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
Self {
dev_id,
device_handle,
config,
texture_view,
gpu_buffer,
}
}
pub fn device(&self) -> &Device {
&self.device_handle.device
}
pub fn queue(&self) -> &Queue {
&self.device_handle.queue
}
pub fn size(&self) -> Extent3d {
Extent3d {
width: self.config.width,
height: self.config.height,
depth_or_array_layers: 1,
}
}
pub fn resize(&mut self, width: u32, height: u32) {
self.config.width = width;
self.config.height = height;
self.texture_view = create_texture(
self.config.width,
self.config.height,
TextureFormat::Rgba8Unorm,
self.config.usage | TextureUsages::COPY_SRC,
&self.device_handle.device,
);
let padded_byte_width = (width * 4).next_multiple_of(256);
let buffer_size = padded_byte_width as u64 * height as u64;
self.gpu_buffer = self.device_handle.device.create_buffer(&BufferDescriptor {
label: None,
size: buffer_size,
usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
}
pub fn target_texture_view(&self) -> TextureView {
self.texture_view.clone()
}
pub fn copy_texture_to_vec(&self, cpu_buffer: &mut Vec<u8>) {
cpu_buffer.clear();
cpu_buffer.reserve((self.config.width * self.config.height * 4) as usize);
self.copy_texture_to_buffer(&mut *cpu_buffer);
}
pub fn copy_texture_to_buffer(&self, cpu_buffer: &mut [u8]) {
let mut encoder = self
.device()
.create_command_encoder(&CommandEncoderDescriptor {
label: Some("Copy out buffer"),
});
let row_byte_width = self.config.width as usize * 4;
let padded_row_byte_width = row_byte_width.next_multiple_of(256);
let texture = self.texture_view.texture();
encoder.copy_texture_to_buffer(
texture.as_image_copy(),
TexelCopyBufferInfo {
buffer: &self.gpu_buffer,
layout: TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(padded_row_byte_width as u32),
rows_per_image: None,
},
},
texture.size(),
);
self.queue().submit([encoder.finish()]);
let buf_slice = self.gpu_buffer.slice(..);
let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
buf_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
if let Ok(recv_result) =
block_on_wgpu(self.device(), receiver.receive()).inspect_err(|err| {
panic!("channel inaccessible: {:#}", err);
})
{
let _ = recv_result.unwrap();
}
let data = buf_slice.get_mapped_range();
for row in 0..(self.config.height as usize) {
let src_start = row * padded_row_byte_width;
let src = &data[src_start..(src_start + row_byte_width)];
let dest_start = row * row_byte_width;
cpu_buffer[dest_start..(dest_start + row_byte_width)].clone_from_slice(src);
}
drop(data);
self.gpu_buffer.unmap();
}
}