2020-08-27 10:32:41 +00:00
|
|
|
use crate::utils::BufferDimensions;
|
2020-05-05 13:27:10 +00:00
|
|
|
use futures::executor::block_on;
|
2020-05-13 19:47:19 +00:00
|
|
|
use image::buffer::ConvertBuffer;
|
|
|
|
use image::{Bgra, ImageBuffer, RgbaImage};
|
2020-05-04 20:59:06 +00:00
|
|
|
use std::fmt::Debug;
|
|
|
|
|
|
|
|
pub trait RenderTargetFrame: Debug {
|
|
|
|
fn view(&self) -> &wgpu::TextureView;
|
|
|
|
}
|
|
|
|
|
2020-05-11 07:02:49 +00:00
|
|
|
pub trait RenderTarget: Debug + 'static {
|
2020-05-04 20:59:06 +00:00
|
|
|
type Frame: RenderTargetFrame;
|
|
|
|
|
|
|
|
fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32);
|
|
|
|
|
|
|
|
fn format(&self) -> wgpu::TextureFormat;
|
|
|
|
|
|
|
|
fn width(&self) -> u32;
|
|
|
|
|
|
|
|
fn height(&self) -> u32;
|
|
|
|
|
2021-09-08 07:20:11 +00:00
|
|
|
fn get_next_texture(&mut self) -> Result<Self::Frame, wgpu::SurfaceError>;
|
2020-05-05 13:27:10 +00:00
|
|
|
|
2020-08-27 10:32:41 +00:00
|
|
|
fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(
|
2020-05-05 13:27:10 +00:00
|
|
|
&self,
|
|
|
|
device: &wgpu::Device,
|
|
|
|
queue: &wgpu::Queue,
|
2020-08-27 10:32:41 +00:00
|
|
|
command_buffers: I,
|
2021-09-08 07:20:11 +00:00
|
|
|
frame: Self::Frame,
|
2020-05-05 13:27:10 +00:00
|
|
|
);
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct SwapChainTarget {
|
|
|
|
window_surface: wgpu::Surface,
|
2021-09-08 07:20:11 +00:00
|
|
|
surface_config: wgpu::SurfaceConfiguration,
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2021-09-08 07:20:11 +00:00
|
|
|
pub struct SwapChainTargetFrame {
|
|
|
|
texture: wgpu::SurfaceTexture,
|
|
|
|
view: wgpu::TextureView,
|
|
|
|
}
|
2020-05-04 20:59:06 +00:00
|
|
|
|
|
|
|
impl RenderTargetFrame for SwapChainTargetFrame {
|
|
|
|
fn view(&self) -> &wgpu::TextureView {
|
2021-09-08 07:20:11 +00:00
|
|
|
&self.view
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SwapChainTarget {
|
2020-05-14 19:44:31 +00:00
|
|
|
pub fn new(surface: wgpu::Surface, size: (u32, u32), device: &wgpu::Device) -> Self {
|
2021-09-08 07:20:11 +00:00
|
|
|
let surface_config = wgpu::SurfaceConfiguration {
|
|
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
2020-05-04 20:59:06 +00:00
|
|
|
format: wgpu::TextureFormat::Bgra8Unorm,
|
|
|
|
width: size.0,
|
|
|
|
height: size.1,
|
|
|
|
present_mode: wgpu::PresentMode::Mailbox,
|
|
|
|
};
|
2021-09-08 07:20:11 +00:00
|
|
|
surface.configure(device, &surface_config);
|
2020-05-04 20:59:06 +00:00
|
|
|
Self {
|
2021-09-08 07:20:11 +00:00
|
|
|
surface_config,
|
2020-05-14 19:44:31 +00:00
|
|
|
window_surface: surface,
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderTarget for SwapChainTarget {
|
|
|
|
type Frame = SwapChainTargetFrame;
|
|
|
|
|
|
|
|
fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
|
2021-09-08 07:20:11 +00:00
|
|
|
self.surface_config.width = width;
|
|
|
|
self.surface_config.height = height;
|
|
|
|
self.window_surface.configure(device, &self.surface_config);
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn format(&self) -> wgpu::TextureFormat {
|
2021-09-08 07:20:11 +00:00
|
|
|
self.surface_config.format
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn width(&self) -> u32 {
|
2021-09-08 07:20:11 +00:00
|
|
|
self.surface_config.width
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn height(&self) -> u32 {
|
2021-09-08 07:20:11 +00:00
|
|
|
self.surface_config.height
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
|
|
|
|
2021-09-08 07:20:11 +00:00
|
|
|
fn get_next_texture(&mut self) -> Result<Self::Frame, wgpu::SurfaceError> {
|
|
|
|
let texture = self.window_surface.get_current_texture()?;
|
|
|
|
let view = texture.texture.create_view(&Default::default());
|
|
|
|
Ok(SwapChainTargetFrame { texture, view })
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
2020-05-05 13:27:10 +00:00
|
|
|
|
2020-08-27 10:32:41 +00:00
|
|
|
fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(
|
2020-05-05 13:27:10 +00:00
|
|
|
&self,
|
|
|
|
_device: &wgpu::Device,
|
|
|
|
queue: &wgpu::Queue,
|
2020-08-27 10:32:41 +00:00
|
|
|
command_buffers: I,
|
2021-09-08 07:20:11 +00:00
|
|
|
frame: Self::Frame,
|
2020-05-05 13:27:10 +00:00
|
|
|
) {
|
|
|
|
queue.submit(command_buffers);
|
2021-09-08 07:20:11 +00:00
|
|
|
frame.texture.present();
|
2020-05-05 13:27:10 +00:00
|
|
|
}
|
2020-05-04 20:59:06 +00:00
|
|
|
}
|
2020-05-04 21:33:45 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct TextureTarget {
|
|
|
|
size: wgpu::Extent3d,
|
|
|
|
texture: wgpu::Texture,
|
|
|
|
format: wgpu::TextureFormat,
|
2020-05-05 13:27:10 +00:00
|
|
|
buffer: wgpu::Buffer,
|
2020-08-27 10:32:41 +00:00
|
|
|
buffer_dimensions: BufferDimensions,
|
2020-05-04 21:33:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct TextureTargetFrame(wgpu::TextureView);
|
|
|
|
|
2020-05-13 19:47:19 +00:00
|
|
|
type BgraImage = ImageBuffer<Bgra<u8>, Vec<u8>>;
|
|
|
|
|
2020-05-04 21:33:45 +00:00
|
|
|
impl RenderTargetFrame for TextureTargetFrame {
|
|
|
|
fn view(&self) -> &wgpu::TextureView {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TextureTarget {
|
|
|
|
pub fn new(device: &wgpu::Device, size: (u32, u32)) -> Self {
|
2020-08-27 10:32:41 +00:00
|
|
|
let buffer_dimensions = BufferDimensions::new(size.0 as usize, size.1 as usize);
|
2020-05-04 21:33:45 +00:00
|
|
|
let size = wgpu::Extent3d {
|
|
|
|
width: size.0,
|
|
|
|
height: size.1,
|
2021-04-16 20:55:31 +00:00
|
|
|
depth_or_array_layers: 1,
|
2020-05-04 21:33:45 +00:00
|
|
|
};
|
2020-05-05 13:27:10 +00:00
|
|
|
let texture_label = create_debug_label!("Render target texture");
|
2020-05-04 21:33:45 +00:00
|
|
|
let format = wgpu::TextureFormat::Bgra8Unorm;
|
|
|
|
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
2020-05-05 13:27:10 +00:00
|
|
|
label: texture_label.as_deref(),
|
2020-05-04 21:33:45 +00:00
|
|
|
size,
|
|
|
|
mip_level_count: 1,
|
|
|
|
sample_count: 1,
|
|
|
|
dimension: wgpu::TextureDimension::D2,
|
|
|
|
format,
|
2021-09-08 07:20:11 +00:00
|
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
2020-05-04 21:33:45 +00:00
|
|
|
});
|
2020-05-05 13:27:10 +00:00
|
|
|
let buffer_label = create_debug_label!("Render target buffer");
|
|
|
|
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
|
|
|
label: buffer_label.as_deref(),
|
2021-04-16 20:55:31 +00:00
|
|
|
size: (buffer_dimensions.padded_bytes_per_row.get() as u64
|
|
|
|
* buffer_dimensions.height as u64),
|
2021-09-08 07:20:11 +00:00
|
|
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
2020-08-27 10:32:41 +00:00
|
|
|
mapped_at_creation: false,
|
2020-05-05 13:27:10 +00:00
|
|
|
});
|
2020-05-04 21:33:45 +00:00
|
|
|
Self {
|
|
|
|
size,
|
|
|
|
texture,
|
|
|
|
format,
|
2020-05-05 13:27:10 +00:00
|
|
|
buffer,
|
2020-08-27 10:32:41 +00:00
|
|
|
buffer_dimensions,
|
2020-05-05 13:27:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn capture(&self, device: &wgpu::Device) -> Option<RgbaImage> {
|
2020-08-27 10:32:41 +00:00
|
|
|
let buffer_future = self.buffer.slice(..).map_async(wgpu::MapMode::Read);
|
2020-05-05 13:27:10 +00:00
|
|
|
device.poll(wgpu::Maintain::Wait);
|
|
|
|
match block_on(buffer_future) {
|
2020-08-27 10:32:41 +00:00
|
|
|
Ok(()) => {
|
|
|
|
let map = self.buffer.slice(..).get_mapped_range();
|
|
|
|
let mut buffer = Vec::with_capacity(
|
|
|
|
self.buffer_dimensions.height * self.buffer_dimensions.unpadded_bytes_per_row,
|
2020-05-13 19:47:19 +00:00
|
|
|
);
|
2020-08-27 10:32:41 +00:00
|
|
|
|
2021-04-16 20:55:31 +00:00
|
|
|
for chunk in map.chunks(self.buffer_dimensions.padded_bytes_per_row.get() as usize)
|
|
|
|
{
|
2020-08-27 10:32:41 +00:00
|
|
|
buffer
|
|
|
|
.extend_from_slice(&chunk[..self.buffer_dimensions.unpadded_bytes_per_row]);
|
|
|
|
}
|
|
|
|
|
|
|
|
let bgra = BgraImage::from_raw(self.size.width, self.size.height, buffer);
|
2020-12-27 03:57:06 +00:00
|
|
|
let ret = bgra.map(|image| image.convert());
|
|
|
|
drop(map);
|
|
|
|
self.buffer.unmap();
|
|
|
|
ret
|
2020-05-05 13:27:10 +00:00
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
log::error!("Unknown error reading capture buffer: {:?}", e);
|
|
|
|
None
|
|
|
|
}
|
2020-05-04 21:33:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderTarget for TextureTarget {
|
|
|
|
type Frame = TextureTargetFrame;
|
|
|
|
|
|
|
|
fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
|
|
|
|
self.size.width = width;
|
|
|
|
self.size.height = height;
|
|
|
|
|
|
|
|
let label = create_debug_label!("Render target texture");
|
|
|
|
self.texture = device.create_texture(&wgpu::TextureDescriptor {
|
|
|
|
label: label.as_deref(),
|
|
|
|
size: self.size,
|
|
|
|
mip_level_count: 1,
|
|
|
|
sample_count: 1,
|
|
|
|
dimension: wgpu::TextureDimension::D2,
|
|
|
|
format: self.format,
|
2021-09-08 07:20:11 +00:00
|
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
|
2020-05-04 21:33:45 +00:00
|
|
|
});
|
2020-05-05 13:27:10 +00:00
|
|
|
|
|
|
|
let buffer_label = create_debug_label!("Render target buffer");
|
|
|
|
self.buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
|
|
|
label: buffer_label.as_deref(),
|
|
|
|
size: width as u64 * height as u64 * 4,
|
2021-09-08 07:20:11 +00:00
|
|
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
|
2020-08-27 10:32:41 +00:00
|
|
|
mapped_at_creation: false,
|
2020-05-05 13:27:10 +00:00
|
|
|
});
|
2020-05-04 21:33:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn format(&self) -> wgpu::TextureFormat {
|
|
|
|
self.format
|
|
|
|
}
|
|
|
|
|
|
|
|
fn width(&self) -> u32 {
|
|
|
|
self.size.width
|
|
|
|
}
|
|
|
|
|
|
|
|
fn height(&self) -> u32 {
|
|
|
|
self.size.height
|
|
|
|
}
|
|
|
|
|
2021-09-08 07:20:11 +00:00
|
|
|
fn get_next_texture(&mut self) -> Result<Self::Frame, wgpu::SurfaceError> {
|
2020-08-27 10:32:41 +00:00
|
|
|
Ok(TextureTargetFrame(
|
|
|
|
self.texture.create_view(&Default::default()),
|
|
|
|
))
|
2020-05-04 21:33:45 +00:00
|
|
|
}
|
2020-05-05 13:27:10 +00:00
|
|
|
|
2020-08-27 10:32:41 +00:00
|
|
|
fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(
|
2020-05-05 13:27:10 +00:00
|
|
|
&self,
|
|
|
|
device: &wgpu::Device,
|
|
|
|
queue: &wgpu::Queue,
|
2020-08-27 10:32:41 +00:00
|
|
|
command_buffers: I,
|
2021-09-08 07:20:11 +00:00
|
|
|
_frame: Self::Frame,
|
2020-05-05 13:27:10 +00:00
|
|
|
) {
|
|
|
|
let label = create_debug_label!("Render target transfer encoder");
|
|
|
|
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
|
|
label: label.as_deref(),
|
|
|
|
});
|
|
|
|
encoder.copy_texture_to_buffer(
|
2021-04-16 20:55:31 +00:00
|
|
|
wgpu::ImageCopyTexture {
|
2020-05-05 13:27:10 +00:00
|
|
|
texture: &self.texture,
|
|
|
|
mip_level: 0,
|
|
|
|
origin: wgpu::Origin3d::ZERO,
|
2021-09-08 07:20:11 +00:00
|
|
|
aspect: wgpu::TextureAspect::All,
|
2020-05-05 13:27:10 +00:00
|
|
|
},
|
2021-04-16 20:55:31 +00:00
|
|
|
wgpu::ImageCopyBuffer {
|
2020-05-05 13:27:10 +00:00
|
|
|
buffer: &self.buffer,
|
2021-04-16 20:55:31 +00:00
|
|
|
layout: wgpu::ImageDataLayout {
|
2020-08-27 10:32:41 +00:00
|
|
|
offset: 0,
|
2021-04-16 20:55:31 +00:00
|
|
|
bytes_per_row: Some(self.buffer_dimensions.padded_bytes_per_row),
|
|
|
|
rows_per_image: None,
|
2020-08-27 10:32:41 +00:00
|
|
|
},
|
2020-05-05 13:27:10 +00:00
|
|
|
},
|
|
|
|
self.size,
|
|
|
|
);
|
2020-08-27 10:32:41 +00:00
|
|
|
queue.submit(command_buffers.into_iter().chain(Some(encoder.finish())));
|
2020-05-05 13:27:10 +00:00
|
|
|
}
|
2020-05-04 21:33:45 +00:00
|
|
|
}
|