use std::time::Instant;
use wgpu::{Adapter, CommandEncoder, CommandEncoderDescriptor, CompositeAlphaMode, Device, DeviceDescriptor, Instance, Limits, PowerPreference, PresentMode, Queue, RenderPipeline, RequestAdapterOptions, Surface, SurfaceConfiguration, SurfaceError, TextureFormat, TextureUsages, TextureView, TextureViewDescriptor};
use winit::dpi::PhysicalSize;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::{Window, WindowBuilder};
pub struct App<T: 'static> {
state: T,
app_data: AppData,
window_event_fn: Option<WindowEventFn<T>>,
resize_fn: Option<ResizeFn<T>>,
update_fn: Option<UpdateFn<T>>,
render_fn: Option<RenderFn<T>>,
init_fn: Option<InitFn<T>>,
}
pub struct AppData {
last_frame_instant: Instant,
render_instant: Instant,
pub fps: f64,
pub delta_time: f64,
pub render_time: f64,
pub update_time: f64,
surface: Surface,
pub device: Device,
pub queue: Queue,
pub config: SurfaceConfiguration,
pub size: PhysicalSize<u32>,
pub render_pipelines: Vec<RenderPipeline>,
}
impl<T: 'static> App<T> {
fn resize(&mut self, new_size: PhysicalSize<u32>) {
if new_size.width == 0 || new_size.height == 0 {
return;
}
self.app_data.size = new_size;
self.app_data.config.width = new_size.width;
self.app_data.config.height = new_size.height;
self.app_data
.surface
.configure(&self.app_data.device, &self.app_data.config);
if self.resize_fn.is_some() {
self.resize_fn.unwrap()(&self.app_data, &mut self.state, &self.app_data.size)
}
}
fn init(&mut self) {
if self.init_fn.is_none() {
return;
}
let mut render_pipelines = Vec::new();
self.init_fn.unwrap()(&self.app_data, &mut self.state, &mut render_pipelines);
self.app_data.render_pipelines = render_pipelines
}
fn render(&mut self) -> Result<(), SurfaceError> {
if self.render_fn.is_none() {
return Ok(());
}
let output = self.app_data.surface.get_current_texture()?;
let view = output
.texture
.create_view(&TextureViewDescriptor::default());
let encoder = self
.app_data
.device
.create_command_encoder(&CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
self.render_fn.unwrap()(&self.app_data, &mut self.state, encoder, view);
output.present();
Ok(())
}
fn run(mut self, window: Window, event_loop: EventLoop<()>) {
self.init();
window.set_visible(true);
event_loop.run(move |event, _, control_flow| match event {
Event::WindowEvent {
ref event,
window_id,
} => {
if window_id != window.id() {
return;
}
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
WindowEvent::Resized(physical_size) => {
self.resize(*physical_size);
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
self.resize(**new_inner_size);
}
_ => {
if self.window_event_fn.is_some() {
self.window_event_fn.unwrap()(&self.app_data, &mut self.state, event);
}
}
}
}
Event::RedrawRequested(window_id) => {
if window_id != window.id() {
return;
}
self.app_data.render_time = self.app_data.render_instant.elapsed().as_secs_f64();
self.app_data.delta_time = self.app_data.last_frame_instant.elapsed().as_secs_f64();
self.app_data.fps = 1.0 / self.app_data.delta_time;
self.app_data.last_frame_instant = Instant::now();
let update_instant = Instant::now();
if self.update_fn.is_some() {
self.update_fn.unwrap()(&self.app_data, &mut self.state);
}
self.app_data.update_time = update_instant.elapsed().as_secs_f64();
self.app_data.render_instant = Instant::now();
match self.render() {
Ok(_) => {}
Err(SurfaceError::Lost) => self.resize(self.app_data.size),
Err(SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
Err(e) => eprintln!("{:?}", e),
}
}
Event::MainEventsCleared => {
window.request_redraw();
}
_ => {}
});
}
}
pub struct AppCreator<T: 'static> {
state: T,
window: Window,
event_loop: EventLoop<()>,
window_event_fn: Option<WindowEventFn<T>>,
resize_fn: Option<ResizeFn<T>>,
update_fn: Option<UpdateFn<T>>,
render_fn: Option<RenderFn<T>>,
init_fn: Option<InitFn<T>>,
present_mode: PresentMode,
power_preference: PowerPreference,
device_limits: Limits,
view_formats: Vec<TextureFormat>,
}
impl<T: 'static> AppCreator<T> {
pub fn new(state: T) -> AppCreator<T> {
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_visible(false)
.build(&event_loop)
.unwrap();
AppCreator {
state,
window,
event_loop,
window_event_fn: None,
resize_fn: None,
update_fn: None,
render_fn: None,
init_fn: None,
present_mode: PresentMode::Fifo,
power_preference: PowerPreference::LowPower,
device_limits: Limits::default(),
view_formats: vec![],
}
}
pub fn window_event(mut self, input: WindowEventFn<T>) -> Self {
self.window_event_fn = Some(input);
self
}
pub fn resize(mut self, resize: ResizeFn<T>) -> Self {
self.resize_fn = Some(resize);
self
}
pub fn update(mut self, update: UpdateFn<T>) -> Self {
self.update_fn = Some(update);
self
}
pub fn render(mut self, render: RenderFn<T>) -> Self {
self.render_fn = Some(render);
self
}
pub fn init(mut self, init: InitFn<T>) -> Self {
self.init_fn = Some(init);
self
}
pub fn present_mode(mut self, present_mode: PresentMode) -> Self {
self.present_mode = present_mode;
self
}
pub fn title(self, title: &str) -> Self {
self.window.set_title(title);
self
}
pub fn resizable(self, resizable: bool) -> Self {
self.window.set_resizable(resizable);
self
}
pub fn get_window(&mut self) -> &mut Window {
&mut self.window
}
pub fn add_view_formats(mut self, texture_format: TextureFormat) -> Self {
self.view_formats.push(texture_format);
self
}
pub fn power_preference(mut self, power_preference: PowerPreference) -> Self {
self.power_preference = power_preference;
self
}
pub fn device_limits(mut self, limits: Limits) -> Self {
self.device_limits = limits;
self
}
fn create_app_data(&self) -> AppData {
env_logger::init();
let size = self.window.inner_size();
let instance = Instance::default();
let surface = unsafe { instance.create_surface(&self.window).unwrap() };
let adapter: Adapter =
pollster::block_on(instance.request_adapter(&RequestAdapterOptions {
power_preference: self.power_preference,
compatible_surface: Some(&surface),
force_fallback_adapter: false,
}))
.unwrap();
let (device, queue) = pollster::block_on(adapter.request_device(
&DeviceDescriptor {
features: wgpu::Features::empty(),
limits: self.device_limits.clone(),
label: None,
},
None,
))
.unwrap();
let config = SurfaceConfiguration {
usage: TextureUsages::RENDER_ATTACHMENT,
format: surface.get_capabilities(&adapter).formats[0],
width: size.width,
height: size.height,
present_mode: self.present_mode,
alpha_mode: CompositeAlphaMode::Auto,
view_formats: self.view_formats.clone(),
};
surface.configure(&device, &config);
AppData {
surface,
device,
queue,
config,
size,
last_frame_instant: Instant::now(),
render_instant: Instant::now(),
render_pipelines: Vec::new(),
fps: 0.0,
delta_time: 1.0,
render_time: 1.0,
update_time: 1.0,
}
}
pub fn run(self) {
let app = App {
app_data: self.create_app_data(),
state: self.state,
window_event_fn: self.window_event_fn,
resize_fn: self.resize_fn,
update_fn: self.update_fn,
render_fn: self.render_fn,
init_fn: self.init_fn,
};
app.run(self.window, self.event_loop);
}
}
pub type WindowEventFn<T> = fn(app_data: &AppData, state: &mut T, window_event: &WindowEvent);
pub type ResizeFn<T> = fn(app_data: &AppData, state: &mut T, size: &PhysicalSize<u32>);
pub type UpdateFn<T> = fn(app_data: &AppData, state: &mut T);
pub type RenderFn<T> = fn(
app_data: &AppData,
state: &mut T,
command_encoder: CommandEncoder,
texture_view: TextureView,
);
pub type InitFn<T> =
fn(app_data: &AppData, state: &mut T, render_pipelines: &mut Vec<RenderPipeline>);