1use std::future::Future;
9use std::time::Instant;
10use winit::event::{self, WindowEvent};
11use winit::event_loop::{ControlFlow, EventLoop};
12use wgpu;
13
14pub fn cast_slice<T>(data: &[T]) -> &[u8] {
16 use std::{mem::size_of, slice::from_raw_parts};
17unsafe {
18 from_raw_parts(data.as_ptr() as *const u8, data.len() * size_of::<T>())
19}
20}
21
22pub enum ShaderStage {
24 Vertex,
25 Fragment,
26 Compute
27}
28
29pub trait App: 'static + Sized {
30 fn optional_features() -> wgpu::Features {
31 wgpu::Features::empty()
32 }
33 fn required_features() -> wgpu::Features {
34wgpu::Features::DEPTH_CLIP_CONTROL
36 }
37 fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities {
38 wgpu::DownlevelCapabilities{
39 flags: wgpu::DownlevelFlags::empty(),
40 shader_model: wgpu::ShaderModel::Sm5,
41 ..wgpu::DownlevelCapabilities::default()
42 }
43 }
44 fn required_limits() -> wgpu::Limits {
46 wgpu::Limits::downlevel_webgl2_defaults()
47 }
48
49 fn init(
50 config: &wgpu::SurfaceConfiguration,
51 adapter: &wgpu::Adapter,
52 device: &wgpu::Device,
53 queue: &wgpu::Queue
54 ) -> Self;
55 fn resize(
56 &mut self,
57 config: &wgpu::SurfaceConfiguration,
58 device: &wgpu::Device,
59 queue: &wgpu::Queue
60 );
61 fn update(&mut self,
62 event: WindowEvent,
63 config: &wgpu::SurfaceConfiguration,
64 device: &wgpu::Device,
65 queue: &wgpu::Queue
66 );
67 fn render(
68 &mut self,
69 view: &wgpu::TextureView,
70 device: &wgpu::Device,
71 queue: &wgpu::Queue,
72 spawner: &Spawner
73 );
74}
75
76struct Setup {
77 window: winit::window::Window,
78 event_loop: EventLoop<()>,
79 instance: wgpu::Instance,
80 size: winit::dpi::PhysicalSize<u32>,
81 surface: wgpu::Surface,
82 adapter: wgpu::Adapter,
83 device: wgpu::Device,
84 queue: wgpu::Queue
85}
86
87async fn setup<E: App>(title: &str) -> Setup {
88 env_logger::init(); let event_loop = EventLoop::new();
91 let mut builder = winit::window::WindowBuilder::new();
92 builder = builder.with_title(title);
93 #[cfg(windows_OFF)] {
95 use winit::platform::windows::WindowBuilderExtWindows;
96 builder = builder.with_no_redirection_bitmap(true);
97 }
98 let window = builder.build(&event_loop).unwrap();
99
100 log::info!("Initializing the surface...");
101
102 let backends = wgpu::util::backend_bits_from_env()
103 .unwrap_or_else(wgpu::Backends::all);
104 let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env()
105 .unwrap_or_default();
106
107 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor{
108 backends,
109 dx12_shader_compiler
110 });
111 let (size, surface) = unsafe {
112 let size = window.inner_size();
113 let surface = instance.create_surface(&window).unwrap();
114 (size, surface)
115 };
116 let adapter = wgpu::util::initialize_adapter_from_env_or_default(
117 &instance, Some(&surface))
118 .await
119 .expect("No suitable GPU adapters found on the system!");
120
121 let adapter_info = adapter.get_info();
122 println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
123
124 let optional_features = E::optional_features();
125 let required_features = E::required_features();
126 let adapter_features = adapter.features();
127 assert!(
128 adapter_features.contains(required_features),
129 "Adapter does not support required features for this App: {:?}",
130 required_features - adapter_features
131 );
132
133 let required_downlevel_capabilities = E::required_downlevel_capabilities();
134 let downlevel_capabilities = adapter.get_downlevel_capabilities();
135 assert!(
136 downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,
137 "Adapter does not support the minimum shader model required to run this App: {:?}",
138 required_downlevel_capabilities.shader_model
139 );
140 assert!(
141 downlevel_capabilities
142 .flags
143 .contains(required_downlevel_capabilities.flags),
144 "Adapter does not support the downlevel capabilities required to run this App: {:?}",
145 required_downlevel_capabilities.flags - downlevel_capabilities.flags
146 );
147
148 let needed_limits = E::required_limits().using_resolution(adapter.limits());
151
152 let trace_dir = std::env::var("WGPU_TRACE");
153 let (device, queue) = adapter
154 .request_device(
155 &wgpu::DeviceDescriptor{
156 label: None,
157 features: (optional_features & adapter_features) | required_features,
158 limits: needed_limits
159 },
160 trace_dir.ok().as_ref().map(std::path::Path::new)
161 )
162 .await
163 .expect("Unable to find a suitable GPU adapter!");
164
165 Setup{
166 window,
167 event_loop,
168 instance,
169 size,
170 surface,
171 adapter,
172 device,
173 queue
174 }
175}
176
177fn start<E: App>(
178 Setup{
179 window,
180 event_loop,
181 instance,
182 size,
183 surface,
184 adapter,
185 device,
186 queue
187 }: Setup
188) {
189 let spawner = Spawner::new();
190 let mut config = surface
191 .get_default_config(&adapter, size.width, size.height)
192 .expect("Surface isn't supported by the adapter.");
193 let surface_view_format = config.format.add_srgb_suffix();
194 config.view_formats.push(surface_view_format);
195 surface.configure(&device, &config);
196
197 log::info!("Initializing the App...");
198 let mut app = E::init(&config, &adapter, &device, &queue);
199
200 let mut last_frame_inst = Instant::now();
201 let (mut frame_count, mut accum_time) = (0, 0.0);
202
203 log::info!("Entering render loop...");
204 event_loop.run(move |event, _, control_flow| {
205 let _ = (&instance, &adapter); *control_flow = if cfg!(feature = "metal-auto-capture") {
207 ControlFlow::Exit
208 } else {
209 ControlFlow::Poll
210 };
211 match event {
212 event::Event::RedrawEventsCleared => {
213 spawner.run_until_stalled();
214 window.request_redraw();
215 },
216 event::Event::WindowEvent {
217 event: WindowEvent::Resized(size) | WindowEvent::ScaleFactorChanged{
218 new_inner_size: &mut size,
219 ..
220 },
221 ..
222 } => {
223 let max_dimension = adapter.limits().max_texture_dimension_2d;
226 if size.width > max_dimension || size.height > max_dimension {
227 log::warn!(
228 "The resizing size {:?} exceeds the limit of {}.",
229 size,
230 max_dimension
231 );
232 } else {
233 log::info!("Resizing to {:?}", size);
234 config.width = size.width.max(1);
235 config.height = size.height.max(1);
236 app.resize(&config, &device, &queue);
237 surface.configure(&device, &config);
238 }
239 },
240 event::Event::WindowEvent{ event, .. } => {
241 match event {
242 WindowEvent::CloseRequested | WindowEvent::KeyboardInput{
243 input: event::KeyboardInput{
244 virtual_keycode: Some(event::VirtualKeyCode::Escape),
245 state: event::ElementState::Pressed,
246 ..
247 },
248 ..
249 } => *control_flow = ControlFlow::Exit,
250 WindowEvent::KeyboardInput{
251 input: event::KeyboardInput{
252 virtual_keycode: Some(event::VirtualKeyCode::R),
253 state: event::ElementState::Pressed,
254 ..
255 },
256 ..
257 } => println!("{:#?}", instance.generate_report()),
258 _ => app.update(event, &config, &device, &queue)
259 }
260 },
261 event::Event::RedrawRequested(_) => {
262 accum_time += last_frame_inst.elapsed().as_secs_f32();
263 last_frame_inst = Instant::now();
264 frame_count += 1;
265 if frame_count == 100 * 60 { println!(
267 "Avg frame time {}ms",
268 accum_time * 1000.0 / frame_count as f32
269 );
270 accum_time = 0.0;
271 frame_count = 0;
272 }
273
274 let frame = match surface.get_current_texture() {
275 Ok(frame) => frame,
276 Err(_) => {
277 surface.configure(&device, &config);
278 surface.get_current_texture()
279 .expect("Failed to acquire next surface texture!")
280 }
281 };
282 let view = frame.texture.create_view(&wgpu::TextureViewDescriptor{
283 format: Some(surface_view_format),
284 ..wgpu::TextureViewDescriptor::default()
285 });
286
287 app.render(&view, &device, &queue, &spawner);
288
289 frame.present();
290 },
291 _ => {}
292 }
293 });
294}
295
296pub struct Spawner<'a> {
297 executor: async_executor::LocalExecutor<'a>
298}
299
300impl<'a> Spawner<'a> {
301 fn new() -> Self {
302 Self{ executor: async_executor::LocalExecutor::new() }
303 }
304
305 pub fn spawn_local(&self, future: impl Future<Output = ()> + 'a) {
307 self.executor.spawn(future).detach();
308 }
309
310 fn run_until_stalled(&self) {
311 while self.executor.try_tick() {}
312 }
313}
314
315pub fn run<E: App>(title: &str) {
316 let setup = pollster::block_on(setup::<E>(title));
317 start::<E>(setup);
318}