1#[cfg(feature = "with-gui")]
4use crate::gui::Gui;
5#[cfg(feature = "with-gui")]
6use crate::plugin_manager::GuiSystem;
7use crate::{
8 camera::Camera,
9 components::Projection,
10 config::Config,
11 forward_renderer::{render_passes::blit_pass::BlitPass, renderer::Renderer},
12 geom::Geom,
13 logger::gloss_setup_logger_from_config,
14 plugin_manager::{
15 plugins::{Plugin, Plugins},
16 systems::{LogicSystem, SystemMetadata},
17 },
18 scene::{Scene, GLOSS_CAM_NAME},
19 set_panic_hook,
20};
21
22use easy_wgpu::gpu::Gpu;
23use easy_wgpu::texture::Texture;
24#[cfg(feature = "with-gui")]
25use egui_winit::EventResponse;
26use gloss_utils::abi_stable_aliases::std_types::{RString, Tuple2};
27use log::{debug, warn};
28use winit::{
29 dpi::PhysicalSize,
30 event::TouchPhase,
31 event_loop::{ActiveEventLoop, EventLoopProxy},
32 keyboard::{KeyCode, PhysicalKey},
33 window::WindowId,
34};
35
36use core::time::Duration;
37use gloss_utils::io::FileType;
38use log::{error, info};
39use pollster::FutureExt;
40use std::{error::Error, sync::Arc};
41
42#[cfg(target_arch = "wasm32")]
43use wasm_bindgen::prelude::*;
44use wasm_timer::Instant;
45use winit::application::ApplicationHandler;
46#[cfg(target_arch = "wasm32")]
47use winit::platform::web::EventLoopExtWebSys;
48
49use winit::{
50 event::{ElementState, Event, WindowEvent},
51 event_loop::EventLoop,
52 window::Window,
53};
54
55pub struct GpuResources {
58 window: Arc<Window>,
59 surface: wgpu::Surface<'static>,
60 surface_config: wgpu::SurfaceConfiguration,
61 pub renderer: Renderer,
62
63 #[cfg(feature = "with-gui")]
64 pub gui: Option<Gui>,
65 _blit_pass: BlitPass, pub redraw_requested: bool, pub gpu: Gpu,
73}
74#[allow(clippy::missing_panics_doc)]
75#[allow(clippy::too_many_lines)]
76#[allow(unused)]
77impl GpuResources {
78 pub fn new(
79 event_loop: &ActiveEventLoop,
80 event_loop_proxy: &EventLoopProxy<CustomEvent>,
81 canvas_id_parsed: &Option<String>,
82 config: &Config,
83 ) -> Self {
84 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
86 backends: supported_backends(),
87 dx12_shader_compiler: wgpu::Dx12Compiler::default(),
88 flags: wgpu::InstanceFlags::default(),
89 gles_minor_version: wgpu::Gles3MinorVersion::Automatic,
90 });
91
92 let window = Viewer::create_window(event_loop, event_loop_proxy, &canvas_id_parsed.clone()).expect("failed to create initial window");
93
94 let window = Arc::new(window);
95
96 let surface = unsafe { instance.create_surface(window.clone()) }.unwrap();
97
98 cfg_if::cfg_if! {
100 if #[cfg(not(target_arch = "wasm32"))]{
101 let adapters = enumerate_adapters(&instance);
102 info!("Number of possible adapters: {:?}", adapters.len());
103 for (i, adapter) in adapters.iter().enumerate() {
104 info!("Adapter option {:?}: {:?}", i + 1, adapter.get_info());
105 }
106 }
107 }
108 let adapter = get_adapter(&instance, Some(&surface));
109 info!("Selected adapter: {:?}", adapter.get_info());
110
111 let mut desired_features = wgpu::Features::empty();
114 cfg_if::cfg_if! {
115 if #[cfg(not(target_arch = "wasm32"))]{
116 desired_features = desired_features.union(wgpu::Features::TIMESTAMP_QUERY.union(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES));
117 desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_POINT);
118 desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_LINE);
119 }
120 }
121 let required_features = adapter.features().intersection(desired_features); let max_limits = adapter.limits();
127 #[allow(unused_mut)]
128 let mut limits_to_request = wgpu::Limits::default();
129 if cfg!(target_arch = "wasm32") {
130 limits_to_request = wgpu::Limits::downlevel_webgl2_defaults();
131 }
132 limits_to_request.max_texture_dimension_1d = max_limits.max_texture_dimension_1d;
133 limits_to_request.max_texture_dimension_2d = max_limits.max_texture_dimension_2d;
134 limits_to_request.max_buffer_size = max_limits.max_buffer_size;
135
136 let mut memory_hints = wgpu::MemoryHints::Performance;
137 if cfg!(target_arch = "wasm32") {
138 memory_hints = wgpu::MemoryHints::MemoryUsage;
141 }
142
143 let (device, queue) = adapter
145 .request_device(
146 &wgpu::DeviceDescriptor {
147 label: None,
148 required_features,
149 required_limits: limits_to_request,
150 memory_hints,
151 },
152 None, )
154 .block_on()
155 .expect("A device and queue could not be created. Maybe there's a driver issue on your machine?");
156 let gpu = Gpu::new(adapter, instance, device, queue);
157
158 let surface_caps = surface.get_capabilities(gpu.adapter());
160 let surface_format = surface_caps
163 .formats
164 .iter()
165 .copied()
166 .find(|f| !f.is_srgb())
167 .unwrap_or(surface_caps.formats[0]);
168
169 let mut size = PhysicalSize::new(1200, 1200);
172 #[cfg(target_arch = "wasm32")]
173 if let Some(canvas_size) = Viewer::get_html_elem_size(&canvas_id_parsed.as_ref().unwrap()) {
174 size = canvas_size.to_physical(window.scale_factor());
175 }
176 let surface_config = wgpu::SurfaceConfiguration {
179 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
180 format: surface_format,
181 width: size.width,
182 height: size.height,
183 present_mode: wgpu::PresentMode::AutoNoVsync, alpha_mode: surface_caps.alpha_modes[0],
186 view_formats: vec![],
187 desired_maximum_frame_latency: 2,
188 };
189 surface.configure(gpu.device(), &surface_config);
190
191 #[cfg(feature = "with-gui")]
193 let gui = if config.core.enable_gui {
194 let mut gui = Gui::new(&window, &gpu, surface_format);
195 gui.hidden = config.core.gui_start_hidden;
196 Some(gui)
197 } else {
198 None
199 };
200
201 let renderer = Renderer::new(&gpu, &config.render, Some(surface_format));
202 let blit_pass = BlitPass::new(&gpu, &surface_format);
203
204 Self {
205 window,
206 surface,
207 surface_config,
208 gpu,
209 renderer,
210 #[cfg(feature = "with-gui")]
211 gui,
212 _blit_pass: blit_pass,
213 redraw_requested: false,
214 }
215 }
216 pub fn request_redraw(&mut self) {
217 if self.redraw_requested {
218 debug!("Redraw was already requested, ignoring.");
219 } else {
220 self.window.request_redraw();
221 self.redraw_requested = true;
222 }
223 }
224}
225
226#[derive(Debug)]
228#[repr(C)]
229pub struct Runner {
230 event_loop: Option<EventLoop<CustomEvent>>, event_loop_proxy: EventLoopProxy<CustomEvent>,
232 pub autostart: bool, pub is_running: bool,
234 pub do_render: bool,
235 pub first_time: bool,
236 pub did_warmup: bool,
237 frame_is_started: bool, time_init: Instant, time_last_frame: Instant, dt: Duration, }
247#[allow(unused)]
248impl Runner {
249 #[allow(clippy::missing_panics_doc)]
250 #[allow(clippy::new_without_default)]
251 pub fn new(canvas_id: &Option<String>) -> Self {
252 let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
253 let event_loop_proxy: EventLoopProxy<CustomEvent> = event_loop.create_proxy();
254
255 #[cfg(target_arch = "wasm32")]
256 if let Some(ref canvas_id) = canvas_id {
257 Viewer::add_listener_to_canvas_resize(&event_loop, &canvas_id);
258 Viewer::add_listener_to_context(&event_loop, &canvas_id);
259 }
260
261 let time_init = Instant::now();
262 let time_last_frame = Instant::now();
263 Self {
264 event_loop: Some(event_loop),
265 event_loop_proxy,
266 autostart: true,
267 is_running: false,
268 do_render: true,
269 first_time: true,
270 did_warmup: false,
271 frame_is_started: false,
272 time_init,
273 time_last_frame,
274 dt: Duration::ZERO,
275 }
276 }
277 pub fn time_since_init(&self) -> Duration {
278 if self.first_time {
279 Duration::ZERO
280 } else {
281 self.time_init.elapsed()
282 }
283 }
284 pub fn update_dt(&mut self) {
285 if self.first_time {
286 self.dt = Duration::ZERO;
287 } else {
288 self.dt = self.time_last_frame.elapsed();
289 }
290 }
291 pub fn override_dt(&mut self, new_dt: f32) {
292 self.dt = Duration::from_secs_f32(new_dt);
293 }
294 pub fn dt(&self) -> Duration {
295 self.dt
296 }
297}
298
299#[repr(C)]
306pub struct Viewer {
307 pub gpu_res: Option<GpuResources>,
309
310 pub camera: Camera, pub scene: Scene,
313
314 window_size: winit::dpi::PhysicalSize<u32>, canvas_id_parsed: Option<String>,
318
319 pub config: Config,
320 pub runner: Runner,
321 pub plugins: Plugins,
322}
323
324impl Viewer {
325 pub fn new(config_path: Option<&str>) -> Self {
326 let config = Config::new(config_path);
327 Self::new_with_config(&config)
328 }
329
330 #[allow(clippy::too_many_lines)]
331 #[allow(clippy::missing_panics_doc)]
332 pub fn new_with_config(config: &Config) -> Self {
333 set_panic_hook();
334
335 if config.core.auto_create_logger {
336 gloss_setup_logger_from_config(config);
337 }
338
339 re_memory::accounting_allocator::set_tracking_callstacks(config.core.enable_memory_profiling_callstacks);
341
342 let canvas_id_parsed = config.core.canvas_id.as_ref().map(|canvas_id| String::from("#") + canvas_id);
343
344 let runner = Runner::new(&canvas_id_parsed);
346
347 let window_size = winit::dpi::PhysicalSize::new(100, 100);
348
349 let mut scene = Scene::new();
350 let camera = Camera::new(GLOSS_CAM_NAME, &mut scene, false); Self {
353 gpu_res: None,
354 runner,
355 scene,
356 camera,
357 plugins: Plugins::new(),
358 canvas_id_parsed: canvas_id_parsed.clone(),
359 config: config.clone(),
360 window_size,
361 }
362 }
363
364 #[cfg(target_arch = "wasm32")]
367 fn add_listener_to_canvas_resize(event_loop: &EventLoop<CustomEvent>, canvas_id: &str) {
368 let event_loop_proxy = event_loop.create_proxy();
374 let canvas_id = String::from(canvas_id); let resize = move || {
378 if let Some(size) = Viewer::get_html_elem_size(&canvas_id) {
379 let event_resize = CustomEvent::Resize(size.width, size.height);
380 event_loop_proxy.send_event(event_resize).ok();
381 }
382 };
383
384 let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_: web_sys::Event| {
388 resize();
389 }) as Box<dyn FnMut(_)>);
390 let window = web_sys::window().unwrap();
391
392 window
393 .add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref())
394 .unwrap();
395 closure.forget();
396 }
397
398 #[cfg(target_arch = "wasm32")]
399 fn add_listener_to_context(event_loop: &EventLoop<CustomEvent>, canvas_id: &str) {
400 let canvas_id = String::from(canvas_id); let win = web_sys::window().unwrap();
403 let doc = win.document().unwrap();
404 let element = doc.query_selector(&canvas_id).ok().unwrap().unwrap();
405 let canvas = element.dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
406
407 let event_loop_proxy = event_loop.create_proxy();
409 let context_lost = move || {
410 let event = CustomEvent::ContextLost;
411 info!("SENDING USER EVENT: context loss");
412 event_loop_proxy.send_event(event).ok();
413 };
414
415 let event_loop_proxy = event_loop.create_proxy();
416 let context_restored = move || {
417 let event = CustomEvent::ContextRestored;
418 info!("SENDING USER EVENT: context restored");
419 event_loop_proxy.send_event(event).ok();
420 };
421
422 let closure_lost = wasm_bindgen::closure::Closure::wrap(Box::new(move |_: web_sys::Event| {
423 context_lost();
424 }) as Box<dyn FnMut(_)>);
425 let closure_restored = wasm_bindgen::closure::Closure::wrap(Box::new(move |_: web_sys::Event| {
426 context_restored();
427 }) as Box<dyn FnMut(_)>);
428
429 canvas
432 .add_event_listener_with_callback("webglcontextlost", closure_lost.as_ref().unchecked_ref())
433 .unwrap();
434 canvas
435 .add_event_listener_with_callback("webglcontextrestored", closure_restored.as_ref().unchecked_ref())
436 .unwrap();
437 closure_lost.forget();
438 closure_restored.forget();
439 }
440
441 #[cfg(target_arch = "wasm32")]
444 fn get_html_elem_size(selector: &str) -> Option<winit::dpi::LogicalSize<f32>> {
445 let win = web_sys::window().unwrap();
447 let doc = win.document().unwrap();
448 let element = doc.query_selector(selector).ok()??;
449 let parent_element = element.parent_element()?;
450 let rect = parent_element.get_bounding_client_rect();
451 return Some(winit::dpi::LogicalSize::new(rect.width() as f32, rect.height() as f32));
452 }
453
454 #[cfg(target_arch = "wasm32")]
455 pub fn resize_to_canvas(&self) {
456 if let Some(size) = Self::get_html_elem_size(&self.canvas_id_parsed.as_ref().unwrap()) {
457 warn!("size is {:?}", size);
459 let event_resize = CustomEvent::Resize(size.width, size.height);
460 self.runner.event_loop_proxy.send_event(event_resize).ok();
461 }
462 }
463
464 #[allow(clippy::cast_precision_loss)]
465 fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
466 debug!("resizing new_size is {:?}", new_size);
467 let max_2d_size = self.gpu_res.as_ref().unwrap().gpu.limits().max_texture_dimension_2d;
468 if new_size.width > 16 && new_size.height > 16 && new_size.width < max_2d_size && new_size.height < max_2d_size {
469 let gpu_res = self.gpu_res.as_mut().unwrap();
470 self.window_size = new_size;
472 gpu_res.surface_config.width = new_size.width;
473 gpu_res.surface_config.height = new_size.height;
474 gpu_res.surface.configure(gpu_res.gpu.device(), &gpu_res.surface_config);
475 gpu_res.request_redraw();
476
477 if self.scene.world.has::<Projection>(self.camera.entity).unwrap() {
480 self.camera
481 .set_aspect_ratio(new_size.width as f32 / new_size.height as f32, &mut self.scene);
482 }
483
484 #[cfg(feature = "with-gui")]
486 if let Some(ref mut gui) = gpu_res.gui {
487 gui.resize(new_size.width, new_size.height);
488 }
489 } else {
490 error!("trying to resize to unsuported size of {new_size:?}");
491 }
492 }
494
495 pub fn request_redraw(&mut self) {
498 if let Some(gpu_res) = self.gpu_res.as_mut() {
499 gpu_res.request_redraw();
500 } else {
501 error!("No gpu_res created yet");
502 }
503 }
504
505 #[cfg(not(target_arch = "wasm32"))]
510 pub fn update(&mut self) {
511 self.event_loop_one_iter();
512 let _ = self.render();
513 }
514
515 #[cfg(not(target_arch = "wasm32"))]
516 pub fn update_offscreen_texture(&mut self) {
517 self.event_loop_one_iter();
518 let _ = self.render_to_texture();
519 }
520
521 fn process_custom_resize_events(&mut self, event: &Event<CustomEvent>) -> bool {
524 match event {
525 Event::UserEvent(CustomEvent::Resize(new_width, new_height)) => {
526 debug!("rs: handling resize canvas: {:?}", event);
527 let logical_size = winit::dpi::LogicalSize {
528 width: *new_width,
529 height: *new_height,
530 };
531 let _ = self.gpu_res.as_ref().unwrap().window.request_inner_size(logical_size);
536
537 self.resize(self.gpu_res.as_ref().unwrap().window.inner_size()); true }
546 _ => false, }
548 }
549
550 fn process_custom_context_event(&mut self, event: &Event<CustomEvent>, event_loop: &ActiveEventLoop) -> bool {
551 match event {
552 Event::UserEvent(event) => {
553 match event {
554 CustomEvent::ContextLost => {
555 info!("rs: handling context lost");
556 self.suspend();
557 true }
560 CustomEvent::ContextRestored => {
561 info!("rs: handling context restored");
562 self.resume(event_loop);
563 true }
566 _ => false, }
568 }
569 _ => false, }
571 }
572
573 #[allow(clippy::collapsible_match)]
574 fn process_custom_other_event(&mut self, event: &Event<CustomEvent>, event_loop: &ActiveEventLoop) -> bool {
575 match event {
576 Event::UserEvent(event) => {
577 match event {
578 CustomEvent::ResumeLoop => {
579 info!("rs: handling custom resume loop");
580 self.resume(event_loop);
581 true
582 }
583 CustomEvent::StopLoop => {
584 info!("rs: handling custom stop loop");
585 self.runner.is_running = false;
586 event_loop.exit();
587 true
588 }
589 _ => false, }
591 }
592 _ => false, }
594 }
595
596 #[allow(clippy::too_many_lines)]
645 fn process_window_events(&mut self, event: &WindowEvent, event_loop: &ActiveEventLoop) -> bool {
646 match event {
647 WindowEvent::RedrawRequested => {
648 {
649 let gpu_res = self.gpu_res.as_mut().unwrap();
650 gpu_res.redraw_requested = false;
651 }
652
653 if !self.runner.do_render {
654 return false;
655 }
656
657 debug!("gloss: render");
658
659 if self.runner.frame_is_started {
662 debug!("the frame was already started, we are ignoring this re-render");
663 return true;
664 }
665 self.start_frame();
666
667 match self.render() {
668 Ok(()) => {}
669 Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
671 self.resize(self.gpu_res.as_ref().unwrap().window.inner_size());
672 }
673 Err(wgpu::SurfaceError::OutOfMemory) => {
675 error!("SurfaceError: out of memory");
676 }
678 Err(wgpu::SurfaceError::Timeout) => error!("SurfaceError: timeout"),
680 }
681 debug!("finsihed handing RedrawRequested");
682 true
683 }
684 WindowEvent::Resized(physical_size) => {
685 self.resize(*physical_size);
686 true
687 }
688 WindowEvent::DroppedFile(path_buf) => {
695 info!("Dropped file {:?}", path_buf);
696 let path = path_buf.to_str().unwrap();
697
698 self.render().ok();
701
702 #[allow(unused_mut)]
704 #[cfg(feature = "with-gui")]
705 {
706 let gpu_res = self.gpu_res.as_mut().unwrap();
707 let gui = gpu_res.gui.as_mut().unwrap();
708 if gui.is_hovering() {
709 gui.on_drop(path_buf, &mut self.scene);
710 return true;
711 }
712 }
713
714 let filetype = match path_buf.extension() {
716 Some(extension) => FileType::find_match(extension.to_str().unwrap_or("")),
717 None => FileType::Unknown,
718 };
719 match filetype {
720 FileType::Obj | FileType::Ply => {
721 let builder = Geom::build_from_file(path);
722 let name = self.scene.get_unused_name();
723 self.scene.get_or_create_entity(&name).insert_builder(builder);
724 return true;
725 }
726 FileType::Unknown => {
727 info!(
728 "Gloss doesn't know how to handle dropped file {:?}. trying to let plugins handle it",
729 path
730 );
731 }
732 }
733
734 let event = crate::plugin_manager::Event::DroppedFile(RString::from(path));
736 let handled = self.plugins.try_handle_event(&mut self.scene, &mut self.runner, &event);
737
738 if !handled {
739 info!("Neither Gloss nor any of the plugin could load the dropped file {:?}", path);
740 return false;
741 }
742
743 true
744 }
745 WindowEvent::KeyboardInput { event, .. } => {
746 if let PhysicalKey::Code(code) = event.physical_key {
747 if event.state == ElementState::Pressed && !event.repeat {
748 #[allow(clippy::single_match)] match code {
750 KeyCode::KeyH => {
751 #[cfg(feature = "with-gui")]
753 {
754 if let Some(gpu_res) = self.gpu_res.as_mut() {
755 if let Some(gui) = gpu_res.gui.as_mut() {
756 gui.hidden = !gui.hidden;
757 gpu_res.request_redraw();
758 }
759 }
760 }
761 }
762 _ => {}
763 }
764 }
765 }
766 true
767 }
768
769 WindowEvent::CloseRequested {} | WindowEvent::Destroyed {} => {
770 event_loop.exit();
771 true
772 }
773
774 WindowEvent::Occluded(_) => {
775 self.camera.reset_all_touch_presses(&mut self.scene);
776 true
777 }
778 _ => false, }
780 }
781
782 #[allow(clippy::cast_possible_truncation)]
785 fn process_input_events(&mut self, event: &WindowEvent) -> bool {
786 if !self.camera.is_initialized(&self.scene) {
788 return false;
789 }
790
791 let gpu_res = self.gpu_res.as_mut().unwrap();
792
793 let consumed = match event {
794 WindowEvent::MouseInput { button, state, .. } => {
795 if *state == ElementState::Pressed {
797 self.camera.mouse_pressed(button, &mut self.scene);
798 } else {
799 self.camera.mouse_released(&mut self.scene);
800 }
801 true
802 }
803 WindowEvent::MouseWheel { delta, .. } => {
804 self.camera.process_mouse_scroll(delta, &mut self.scene);
805 true
806 }
807 WindowEvent::CursorMoved { position, .. } => {
808 self.camera.process_mouse_move(
809 position.x as f32,
810 position.y as f32,
811 self.window_size.width,
812 self.window_size.height,
813 &mut self.scene,
814 );
815 true
816 }
817 WindowEvent::Touch(touch) => {
818 if touch.phase == TouchPhase::Started {
819 self.camera.touch_pressed(touch, &mut self.scene);
820 }
821 if touch.phase == TouchPhase::Ended || touch.phase == TouchPhase::Cancelled {
822 self.camera.touch_released(touch, &mut self.scene);
823 }
824 if touch.phase == TouchPhase::Moved {
825 self.camera
826 .process_touch_move(touch, self.window_size.width, self.window_size.height, &mut self.scene);
827 }
828 true
829 }
830 _ => false, };
832
833 if consumed {
834 gpu_res.request_redraw();
835 }
836
837 consumed
838 }
839
840 #[cfg(feature = "with-gui")]
841 fn process_gui_events(&mut self, event: &WindowEvent) -> EventResponse {
842 let gpu_res = self.gpu_res.as_mut().unwrap();
843 if let Some(mut gui) = gpu_res.gui.take() {
846 let response = gui.on_event(&gpu_res.window, event);
847 gpu_res.gui = Some(gui); response
849 } else {
850 EventResponse {
851 repaint: false,
852 consumed: false,
853 }
854 }
855 }
856
857 fn process_all_events(&mut self, event: &Event<CustomEvent>, event_loop: &ActiveEventLoop) {
859 if !self.runner.is_running {
860 if let Event::WindowEvent { ref event, window_id: _ } = event {
863 if event == &WindowEvent::RedrawRequested {
864 let gpu_res = self.gpu_res.as_mut().unwrap();
866 gpu_res.redraw_requested = false;
867 }
868 }
869 if let Event::WindowEvent {
871 event: WindowEvent::Resized(physical_size),
872 window_id: _,
873 } = event
874 {
875 self.resize(*physical_size);
876 }
877
878 return; }
880
881 match event {
887 Event::WindowEvent { ref event, window_id } if *window_id == self.gpu_res.as_ref().unwrap().window.id() => {
888 if self.window_size.height < 16 || self.window_size.width < 16 {
893 warn!("Skipping rendering and trying again to resize. Window size is {:?}", self.window_size);
894 #[cfg(target_arch = "wasm32")]
897 self.resize_to_canvas();
898 let gpu_res = self.gpu_res.as_mut().unwrap();
899 gpu_res.request_redraw();
900 return;
901 }
902
903 self.process_window_events(event, event_loop); cfg_if::cfg_if! {
905 if #[cfg(feature = "with-gui")]{
906 let res = self.process_gui_events(event);
907 if res.consumed {
909 self.gpu_res.as_mut().unwrap().request_redraw();
910 } else {
911 self.process_input_events(event);
912 }
913 if let Some(ref gui) = self.gpu_res.as_mut().unwrap().gui {
916 if gui.wants_pointer_input() {
917 self.camera.mouse_released(&mut self.scene);
918 }
919 }
920 }else{ self.process_input_events(event);
922 }
923 }
924 }
925 _ => {}
926 }
927 }
928
929 #[allow(unused)]
932 #[cfg(not(target_arch = "wasm32"))] fn event_loop_one_iter(&mut self) {
935 use winit::platform::pump_events::EventLoopExtPumpEvents;
941 let mut event_loop = self.runner.event_loop.take().unwrap();
942 self.runner.is_running = true;
943 self.runner.do_render = false; let timeout = Some(Duration::ZERO);
946 event_loop.pump_app_events(timeout, self);
947 self.runner.is_running = false;
953
954 self.runner.event_loop = Some(event_loop);
956 }
957
958 pub fn start_frame(&mut self) -> Duration {
961 #[cfg(not(target_arch = "wasm32"))]
963 {
964 if self.gpu_res.is_none() {
965 self.event_loop_one_iter(); }
967 assert!(self.gpu_res.is_some(), "GPU Res has not been created!");
968 }
969 if !self.runner.did_warmup {
971 self.runner.did_warmup = true; self.warmup();
974 self.warmup(); }
976
977 self.runner.update_dt();
978 debug!("after update dt it is {:?}", self.runner.dt());
979 self.runner.time_last_frame = Instant::now();
980
981 self.runner.frame_is_started = true;
982
983 self.runner.dt
984 }
985
986 pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
991 if !self.runner.frame_is_started {
992 error!("The frame was not started so this might contain stale dt. Please use viewer.start_frame() before doing a v.render()");
993 }
994
995 if self.runner.first_time {
999 self.runner.time_init = Instant::now();
1000 }
1001
1002 let gpu_res = self.gpu_res.as_mut().unwrap();
1003 self.plugins.run_logic_systems(gpu_res, &mut self.scene, &mut self.runner, true);
1004
1005 let output = gpu_res.surface.get_current_texture()?;
1007 let out_view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
1008 let out_width = output.texture.width();
1009 let out_height = output.texture.height();
1010
1011 let dt = self.runner.dt();
1013
1014 self.camera.on_window_resize(out_width, out_height, &mut self.scene);
1015
1016 gpu_res.renderer.render_to_view(
1019 &out_view,
1020 &gpu_res.gpu,
1023 &mut self.camera,
1024 &mut self.scene,
1025 &mut self.config,
1026 dt,
1027 );
1028
1029 #[cfg(feature = "with-gui")]
1032 if let Some(ref mut gui) = gpu_res.gui {
1033 gui.render(
1034 &gpu_res.window,
1035 &gpu_res.gpu,
1036 &gpu_res.renderer,
1037 &self.runner,
1038 &mut self.scene,
1039 &self.plugins,
1040 &mut self.config,
1041 &out_view,
1042 );
1043 }
1044
1045 output.present();
1047
1048 self.runner.first_time = false;
1049 self.runner.frame_is_started = false;
1050
1051 Ok(())
1052 }
1053
1054 pub fn render_to_texture(&mut self) -> Result<(), wgpu::SurfaceError> {
1059 if !self.runner.frame_is_started {
1060 error!("The frame was not started so this might contain stale dt. Please use viewer.start_frame() before doing a v.render_to_texture()");
1061 }
1062
1063 if self.runner.first_time {
1067 self.runner.time_init = Instant::now();
1068 }
1069
1070 let gpu_res = self.gpu_res.as_mut().unwrap();
1071 self.plugins.run_logic_systems(gpu_res, &mut self.scene, &mut self.runner, true);
1072
1073 let out_tex = gpu_res.renderer.rendered_tex();
1076 let out_width = out_tex.width();
1077 let out_height = out_tex.height();
1078
1079 let dt = self.runner.dt();
1081
1082 self.camera.on_window_resize(out_width, out_height, &mut self.scene);
1083
1084 gpu_res
1087 .renderer
1088 .render_to_texture(&gpu_res.gpu, &mut self.camera, &mut self.scene, &mut self.config, dt);
1089
1090 #[cfg(feature = "with-gui")]
1093 if let Some(ref mut gui) = gpu_res.gui {
1094 let out_view = &gpu_res.renderer.rendered_tex().view;
1095 gui.render(
1096 &gpu_res.window,
1097 &gpu_res.gpu,
1098 &gpu_res.renderer,
1099 &self.runner,
1100 &mut self.scene,
1101 &self.plugins,
1102 &mut self.config,
1103 out_view,
1104 );
1105 }
1106
1107 self.runner.first_time = false;
1108 self.runner.frame_is_started = false;
1109
1110 Ok(())
1111 }
1112
1113 #[cfg(not(target_arch = "wasm32"))]
1142 pub fn run(&mut self) {
1143 let event_loop = self.runner.event_loop.take().unwrap();
1144 self.runner.is_running = self.runner.autostart;
1145 let _ = event_loop.run_app(self);
1146 }
1147
1148 #[cfg(target_arch = "wasm32")]
1149 pub fn run(mut self) {
1150 let event_loop = self.runner.event_loop.take().unwrap();
1151 self.runner.is_running = self.runner.autostart;
1152 let _ = event_loop.spawn_app(self);
1153 }
1154
1155 #[allow(clippy::missing_panics_doc)]
1158 #[allow(unreachable_code)] pub fn run_static_ref(&'static mut self) {
1160 let event_loop = self.runner.event_loop.take().unwrap();
1161 self.runner.is_running = self.runner.autostart;
1162
1163 cfg_if::cfg_if! {
1165 if #[cfg(not(target_arch = "wasm32"))]{
1166 let _ = event_loop.run_app(self);
1167 }else{
1168 let _ = event_loop.spawn_app(self);
1169 }
1170 }
1171 }
1172
1173 #[allow(clippy::missing_panics_doc)]
1177 pub fn recreate_event_loop(&mut self) {
1178 self.stop_event_loop();
1179 self.suspend(); let runner = Runner::new(&self.canvas_id_parsed);
1181 self.runner = runner;
1182 let event_stop = CustomEvent::ResumeLoop;
1184 self.runner.event_loop_proxy.send_event(event_stop).ok();
1185 }
1186
1187 pub fn stop_event_loop(&self) {
1188 let event_stop = CustomEvent::StopLoop;
1189 self.runner.event_loop_proxy.send_event(event_stop).ok();
1190 }
1191
1192 fn resume(&mut self, event_loop: &ActiveEventLoop) {
1193 self.runner.is_running = self.runner.autostart;
1195 if self.gpu_res.is_none() {
1196 self.gpu_res = Some(GpuResources::new(
1197 event_loop,
1198 &self.runner.event_loop_proxy,
1199 &self.canvas_id_parsed.clone(),
1200 &self.config,
1201 ));
1202 self.scene.world.set_trackers_changed(); self.gpu_res.as_mut().unwrap().request_redraw();
1209 }
1210 #[cfg(target_arch = "wasm32")]
1211 self.resize_to_canvas()
1212 }
1213
1214 pub fn suspend(&mut self) {
1215 info!("RS: suspend");
1218 self.runner.is_running = false;
1219 self.scene.remove_all_gpu_components();
1220 self.gpu_res.take();
1221 self.camera.reset_all_touch_presses(&mut self.scene);
1222 }
1223
1224 pub fn warmup(&mut self) {
1229 debug!("Starting warmup");
1230 self.start_frame();
1232 self.run_manual_plugins(); let _ = self.render();
1238 self.reset_for_first_time();
1239 debug!("finished warmup");
1240 }
1241
1242 pub fn reset_for_first_time(&mut self) {
1243 self.runner.first_time = true;
1244 }
1245
1246 pub fn add_logic_system(&mut self, sys: LogicSystem) {
1247 self.plugins.logic_systems.push(Tuple2(sys, SystemMetadata::default()));
1248 }
1249
1250 #[cfg(feature = "with-gui")]
1251 pub fn add_gui_system(&mut self, sys: GuiSystem) {
1252 self.plugins.gui_systems.push(Tuple2(sys, SystemMetadata::default()));
1253 }
1254
1255 #[allow(clippy::missing_panics_doc)]
1256 pub fn run_manual_plugins(&mut self) {
1257 {
1258 let gpu_res = self.gpu_res.as_mut().unwrap();
1260 self.plugins.run_logic_systems(gpu_res, &mut self.scene, &mut self.runner, false);
1261 }
1263 }
1264
1265 pub fn insert_plugin<T: Plugin + 'static>(&mut self, plugin: &T) {
1266 self.plugins.insert_plugin(plugin);
1267 }
1268
1269 pub fn wait_gpu_finish(&self) -> wgpu::MaintainResult {
1272 self.gpu_res.as_ref().unwrap().gpu.device().poll(wgpu::Maintain::Wait)
1273 }
1274
1275 fn create_window(
1278 event_loop: &ActiveEventLoop,
1279 _event_loop_proxy: &EventLoopProxy<CustomEvent>,
1280 _canvas_id: &Option<String>,
1281 ) -> Result<Window, Box<dyn Error>> {
1282 #[allow(unused_mut)]
1285 let mut window_attributes = Window::default_attributes().with_title("Gloss").with_maximized(true);
1286
1287 #[cfg(target_arch = "wasm32")]
1289 {
1290 use winit::platform::web::WindowAttributesExtWebSys;
1291 let window = web_sys::window().unwrap();
1292 let document = window.document().unwrap();
1293 let canvas = document
1294 .query_selector(&_canvas_id.as_ref().unwrap())
1295 .expect("Cannot query for canvas element.");
1296 if let Some(canvas) = canvas {
1297 let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok();
1298
1299 window_attributes = window_attributes.with_canvas(canvas).with_prevent_default(false).with_append(false)
1301 } else {
1302 panic!("Cannot find element: {:?}.", _canvas_id.as_ref().unwrap());
1303 }
1304 }
1305
1306 let window = event_loop.create_window(window_attributes)?;
1307
1308 Ok(window)
1309 }
1310
1311 pub fn override_dt(&mut self, new_dt: f32) {
1312 self.runner.override_dt(new_dt);
1313 }
1314
1315 pub fn get_final_tex(&self) -> &Texture {
1316 let tex = self.gpu_res.as_ref().unwrap().renderer.rendered_tex();
1317 tex
1318 }
1319
1320 pub fn get_final_depth(&self) -> &Texture {
1321 let depth = self.gpu_res.as_ref().unwrap().renderer.depth_buffer();
1322 depth
1323 }
1324}
1325
1326impl ApplicationHandler<CustomEvent> for Viewer {
1327 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
1328 self.resume(event_loop);
1329 }
1330
1331 fn user_event(&mut self, event_loop: &ActiveEventLoop, event: CustomEvent) {
1332 self.process_custom_context_event(&Event::UserEvent(event), event_loop);
1333 self.process_custom_resize_events(&Event::UserEvent(event));
1334 self.process_custom_other_event(&Event::UserEvent(event), event_loop);
1335 }
1336 fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
1337 if window_id != self.gpu_res.as_ref().unwrap().window.id() {
1338 return;
1339 }
1340 self.process_all_events(&Event::WindowEvent { window_id, event }, event_loop);
1341 }
1342 #[allow(unused_variables)]
1343 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
1344 #[cfg(not(target_arch = "wasm32"))]
1345 {
1346 if let Some(gpu_res) = self.gpu_res.as_mut() {
1347 gpu_res.request_redraw();
1348 }
1349 }
1350 }
1351
1352 fn suspended(&mut self, _event_loop: &ActiveEventLoop) {
1353 debug!("Handling Suspended event");
1354 self.suspend();
1355 }
1356
1357 fn exiting(&mut self, event_loop: &ActiveEventLoop) {
1358 self.runner.is_running = false;
1359 event_loop.exit();
1360 }
1361}
1362
1363pub fn supported_backends() -> wgpu::Backends {
1366 if cfg!(target_arch = "wasm32") {
1367 wgpu::Backends::GL
1369 } else {
1370 wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::VULKAN | wgpu::Backends::METAL)
1372 }
1373}
1374
1375pub fn get_adapter(instance: &wgpu::Instance, surface: Option<&wgpu::Surface>) -> wgpu::Adapter {
1378 #[cfg(not(target_arch = "wasm32"))]
1379 fn remove_from_vec(vec: &mut Vec<wgpu::Adapter>, idx_str: &str) -> wgpu::Adapter {
1380 let idx = idx_str.split(',').next().unwrap().parse::<usize>().unwrap(); assert!(
1383 (0..vec.len()).contains(&idx),
1384 "Tried to index device with idx {} but we only have detected {} devices",
1385 idx,
1386 vec.len()
1387 );
1388
1389 info!("Selecting adapter with idx {}", idx);
1390 vec.remove(idx)
1391 }
1392
1393 cfg_if::cfg_if! {
1394 if #[cfg(target_arch = "wasm32")]{
1395 instance
1396 .request_adapter(&wgpu::RequestAdapterOptions {
1397 power_preference: wgpu::PowerPreference::HighPerformance,
1398 compatible_surface: surface,
1399 force_fallback_adapter: false,
1400 })
1401 .block_on()
1402 .expect("An adapter could not be found. Maybe there's a driver issue on your machine?")
1403 }else {
1404 let mut adapters = enumerate_adapters(instance);
1405
1406 let wgpu_dev_id = std::env::var("WGPU_VISIBLE_DEVICES");
1407 let cuda_dev_id = std::env::var("CUDA_VISIBLE_DEVICES");
1408 match wgpu_dev_id {
1409 Ok(idx) => remove_from_vec(&mut adapters, &idx),
1410 Err(_) => match cuda_dev_id {
1411 Ok(idx) => remove_from_vec(&mut adapters, &idx),
1412 Err(_) => instance
1413 .request_adapter(&wgpu::RequestAdapterOptions {
1414 power_preference: wgpu::PowerPreference::HighPerformance,
1415 compatible_surface: surface,
1416 force_fallback_adapter: false,
1417 })
1418 .block_on()
1419 .expect("An adapter could not be found. Maybe there's a driver issue on your machine?"),
1420 },
1421 }
1422 }
1423 }
1424}
1425
1426#[cfg(not(target_arch = "wasm32"))]
1430pub fn enumerate_adapters(instance: &wgpu::Instance) -> Vec<wgpu::Adapter> {
1431 let mut adapters = instance.enumerate_adapters(wgpu::Backends::all());
1432
1433 adapters.sort_by_key(|x| (x.get_info().device_type as i32 - wgpu::DeviceType::DiscreteGpu as i32).abs());
1435
1436 adapters
1437}
1438
1439#[derive(Debug, Clone, Copy)]
1441pub enum CustomEvent {
1442 Resize(f32, f32),
1443 ContextLost,
1444 ContextRestored,
1445 ResumeLoop,
1446 StopLoop,
1447}