use crate::color::IntoLinSrgba;
use crate::wgpu;
use std::ops;
use std::path::PathBuf;
use std::sync::Mutex;
use std::time::Duration;
pub mod raw;
pub use self::raw::RawFrame;
pub struct Frame<'swap_chain> {
raw_frame: RawFrame<'swap_chain>,
render_data: &'swap_chain RenderData,
capture_data: &'swap_chain CaptureData,
}
#[derive(Debug)]
pub struct RenderData {
intermediary_lin_srgba: IntermediaryLinSrgba,
msaa_samples: u32,
size: [u32; 2],
texture_reshaper: wgpu::TextureReshaper,
}
#[derive(Debug)]
pub(crate) struct CaptureData {
pub(crate) next_frame_path: Mutex<Option<PathBuf>>,
pub(crate) texture_capturer: wgpu::TextureCapturer,
}
#[derive(Debug)]
pub(crate) struct IntermediaryLinSrgba {
msaa_texture: Option<(wgpu::Texture, wgpu::TextureView)>,
texture: wgpu::Texture,
texture_view: wgpu::TextureView,
}
impl<'swap_chain> ops::Deref for Frame<'swap_chain> {
type Target = RawFrame<'swap_chain>;
fn deref(&self) -> &Self::Target {
&self.raw_frame
}
}
impl<'swap_chain> Frame<'swap_chain> {
pub const DEFAULT_MSAA_SAMPLES: u32 = 4;
pub const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float;
pub(crate) fn new_empty(
raw_frame: RawFrame<'swap_chain>,
render_data: &'swap_chain RenderData,
capture_data: &'swap_chain CaptureData,
) -> Self {
Frame {
raw_frame,
render_data,
capture_data,
}
}
fn submit_inner(&mut self) {
let Frame {
ref capture_data,
ref render_data,
ref mut raw_frame,
} = *self;
if let Some((_, ref msaa_texture_view)) = render_data.intermediary_lin_srgba.msaa_texture {
let mut encoder = raw_frame.command_encoder();
wgpu::resolve_texture(
msaa_texture_view,
&render_data.intermediary_lin_srgba.texture_view,
&mut *encoder,
);
}
let mut snapshot_capture = None;
if let Ok(mut guard) = capture_data.next_frame_path.lock() {
if let Some(path) = guard.take() {
let device = raw_frame.device_queue_pair().device();
let mut encoder = raw_frame.command_encoder();
let snapshot = capture_data.texture_capturer.capture(
device,
&mut *encoder,
&render_data.intermediary_lin_srgba.texture,
);
snapshot_capture = Some((path, snapshot));
}
}
{
let mut encoder = raw_frame.command_encoder();
render_data
.texture_reshaper
.encode_render_pass(raw_frame.swap_chain_texture(), &mut *encoder);
}
raw_frame.submit_inner();
if let Some((path, snapshot)) = snapshot_capture {
let result = snapshot.read(move |result| match result {
Err(e) => eprintln!("failed to async read captured frame: {:?}", e),
Ok(image) => {
let image = image.to_owned();
if let Err(e) = image.save(&path) {
eprintln!(
"failed to save captured frame to \"{}\": {}",
path.display(),
e
);
}
}
});
if let Err(wgpu::TextureCapturerAwaitWorkerTimeout(_)) = result {
eprintln!("timed out while waiting for a worker thread to capture the frame");
}
}
}
pub fn texture(&self) -> &wgpu::Texture {
self.render_data
.intermediary_lin_srgba
.msaa_texture
.as_ref()
.map(|(tex, _)| tex)
.unwrap_or(&self.render_data.intermediary_lin_srgba.texture)
}
pub fn texture_view(&self) -> &wgpu::TextureView {
self.render_data
.intermediary_lin_srgba
.msaa_texture
.as_ref()
.map(|(_, view)| view)
.unwrap_or(&self.render_data.intermediary_lin_srgba.texture_view)
}
pub fn resolve_target(&self) -> Option<&wgpu::TextureView> {
if self.render_data.msaa_samples <= 1 {
None
} else {
Some(&self.render_data.intermediary_lin_srgba.texture_view)
}
}
pub fn texture_format(&self) -> wgpu::TextureFormat {
Self::TEXTURE_FORMAT
}
pub fn texture_msaa_samples(&self) -> u32 {
self.render_data.msaa_samples
}
pub fn texture_size(&self) -> [u32; 2] {
self.render_data.size
}
pub fn color_attachment_descriptor(&self) -> wgpu::RenderPassColorAttachmentDescriptor {
let load_op = wgpu::LoadOp::Load;
let store_op = wgpu::StoreOp::Store;
let attachment = match self.render_data.intermediary_lin_srgba.msaa_texture {
None => &self.render_data.intermediary_lin_srgba.texture_view,
Some((_, ref msaa_texture_view)) => msaa_texture_view,
};
let resolve_target = None;
let clear_color = wgpu::Color::TRANSPARENT;
wgpu::RenderPassColorAttachmentDescriptor {
attachment,
resolve_target,
load_op,
store_op,
clear_color,
}
}
pub fn clear<C>(&self, color: C)
where
C: IntoLinSrgba<f32>,
{
let lin_srgba = color.into_lin_srgba();
let (r, g, b, a) = lin_srgba.into_components();
let (r, g, b, a) = (r as f64, g as f64, b as f64, a as f64);
let color = wgpu::Color { r, g, b, a };
wgpu::clear_texture(self.texture_view(), color, &mut *self.command_encoder())
}
pub fn submit(mut self) {
self.submit_inner();
}
}
impl CaptureData {
pub(crate) fn new(max_jobs: u32, timeout: Option<Duration>) -> Self {
CaptureData {
next_frame_path: Default::default(),
texture_capturer: wgpu::TextureCapturer::new(Some(max_jobs), timeout),
}
}
}
impl RenderData {
pub(crate) fn new(
device: &wgpu::Device,
swap_chain_dims: [u32; 2],
swap_chain_format: wgpu::TextureFormat,
msaa_samples: u32,
) -> Self {
let intermediary_lin_srgba =
create_intermediary_lin_srgba(device, swap_chain_dims, msaa_samples);
let src_sample_count = 1;
let swap_chain_sample_count = 1;
let texture_reshaper = wgpu::TextureReshaper::new(
device,
&intermediary_lin_srgba.texture_view,
src_sample_count,
intermediary_lin_srgba.texture_view.component_type(),
swap_chain_sample_count,
swap_chain_format,
);
RenderData {
intermediary_lin_srgba,
texture_reshaper,
size: swap_chain_dims,
msaa_samples,
}
}
}
impl<'swap_chain> Drop for Frame<'swap_chain> {
fn drop(&mut self) {
if !self.raw_frame.is_submitted() {
self.submit_inner();
}
}
}
fn create_lin_srgba_msaa_texture(
device: &wgpu::Device,
swap_chain_dims: [u32; 2],
msaa_samples: u32,
) -> wgpu::Texture {
wgpu::TextureBuilder::new()
.size(swap_chain_dims)
.sample_count(msaa_samples)
.usage(wgpu::TextureUsage::OUTPUT_ATTACHMENT)
.format(Frame::TEXTURE_FORMAT)
.build(device)
}
fn create_lin_srgba_texture(device: &wgpu::Device, swap_chain_dims: [u32; 2]) -> wgpu::Texture {
wgpu::TextureBuilder::new()
.size(swap_chain_dims)
.format(Frame::TEXTURE_FORMAT)
.usage(wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED)
.build(device)
}
fn create_intermediary_lin_srgba(
device: &wgpu::Device,
swap_chain_dims: [u32; 2],
msaa_samples: u32,
) -> IntermediaryLinSrgba {
let msaa_texture = match msaa_samples {
0 | 1 => None,
_ => {
let texture = create_lin_srgba_msaa_texture(device, swap_chain_dims, msaa_samples);
let texture_view = texture.view().build();
Some((texture, texture_view))
}
};
let texture = create_lin_srgba_texture(device, swap_chain_dims);
let texture_view = texture.view().build();
IntermediaryLinSrgba {
msaa_texture,
texture,
texture_view,
}
}