ruffle/render/wgpu/src/target.rs

281 lines
8.5 KiB
Rust
Raw Normal View History

#[cfg(not(target_family = "wasm"))]
2020-08-27 10:32:41 +00:00
use crate::utils::BufferDimensions;
#[cfg(not(target_family = "wasm"))]
use crate::Error;
#[cfg(not(target_family = "wasm"))]
use ruffle_render::utils::unmultiply_alpha_rgba;
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 {
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-08-27 10:32:41 +00:00
fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(
&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,
);
}
#[derive(Debug)]
pub struct SwapChainTarget {
window_surface: wgpu::Surface,
2021-09-08 07:20:11 +00:00
surface_config: wgpu::SurfaceConfiguration,
}
#[derive(Debug)]
2021-09-08 07:20:11 +00:00
pub struct SwapChainTargetFrame {
texture: wgpu::SurfaceTexture,
view: wgpu::TextureView,
}
impl RenderTargetFrame for SwapChainTargetFrame {
fn view(&self) -> &wgpu::TextureView {
2021-09-08 07:20:11 +00:00
&self.view
}
}
impl SwapChainTarget {
pub fn new(
surface: wgpu::Surface,
format: wgpu::TextureFormat,
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,
format,
width: size.0,
height: size.1,
present_mode: wgpu::PresentMode::Fifo,
};
2021-09-08 07:20:11 +00:00
surface.configure(device, &surface_config);
Self {
2021-09-08 07:20:11 +00:00
surface_config,
window_surface: surface,
}
}
}
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);
}
fn format(&self) -> wgpu::TextureFormat {
2021-09-08 07:20:11 +00:00
self.surface_config.format
}
fn width(&self) -> u32 {
2021-09-08 07:20:11 +00:00
self.surface_config.width
}
fn height(&self) -> u32 {
2021-09-08 07:20:11 +00:00
self.surface_config.height
}
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-08-27 10:32:41 +00:00
fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(
&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,
) {
queue.submit(command_buffers);
2021-09-08 07:20:11 +00:00
frame.texture.present();
}
}
#[cfg(not(target_family = "wasm"))]
#[derive(Debug)]
pub struct TextureTarget {
size: wgpu::Extent3d,
texture: wgpu::Texture,
format: wgpu::TextureFormat,
buffer: wgpu::Buffer,
2020-08-27 10:32:41 +00:00
buffer_dimensions: BufferDimensions,
}
#[cfg(not(target_family = "wasm"))]
#[derive(Debug)]
pub struct TextureTargetFrame(wgpu::TextureView);
#[cfg(not(target_family = "wasm"))]
impl RenderTargetFrame for TextureTargetFrame {
fn view(&self) -> &wgpu::TextureView {
&self.0
}
}
#[cfg(not(target_family = "wasm"))]
impl TextureTarget {
pub fn new(device: &wgpu::Device, size: (u32, u32)) -> Result<Self, Error> {
if size.0 > device.limits().max_texture_dimension_2d
|| size.1 > device.limits().max_texture_dimension_2d
{
return Err(format!(
"Texture target cannot be larger than {}px on either dimension (requested {} x {})",
device.limits().max_texture_dimension_2d,
size.0,
size.1
)
.into());
}
2020-08-27 10:32:41 +00:00
let buffer_dimensions = BufferDimensions::new(size.0 as usize, size.1 as usize);
let size = wgpu::Extent3d {
width: size.0,
height: size.1,
2021-04-16 20:55:31 +00:00
depth_or_array_layers: 1,
};
let texture_label = create_debug_label!("Render target texture");
let format = wgpu::TextureFormat::Rgba8Unorm;
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: texture_label.as_deref(),
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,
});
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,
});
Ok(Self {
size,
texture,
format,
buffer,
2020-08-27 10:32:41 +00:00
buffer_dimensions,
})
}
/// Captures the current contents of our texture buffer
/// as an `RgbaImage` (using straight alpha).
2022-07-02 19:50:36 +00:00
pub fn capture(&self, device: &wgpu::Device) -> Option<image::RgbaImage> {
let (sender, receiver) = std::sync::mpsc::channel();
let buffer_slice = self.buffer.slice(..);
buffer_slice.map_async(wgpu::MapMode::Read, move |result| {
sender.send(result).unwrap();
});
device.poll(wgpu::Maintain::Wait);
let result = receiver.recv().unwrap();
match result {
2020-08-27 10:32:41 +00:00
Ok(()) => {
2022-07-02 19:50:36 +00:00
let map = buffer_slice.get_mapped_range();
2020-08-27 10:32:41 +00:00
let mut buffer = Vec::with_capacity(
self.buffer_dimensions.height * self.buffer_dimensions.unpadded_bytes_per_row,
);
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]);
}
// Our texture uses premutliplied alpha - convert to straight alpha
// so that this image can be saved directly as a PNG.
unmultiply_alpha_rgba(&mut buffer);
let image = image::RgbaImage::from_raw(self.size.width, self.size.height, buffer);
drop(map);
self.buffer.unmap();
image
}
Err(e) => {
log::error!("Unknown error reading capture buffer: {:?}", e);
None
}
}
}
}
#[cfg(not(target_family = "wasm"))]
impl RenderTarget for TextureTarget {
type Frame = TextureTargetFrame;
fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
*self =
TextureTarget::new(device, (width, height)).expect("Unable to resize texture target");
}
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-08-27 10:32:41 +00:00
fn submit<I: IntoIterator<Item = wgpu::CommandBuffer>>(
&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,
) {
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 {
texture: &self.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
2021-09-08 07:20:11 +00:00
aspect: wgpu::TextureAspect::All,
},
2021-04-16 20:55:31 +00:00
wgpu::ImageCopyBuffer {
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
},
},
self.size,
);
2020-08-27 10:32:41 +00:00
queue.submit(command_buffers.into_iter().chain(Some(encoder.finish())));
}
}