1use std::time::Instant;
2
3use wgpu::{Adapter, CommandEncoder, CommandEncoderDescriptor, CompositeAlphaMode, Device, DeviceDescriptor, Instance, Limits, PowerPreference, PresentMode, Queue, RenderPipeline, RequestAdapterOptions, Surface, SurfaceConfiguration, SurfaceError, TextureFormat, TextureUsages, TextureView, TextureViewDescriptor};
4use winit::dpi::PhysicalSize;
5use winit::event::{Event, WindowEvent};
6use winit::event_loop::{ControlFlow, EventLoop};
7use winit::window::{Window, WindowBuilder};
8
9pub struct App<T: 'static> {
10 state: T,
11 app_data: AppData,
12
13 window_event_fn: Option<WindowEventFn<T>>,
14 resize_fn: Option<ResizeFn<T>>,
15 update_fn: Option<UpdateFn<T>>,
16 render_fn: Option<RenderFn<T>>,
17 init_fn: Option<InitFn<T>>,
18}
19
20pub struct AppData {
22 last_frame_instant: Instant,
23 render_instant: Instant,
24
25 pub fps: f64,
27 pub delta_time: f64,
29 pub render_time: f64,
31 pub update_time: f64,
33
34 surface: Surface,
35
36 pub device: Device,
37 pub queue: Queue,
38 pub config: SurfaceConfiguration,
39
40 pub size: PhysicalSize<u32>,
41
42 pub render_pipelines: Vec<RenderPipeline>,
44}
45
46impl<T: 'static> App<T> {
47 fn resize(&mut self, new_size: PhysicalSize<u32>) {
48 if new_size.width == 0 || new_size.height == 0 {
49 return;
50 }
51
52 self.app_data.size = new_size;
53 self.app_data.config.width = new_size.width;
54 self.app_data.config.height = new_size.height;
55 self.app_data
56 .surface
57 .configure(&self.app_data.device, &self.app_data.config);
58
59 if self.resize_fn.is_some() {
60 self.resize_fn.unwrap()(&self.app_data, &mut self.state, (self.app_data.size.width, self.app_data.size.height))
61 }
62 }
63
64 fn init(&mut self) {
65 if self.init_fn.is_none() {
66 return;
67 }
68
69 let mut render_pipelines = Vec::new();
70 self.init_fn.unwrap()(&self.app_data, &mut self.state, &mut render_pipelines);
71 self.app_data.render_pipelines = render_pipelines
72 }
73
74 fn render(&mut self) -> Result<(), SurfaceError> {
75 if self.render_fn.is_none() {
76 return Ok(());
77 }
78
79 let output = self.app_data.surface.get_current_texture()?;
80 let view = output
81 .texture
82 .create_view(&TextureViewDescriptor::default());
83 let encoder = self
84 .app_data
85 .device
86 .create_command_encoder(&CommandEncoderDescriptor {
87 label: Some("Render Encoder"),
88 });
89
90 self.render_fn.unwrap()(&self.app_data, &mut self.state, encoder, view);
91
92 output.present();
93
94 Ok(())
95 }
96
97 fn run(mut self, window: Window, event_loop: EventLoop<()>) {
98 self.init();
99
100 window.set_visible(true);
101
102 event_loop.run(move |event, _, control_flow| match event {
103 Event::WindowEvent {
104 ref event,
105 window_id,
106 } => {
107 if window_id != window.id() {
108 return;
109 }
110 match event {
111 WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
112 WindowEvent::Resized(physical_size) => {
113 self.resize(*physical_size);
114 }
115 WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
116 self.resize(**new_inner_size);
117 }
118 _ => {
119 if self.window_event_fn.is_some() {
120 self.window_event_fn.unwrap()(&self.app_data, &mut self.state, event);
121 }
122 }
123 }
124 }
125 Event::RedrawRequested(window_id) => {
126 if window_id != window.id() {
127 return;
128 }
129
130 self.app_data.render_time = self.app_data.render_instant.elapsed().as_secs_f64();
131
132 self.app_data.delta_time = self.app_data.last_frame_instant.elapsed().as_secs_f64();
133 self.app_data.fps = 1.0 / self.app_data.delta_time;
134
135 self.app_data.last_frame_instant = Instant::now();
136
137 let update_instant = Instant::now();
138
139 if self.update_fn.is_some() {
140 self.update_fn.unwrap()(&self.app_data, &mut self.state);
141 }
142
143 self.app_data.update_time = update_instant.elapsed().as_secs_f64();
144 self.app_data.render_instant = Instant::now();
145
146 match self.render() {
147 Ok(_) => {}
148 Err(SurfaceError::Lost) => self.resize(self.app_data.size),
149 Err(SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,
150 Err(e) => eprintln!("{:?}", e),
151 }
152 }
153 Event::MainEventsCleared => {
154 window.request_redraw();
155 }
156
157 _ => {}
158 });
159 }
160}
161
162pub struct AppCreator<T: 'static> {
164 state: T,
165
166 window: Window,
167 event_loop: EventLoop<()>,
168
169 window_event_fn: Option<WindowEventFn<T>>,
170 resize_fn: Option<ResizeFn<T>>,
171 update_fn: Option<UpdateFn<T>>,
172 render_fn: Option<RenderFn<T>>,
173 init_fn: Option<InitFn<T>>,
174
175 present_mode: PresentMode,
176 power_preference: PowerPreference,
177 device_limits: Limits,
178
179 view_formats: Vec<TextureFormat>,
180}
181
182impl<T: 'static> AppCreator<T> {
183 pub fn new(state: T) -> AppCreator<T> {
190 let event_loop = EventLoop::new();
191 let window = WindowBuilder::new()
192 .with_visible(false)
193 .build(&event_loop)
194 .unwrap();
195
196 AppCreator {
197 state,
198
199 window,
200 event_loop,
201 window_event_fn: None,
202 resize_fn: None,
203 update_fn: None,
204 render_fn: None,
205 init_fn: None,
206
207 present_mode: PresentMode::Fifo,
208 power_preference: PowerPreference::LowPower,
209 device_limits: Limits::default(),
210
211 view_formats: vec![],
212 }
213 }
214
215 pub fn window_event(mut self, input: WindowEventFn<T>) -> Self {
219 self.window_event_fn = Some(input);
220 self
221 }
222
223 pub fn resize(mut self, resize: ResizeFn<T>) -> Self {
225 self.resize_fn = Some(resize);
226 self
227 }
228
229 pub fn update(mut self, update: UpdateFn<T>) -> Self {
233 self.update_fn = Some(update);
234 self
235 }
236
237 pub fn render(mut self, render: RenderFn<T>) -> Self {
241 self.render_fn = Some(render);
242 self
243 }
244
245 pub fn init(mut self, init: InitFn<T>) -> Self {
249 self.init_fn = Some(init);
250 self
251 }
252
253 pub fn present_mode(mut self, present_mode: PresentMode) -> Self {
257 self.present_mode = present_mode;
258 self
259 }
260
261 pub fn title(self, title: &str) -> Self {
262 self.window.set_title(title);
263 self
264 }
265
266 pub fn resizable(self, resizable: bool) -> Self {
267 self.window.set_resizable(resizable);
268 self
269 }
270
271 pub fn get_window(&mut self) -> &mut Window {
272 &mut self.window
273 }
274
275 pub fn add_view_formats(mut self, texture_format: TextureFormat) -> Self {
276 self.view_formats.push(texture_format);
277 self
278 }
279
280 pub fn power_preference(mut self, power_preference: PowerPreference) -> Self {
284 self.power_preference = power_preference;
285 self
286 }
287
288 pub fn device_limits(mut self, limits: Limits) -> Self {
292 self.device_limits = limits;
293 self
294 }
295
296 fn create_app_data(&self) -> AppData {
297 env_logger::init();
298 let size = self.window.inner_size();
299
300 let instance = Instance::default();
301 let surface = unsafe { instance.create_surface(&self.window).unwrap() };
302
303 let adapter: Adapter =
304 pollster::block_on(instance.request_adapter(&RequestAdapterOptions {
305 power_preference: self.power_preference,
306 compatible_surface: Some(&surface),
307 force_fallback_adapter: false,
308 }))
309 .unwrap();
310
311 let (device, queue) = pollster::block_on(adapter.request_device(
312 &DeviceDescriptor {
313 features: wgpu::Features::empty(),
314 limits: self.device_limits.clone(),
315 label: None,
316 },
317 None,
318 ))
319 .unwrap();
320
321 let config = SurfaceConfiguration {
322 usage: TextureUsages::RENDER_ATTACHMENT,
323 format: surface.get_capabilities(&adapter).formats[0],
324 width: size.width,
325 height: size.height,
326 present_mode: self.present_mode,
327 alpha_mode: CompositeAlphaMode::Auto,
328 view_formats: self.view_formats.clone(),
329 };
330
331 surface.configure(&device, &config);
332
333 AppData {
334 surface,
335 device,
336 queue,
337 config,
338 size,
339
340 last_frame_instant: Instant::now(),
341 render_instant: Instant::now(),
342
343 render_pipelines: Vec::new(),
344 fps: 0.0,
345
346 delta_time: 1.0,
347 render_time: 1.0,
348 update_time: 1.0,
349 }
350 }
351
352 pub fn run(self) {
354 let app = App {
355 app_data: self.create_app_data(),
356
357 state: self.state,
358
359 window_event_fn: self.window_event_fn,
360 resize_fn: self.resize_fn,
361 update_fn: self.update_fn,
362 render_fn: self.render_fn,
363 init_fn: self.init_fn,
364 };
365
366 app.run(self.window, self.event_loop);
367 }
368}
369
370pub type WindowEventFn<T> = fn(app_data: &AppData, state: &mut T, window_event: &WindowEvent);
371
372pub type ResizeFn<T> = fn(app_data: &AppData, state: &mut T, size: (u32, u32));
373
374pub type UpdateFn<T> = fn(app_data: &AppData, state: &mut T);
375
376pub type RenderFn<T> = fn(
377 app_data: &AppData,
378 state: &mut T,
379 command_encoder: CommandEncoder,
380 texture_view: TextureView,
381);
382
383pub type InitFn<T> =
384fn(app_data: &AppData, state: &mut T, render_pipelines: &mut Vec<RenderPipeline>);