mod builder;
pub use builder::*;
mod frame;
pub use frame::*;
mod render_pass;
pub use render_pass::*;
use std::{cell::RefCell, ops::Deref};
use crate::{bitor, Gpu, GpuError, Texture};
pub trait BeginRenderFrame {
fn begin_frame(&self) -> Result<Frame, GpuError>;
}
pub struct Viewport {
pub gpu: Gpu,
pub surface: wgpu::Surface,
pub window: winit::window::Window,
pub sc_desc: RefCell<wgpu::SurfaceConfiguration>,
pub depth_texture: RefCell<crate::Texture<crate::D2>>,
pub data_buffer: wgpu::Buffer,
pub resize_to: RefCell<Option<(u32, u32)>>,
}
impl<'a> Viewport {
#[must_use]
pub fn new(
gpu: Gpu,
surface: wgpu::Surface,
width: u32,
height: u32,
format: wgpu::TextureFormat,
window: winit::window::Window,
) -> Self {
let sc_desc = wgpu::SurfaceConfiguration {
usage: bitor!(
wgpu::TextureUsages: COPY_SRC | COPY_DST | TEXTURE_BINDING | RENDER_ATTACHMENT
),
format,
width,
height,
present_mode: wgpu::PresentMode::Mailbox,
};
surface.configure(&gpu.device, &sc_desc);
let depth_texture = Self::create_depth_texture(&gpu, width, height);
let data_buffer = gpu
.new_buffer("Viewport buffer")
.as_uniform_buffer()
.allow_copy_to()
.create(&[width as f32, height as f32])
.inner;
let sc_desc = RefCell::new(sc_desc);
let depth_texture = RefCell::new(depth_texture);
Self {
gpu,
surface,
depth_texture,
sc_desc,
data_buffer,
resize_to: RefCell::new(None),
window,
}
}
fn configure_surface(&self) {
self.surface
.configure(&self.gpu.device, &self.sc_desc.borrow());
}
fn create_depth_texture(gpu: &Gpu, width: u32, height: u32) -> Texture<crate::D2> {
gpu.new_texture("Viewport depth texture")
.as_render_target()
.with_format(crate::TextureFormat::Depth32Float)
.create_empty((width, height))
}
pub(crate) fn set_conf_size(&self, width: u32, height: u32) -> bool {
let mut sc_desc = self.sc_desc.borrow_mut();
if sc_desc.width == width && sc_desc.height == height {
return false;
}
sc_desc.width = width;
sc_desc.height = height;
true
}
pub fn resize(&self, width: u32, height: u32) {
let mut resize_to = self.resize_to.borrow_mut();
*resize_to = Some((width, height));
}
fn resolve_resize(&self) {
if let Some((width, height)) = self.resize_to.borrow_mut().take() {
self.set_conf_size(width, height);
self.resize_impl();
}
}
pub(crate) fn resize_impl(&self) {
let sc_desc = &self.sc_desc.borrow();
let (width, height) = (sc_desc.width, sc_desc.height);
if width == 0 || height == 0 {
return;
}
self.configure_surface();
let depth_texture = Self::create_depth_texture(&self.gpu, width, height);
self.depth_texture.replace(depth_texture);
self.gpu.queue.write_buffer(
&self.data_buffer,
0,
bytemuck::cast_slice(&[width as f32, height as f32]),
);
}
#[deprecated(note = "Use begin_frame() instead.")]
pub fn get_current_frame(&self) -> Result<wgpu::SurfaceTexture, wgpu::SurfaceError> {
self.resolve_resize();
let texture = self.surface.get_current_texture()?;
Ok(texture)
}
fn resize_using_window(&self) -> bool {
let (width, height) = self.window.inner_size().into();
let changed = self.set_conf_size(width, height);
if changed {
self.resize_impl();
}
changed
}
pub fn begin_frame(&self) -> Result<Frame, GpuError> {
self.resolve_resize();
match Frame::new(
&self.gpu,
&self.surface,
self.depth_texture
.borrow()
.create_view(&wgpu::TextureViewDescriptor::default()),
) {
Ok(frame) => Ok(frame),
Err(GpuError::SurfaceError(wgpu::SurfaceError::Outdated)) => {
if self.resize_using_window() {
self.begin_frame()
} else {
Err(GpuError::SurfaceError(wgpu::SurfaceError::Outdated))
}
}
Err(e) => Err(e),
}
}
#[allow(clippy::cast_precision_loss)]
pub fn aspect_ratio(&self) -> f32 {
let sc_desc = self.sc_desc.borrow();
sc_desc.width as f32 / sc_desc.height as f32
}
pub fn width(&self) -> u32 {
let sc_desc = self.sc_desc.borrow();
sc_desc.width
}
pub fn height(&self) -> u32 {
let sc_desc = self.sc_desc.borrow();
sc_desc.height
}
pub fn area(&self) -> u32 {
self.width() * self.height()
}
}
impl Deref for Viewport {
type Target = winit::window::Window;
fn deref(&self) -> &Self::Target {
&self.window
}
}