1use wgpu::{Device, Queue, RenderPass};
2use crate::render::camera::Camera;
3
4pub mod geometry;
5pub mod render;
6pub mod builtin;
7
8#[cfg(feature = "debug")]
9pub trait SizedThreadSafe: Sized + Sync + Send + std::fmt::Debug {}
10#[cfg(not(feature = "debug"))]
11pub trait SizedThreadSafe: Sized + Sync + Send {}
12
13#[cfg(feature = "debug")]
14impl<T> SizedThreadSafe for T where T: Sized + Sync + Send + std::fmt::Debug {}
15#[cfg(not(feature = "debug"))]
16impl<T> SizedThreadSafe for T where T: Sized + Sync + Send {}
17
18
19pub trait Renderable<Shared> : Send + Sync {
20 fn pre_render(&mut self, device: &Device, queue: &Queue, camera: &Camera, shared: &Shared) {
21 let _ = (device, queue, camera, shared);
22 }
23 fn render(&mut self, device: &Device, pass: &mut RenderPass, camera: &Camera, shared: &Shared) {
24 let _ = (device, pass, camera, shared);
25 }
26}
27
28
29#[cfg(feature = "egui")]
30use egui_winit::winit::{
31 application::ApplicationHandler,
32 error::EventLoopError,
33 event::{DeviceEvent, DeviceId, KeyEvent, WindowEvent},
34 event_loop::{ActiveEventLoop, EventLoop},
35 keyboard::{KeyCode, PhysicalKey},
36 window::{Window, WindowId}
37};
38#[cfg(feature = "egui")]
39use egui_winit::winit::dpi::LogicalSize;
40#[cfg(feature = "egui")]
41use glam::Mat4;
42#[cfg(feature = "egui")]
43use std::collections::HashSet;
44#[cfg(feature = "egui")]
45use std::sync::Arc;
46
47#[cfg(feature = "egui")]
48#[cfg_attr(feature = "debug", derive(Debug))]
49pub struct Viewport {
50 texture: wgpu::Texture,
51 view: wgpu::TextureView,
52 depth_tex: wgpu::Texture,
53 depth_view: wgpu::TextureView,
54 size: (u32, u32),
55 egui_id: egui::TextureId,
56 clear_color: wgpu::Color,
57}
58
59#[cfg(feature = "egui")]
60impl Viewport {
61 pub fn new(
62 device: &Device,
63 egui_renderer: &mut egui_wgpu::Renderer,
64 fmt: wgpu::TextureFormat,
65 clear_color: wgpu::Color,
66 ) -> Self {
67 let texture = device.create_texture(&wgpu::TextureDescriptor {
68 label: Some("viewport"),
69 size: wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 },
70 mip_level_count: 1,
71 sample_count: 1,
72 dimension: wgpu::TextureDimension::D2,
73 format: fmt,
74 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
75 view_formats: &[],
76 });
77 let view = texture.create_view(&Default::default());
78 let (depth_tex, depth_view) = create_depth(device, 1, 1);
79
80 let egui_id = egui_renderer.register_native_texture(
81 device,
82 &view,
83 wgpu::FilterMode::Linear,
84 );
85
86 Self { texture, view, depth_tex, depth_view, size: (0, 0), egui_id, clear_color }
87 }
88
89 fn resize_if_needed(
90 &mut self,
91 device: &Device,
92 egui_renderer: &mut egui_wgpu::Renderer,
93 w: u32, h: u32,
94 fmt: wgpu::TextureFormat,
95 ) {
96 if self.size == (w, h) { return; }
97 self.texture = device.create_texture(&wgpu::TextureDescriptor {
98 label: Some("viewport"),
99 size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
100 mip_level_count: 1,
101 sample_count: 1,
102 dimension: wgpu::TextureDimension::D2,
103 format: fmt,
104 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
105 view_formats: &[],
106 });
107 self.view = self.texture.create_view(&Default::default());
108 let (dt, dv) = create_depth(device, w, h);
109 self.depth_tex = dt;
110 self.depth_view = dv;
111 self.size = (w, h);
112 egui_renderer.update_egui_texture_from_wgpu_texture(
113 device,
114 &self.view,
115 wgpu::FilterMode::Linear,
116 self.egui_id,
117 );
118 }
119}
120
121#[cfg(feature = "egui")]
122pub struct Core {
123 window: Arc<Window>,
124 surface: Option<wgpu::Surface<'static>>,
125 surface_config: wgpu::SurfaceConfiguration,
126 device: Arc<Device>,
127 queue: Arc<Queue>,
128 depth_texture: wgpu::Texture,
129 depth_view: wgpu::TextureView,
130 egui_ctx: egui::Context,
131 egui_winit: egui_winit::State,
132 egui_renderer: egui_wgpu::Renderer,
133 surface_format: wgpu::TextureFormat,
134 camera: Camera,
135 suppress_keys: HashSet<KeyCode>,
136}
137
138#[cfg(feature = "egui")]
139impl Core {
140 pub fn surface_format(&self) -> wgpu::TextureFormat { self.surface_format }
141 pub fn device(&self) -> &Arc<Device> { &self.device }
142 pub fn queue(&self) -> &Arc<Queue> { &self.queue }
143 pub fn depth_view(&self) -> &wgpu::TextureView { &self.depth_view }
144
145 pub fn suppress_key(&mut self, key: KeyCode) { self.suppress_keys.insert(key); }
146 pub fn unsuppress_key(&mut self, key: KeyCode) { self.suppress_keys.remove(&key); }
147
148 pub fn window(&self) -> &Arc<Window> { &self.window }
149 pub fn surface_size(&self) -> (u32, u32) { (self.surface_config.width, self.surface_config.height) }
150 pub fn camera(&self) -> &Camera { &self.camera }
151 pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera }
152
153 pub fn create_viewport(&mut self, clear_color: wgpu::Color) -> Viewport {
154 Viewport::new(&self.device, &mut self.egui_renderer, self.surface_format, clear_color)
155 }
156
157 pub fn render_to_rect<Shared: Send + Sync>(
158 &mut self,
159 ui: &mut egui::Ui,
160 rect: egui::Rect,
161 viewport: &mut Viewport,
162 scene: &mut dyn Renderable<Shared>,
163 shared: &Shared,
164 ) {
165 let ppp = ui.ctx().pixels_per_point();
167 let w = (rect.width() * ppp) as u32;
168 let h = (rect.height() * ppp) as u32;
169
170 if w == 0 || h == 0 { return }
171
172 viewport.resize_if_needed(&self.device, &mut self.egui_renderer, w, h, self.surface_format);
173
174 scene.pre_render(&self.device, &self.queue, self.camera(), shared);
175
176 let mut encoder = self.device.create_command_encoder(&Default::default());
177 {
178 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
179 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
180 view: &viewport.view,
181 resolve_target: None,
182 depth_slice: None,
183 ops: wgpu::Operations {
184 load: wgpu::LoadOp::Clear(viewport.clear_color),
185 store: wgpu::StoreOp::Store,
186 },
187 })],
188 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
189 view: &viewport.depth_view,
190 depth_ops: Some(wgpu::Operations {
191 load: wgpu::LoadOp::Clear(1.0),
192 store: wgpu::StoreOp::Discard,
193 }),
194 stencil_ops: None,
195 }),
196 ..Default::default()
197 });
198 scene.render(&self.device, &mut pass, self.camera(), shared);
199 }
200 self.queue.submit([encoder.finish()]);
201
202 ui.put(rect, egui::Image::new(egui::load::SizedTexture::new(
203 viewport.egui_id,
204 egui::vec2(rect.width(), rect.height()),
205 )));
206 }
207
208 pub fn handle_window_event(&mut self, event: &WindowEvent) -> bool {
209 let pass_to_egui = match event {
210 WindowEvent::KeyboardInput { event: KeyEvent {
211 physical_key: PhysicalKey::Code(code), ..
212 }, .. } => !self.suppress_keys.contains(code),
213 _ => true,
214 };
215 if pass_to_egui {
216 self.egui_winit.on_window_event(&self.window, event).consumed
217 } else {
218 false
219 }
220 }
221
222 pub fn begin_egui(&mut self) -> egui::Context {
223 let raw = self.egui_winit.take_egui_input(&self.window);
224 self.egui_ctx.begin_pass(raw);
225 self.egui_ctx.clone()
226 }
227
228 pub fn finish_egui(&mut self, egui_out: egui::FullOutput, view: &wgpu::TextureView) {
229 self.egui_winit.handle_platform_output(&self.window, egui_out.platform_output);
230 let tris = self.egui_ctx.tessellate(egui_out.shapes, egui_out.pixels_per_point);
231 let sd = egui_wgpu::ScreenDescriptor {
232 size_in_pixels: [self.surface_config.width, self.surface_config.height],
233 pixels_per_point: egui_out.pixels_per_point,
234 };
235 for (id, img) in &egui_out.textures_delta.set {
236 self.egui_renderer.update_texture(&self.device, &self.queue, *id, img);
237 }
238 let mut encoder = self.device.create_command_encoder(&Default::default());
239 let cmds = self.egui_renderer.update_buffers(&self.device, &self.queue, &mut encoder, &tris, &sd);
240 self.queue.submit(std::iter::once(encoder.finish()).chain(cmds));
241
242 let mut encoder = self.device.create_command_encoder(&Default::default());
243 {
244 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
245 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
246 view,
247 resolve_target: None,
248 depth_slice: None,
249 ops: wgpu::Operations {
250 load: wgpu::LoadOp::Load,
251 store: wgpu::StoreOp::Store
252 },
253 })],
254 depth_stencil_attachment: None,
255 ..Default::default()
256 }).forget_lifetime();
257 self.egui_renderer.render(&mut pass, &tris, &sd);
258 }
259 self.queue.submit([encoder.finish()]);
260 for id in &egui_out.textures_delta.free { self.egui_renderer.free_texture(id); }
261 }
262
263 pub fn new(window: Arc<Window>, backends: wgpu::Backends) -> Self {
264 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
265 backends,
266 ..Default::default()
267 });
268 let surface = instance.create_surface(Arc::clone(&window)).unwrap();
269 let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
270 power_preference: wgpu::PowerPreference::HighPerformance,
271 compatible_surface: Some(&surface),
272 force_fallback_adapter: false,
273 })).expect("no adapter");
274 let (device, queue) = pollster::block_on(adapter.request_device(
275 &wgpu::DeviceDescriptor::default(),
276 )).expect("no device");
277 let device = Arc::new(device);
278 let queue = Arc::new(queue);
279 let size = window.inner_size();
280 let caps = surface.get_capabilities(&adapter);
281 let fmt = caps.formats.iter().copied().find(|f| f.is_srgb()).unwrap_or(caps.formats[0]);
282 let surface_config = wgpu::SurfaceConfiguration {
283 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
284 format: fmt,
285 width: size.width,
286 height: size.height,
287 present_mode: wgpu::PresentMode::Fifo,
288 alpha_mode: caps.alpha_modes[0],
289 view_formats: vec![],
290 desired_maximum_frame_latency: 2,
291 };
292 surface.configure(&device, &surface_config);
293 let (depth_texture, depth_view) = create_depth(&device, size.width, size.height);
294 let egui_ctx = egui::Context::default();
295 let egui_winit = egui_winit::State::new(egui_ctx.clone(), egui_ctx.viewport_id(), &window, None, None, None);
296 let egui_renderer = egui_wgpu::Renderer::new(&device, fmt, egui_wgpu::RendererOptions {
297 msaa_samples: 1,
298 depth_stencil_format: None,
299 dithering: false,
300 predictable_texture_filtering: false,
301 });
302 Self {
303 window, surface: Some(surface), surface_config,
304 device, queue, depth_texture, depth_view,
305 egui_ctx, egui_winit, egui_renderer,
306 surface_format: fmt,
307 camera: Camera { view: Mat4::IDENTITY, proj: Mat4::IDENTITY },
308 suppress_keys: HashSet::new(),
309 }
310 }
311
312 pub fn resize(&mut self, w: u32, h: u32) {
313 if w == 0 || h == 0 { return; }
314 self.surface_config.width = w;
315 self.surface_config.height = h;
316 self.surface.as_ref().unwrap().configure(&self.device, &self.surface_config);
317 let (dt, dv) = create_depth(&self.device, w, h);
318 self.depth_texture = dt;
319 self.depth_view = dv;
320 }
321}
322
323#[cfg(feature = "egui")]
324fn create_depth(device: &Device, w: u32, h: u32) -> (wgpu::Texture, wgpu::TextureView) {
325 let tex = device.create_texture(&wgpu::TextureDescriptor {
326 label: Some("depth"),
327 size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
328 mip_level_count: 1,
329 sample_count: 1,
330 dimension: wgpu::TextureDimension::D2,
331 format: wgpu::TextureFormat::Depth32Float,
332 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
333 view_formats: &[],
334 });
335 let view = tex.create_view(&Default::default());
336 (tex, view)
337}
338
339#[cfg(feature = "egui")]
340pub trait App: Sized + 'static {
341 fn name() -> &'static str { "App" }
342 fn size() -> LogicalSize<u32> { LogicalSize::new(1280, 720) }
343 fn backends() -> wgpu::Backends { wgpu::Backends::default() }
344 fn new(core: &mut Core) -> Self;
345 fn on_event(&mut self, core: &mut Core, event: &WindowEvent) -> bool;
347 fn on_device_event(&mut self, core: &mut Core, event: &DeviceEvent) { let _ = (core, event); }
348 fn update(&mut self, core: &mut Core, dt: f32) { let _ = (core, dt); }
349 fn ui(&mut self, core: &mut Core, ctx: &egui::Context) { let _ = (core, ctx); }
350}
351
352#[cfg(feature = "egui")]
353struct AppRunner<G: App> {
354 core: Option<Core>,
355 game: Option<G>,
356 last: std::time::Instant,
357}
358
359#[cfg(feature = "egui")]
360impl<G: App> ApplicationHandler for AppRunner<G> {
361 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
362 let window = Arc::new(
363 event_loop.create_window(
364 egui_winit::winit::window::WindowAttributes::default()
365 .with_title(G::name())
366 .with_inner_size(G::size())
367 ).unwrap()
368 );
369 let mut core = Core::new(window, G::backends());
370 let game = G::new(&mut core);
371 self.core = Some(core);
372 self.game = Some(game);
373 }
374
375 fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) {
376 let (core, game) = match (self.core.as_mut(), self.game.as_mut()) {
377 (Some(c), Some(g)) => (c, g),
378 _ => return,
379 };
380
381 let consumed = game.on_event(core, &event);
382 if consumed { return; }
383
384 let _ = core.handle_window_event(&event);
385
386 match &event {
387 WindowEvent::CloseRequested => event_loop.exit(),
388 WindowEvent::Resized(size) => core.resize(size.width, size.height),
389 WindowEvent::RedrawRequested => {
390 let now = std::time::Instant::now();
391 let dt = (now - self.last).as_secs_f32().min(0.1);
392 self.last = now;
393
394 let (core, game) = (self.core.as_mut().unwrap(), self.game.as_mut().unwrap());
395
396 game.update(core, dt);
397
398 let output = match core.surface.as_ref().unwrap().get_current_texture() {
399 Ok(o) => o,
400 Err(_) => { core.window.request_redraw(); return; }
401 };
402 let view = output.texture.create_view(&Default::default());
403
404
405 let raw = core.egui_winit.take_egui_input(&core.window);
406 let egui_ctx = core.egui_ctx.clone();
407 let egui_out = egui_ctx.run(raw, |ctx| game.ui(core, ctx));
408
409 core.finish_egui(egui_out, &view);
410
411 output.present();
412 core.window.request_redraw();
413 }
414 _ => {}
415 }
416 }
417
418 fn device_event(&mut self, _: &ActiveEventLoop, _: DeviceId, event: DeviceEvent) {
419 if let (Some(c), Some(g)) = (self.core.as_mut(), self.game.as_mut()) {
420 g.on_device_event(c, &event);
421 }
422 }
423
424 fn exiting(&mut self, _: &ActiveEventLoop) {
425 self.game = None;
426 self.core = None;
427 }
428}
429
430#[cfg(feature = "egui")]
431pub fn run<G: App>() -> Result<(), EventLoopError> {
432 let event_loop = EventLoop::new()?;
433 let mut runner = AppRunner::<G> {
434 core: None, game: None, last: std::time::Instant::now()
435 };
436 event_loop.run_app(&mut runner)
437}
438
439
440