1#[cfg(feature = "with-gui")]
4use crate::gui::Gui;
5#[cfg(feature = "with-gui")]
6use crate::plugin_manager::GuiSystem;
7use crate::{
8 builders,
9 camera::Camera,
10 components::Projection,
11 config::Config,
12 forward_renderer::{render_passes::blit_pass::BlitPass, renderer::Renderer},
13 logger::gloss_setup_logger_from_config,
14 plugin_manager::{
15 plugins::{Plugin, Plugins},
16 systems::{LogicSystem, SystemMetadata},
17 InternalPlugins,
18 },
19 scene::{Scene, GLOSS_CAM_NAME},
20 selector::SelectorPlugin,
21 set_panic_hook,
22};
23
24use easy_wgpu::gpu::Gpu;
25use easy_wgpu::texture::Texture;
26#[cfg(feature = "with-gui")]
27use egui_winit::EventResponse;
28use gloss_utils::abi_stable_aliases::std_types::{RString, Tuple2};
29use log::{debug, warn};
30use winit::{
31 dpi::PhysicalSize,
32 event::TouchPhase,
33 event_loop::{ActiveEventLoop, EventLoopProxy},
34 keyboard::{KeyCode, PhysicalKey},
35 window::WindowId,
36};
37
38use core::time::Duration;
39use gloss_utils::io::FileType;
40use log::{error, info};
41use pollster::FutureExt;
42use std::{error::Error, sync::Arc};
43
44#[cfg(target_arch = "wasm32")]
45use wasm_bindgen::prelude::*;
46use wasm_timer::Instant;
47use winit::application::ApplicationHandler;
48#[cfg(target_arch = "wasm32")]
49use winit::platform::web::EventLoopExtWebSys;
50
51use winit::{
52 event::{ElementState, Event, WindowEvent},
53 event_loop::EventLoop,
54 window::Window,
55};
56
57#[repr(C)]
62pub struct GpuResources {
63 window: Arc<Window>,
64 surface: wgpu::Surface<'static>,
65 surface_config: wgpu::SurfaceConfiguration,
66 pub renderer: Renderer,
67
68 #[cfg(feature = "with-gui")]
69 pub gui: Option<Gui>,
70 _blit_pass: BlitPass, pub redraw_requested: bool, pub gpu: Gpu,
78}
79#[allow(clippy::missing_panics_doc)]
80#[allow(clippy::too_many_lines)]
81#[allow(unused)]
82impl GpuResources {
83 pub fn new(
84 event_loop: &ActiveEventLoop,
85 event_loop_proxy: &EventLoopProxy<CustomEvent>,
86 canvas_id_parsed: Option<&String>,
87 config: &Config,
88 ) -> Self {
89 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
91 backends: supported_backends(),
92 dx12_shader_compiler: wgpu::Dx12Compiler::default(),
93 flags: wgpu::InstanceFlags::default(),
94 gles_minor_version: wgpu::Gles3MinorVersion::Automatic,
95 });
96
97 let window = Viewer::create_window(event_loop, event_loop_proxy, canvas_id_parsed).expect("failed to create initial window");
98
99 let window = Arc::new(window);
100
101 let surface = unsafe { instance.create_surface(window.clone()) }.unwrap();
102
103 cfg_if::cfg_if! {
105 if #[cfg(not(target_arch = "wasm32"))]{
106 let adapters = enumerate_adapters(&instance);
107 info!("Number of possible adapters: {:?}", adapters.len());
108 for (i, adapter) in adapters.iter().enumerate() {
109 info!("Adapter option {:?}: {:?}", i + 1, adapter.get_info());
110 }
111 }
112 }
113 let adapter = get_adapter(&instance, Some(&surface));
114 info!("Selected adapter: {:?}", adapter.get_info());
115
116 let mut desired_features = wgpu::Features::empty();
119 cfg_if::cfg_if! {
120 if #[cfg(not(target_arch = "wasm32"))]{
121 desired_features = desired_features.union(wgpu::Features::TIMESTAMP_QUERY.union(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES));
122 desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_POINT);
123 desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_LINE);
124 }
125 }
126 let mut required_features = adapter.features().intersection(desired_features); required_features = required_features.union(wgpu::Features::DEPTH32FLOAT_STENCIL8);
138
139 let max_limits = adapter.limits();
142 #[allow(unused_mut)]
143 let mut limits_to_request = wgpu::Limits::default();
144 if cfg!(target_arch = "wasm32") {
145 limits_to_request = wgpu::Limits::downlevel_webgl2_defaults();
146 }
147 limits_to_request.max_texture_dimension_1d = max_limits.max_texture_dimension_1d;
148 limits_to_request.max_texture_dimension_2d = max_limits.max_texture_dimension_2d;
149 limits_to_request.max_buffer_size = max_limits.max_buffer_size;
150
151 let mut memory_hints = wgpu::MemoryHints::Performance;
152 if cfg!(target_arch = "wasm32") {
153 memory_hints = wgpu::MemoryHints::MemoryUsage;
156 }
157
158 let (device, queue) = adapter
160 .request_device(
161 &wgpu::DeviceDescriptor {
162 label: None,
163 required_features,
164 required_limits: limits_to_request,
165 memory_hints,
166 },
167 None, )
169 .block_on()
170 .expect("A device and queue could not be created. Maybe there's a driver issue on your machine?");
171 let gpu = Gpu::new(adapter, instance, device, queue);
172
173 let surface_caps = surface.get_capabilities(gpu.adapter());
175 let surface_format = surface_caps
178 .formats
179 .iter()
180 .copied()
181 .find(|f| !f.is_srgb())
182 .unwrap_or(surface_caps.formats[0]);
183
184 let mut size = PhysicalSize::new(1200, 1200);
187 #[cfg(target_arch = "wasm32")]
188 if let Some(canvas_size) = Viewer::get_html_elem_size(&canvas_id_parsed.as_ref().unwrap()) {
189 size = canvas_size.to_physical(window.scale_factor());
190 }
191
192 println!("scale factor: {:?}", window.scale_factor());
193 let surface_config = wgpu::SurfaceConfiguration {
194 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
195 format: surface_format,
196 width: size.width,
197 height: size.height,
198 present_mode: wgpu::PresentMode::AutoNoVsync, alpha_mode: surface_caps.alpha_modes[0],
201 view_formats: vec![],
202 desired_maximum_frame_latency: 2,
203 };
204 surface.configure(gpu.device(), &surface_config);
205
206 #[cfg(feature = "with-gui")]
207 let gui = if config.core.enable_gui {
208 let mut gui = Gui::new(&window, &gpu, surface_format);
209 gui.hidden = config.core.gui_start_hidden;
210 Some(gui)
211 } else {
212 None
213 };
214
215 let renderer = Renderer::new(&gpu, &config.render, Some(surface_format));
216 let blit_pass = BlitPass::new(&gpu, &surface_format);
217
218 Self {
219 window,
220 surface,
221 surface_config,
222 gpu,
223 renderer,
224 #[cfg(feature = "with-gui")]
225 gui,
226 _blit_pass: blit_pass,
227 redraw_requested: false,
228 }
229 }
230 pub fn request_redraw(&mut self) {
231 if self.redraw_requested {
232 debug!("Redraw was already requested, ignoring.");
233 } else {
234 self.window.request_redraw();
235 self.redraw_requested = true;
236 }
237 }
238}
239
240#[derive(Debug)]
242#[repr(C)]
243pub struct Runner {
244 event_loop: Option<EventLoop<CustomEvent>>, event_loop_proxy: EventLoopProxy<CustomEvent>,
246 pub autostart: bool, pub is_running: bool,
248 pub do_render: bool,
249 pub first_time: bool,
250 pub did_warmup: bool,
251 frame_is_started: bool, time_init: Instant, time_last_frame: Instant, dt: Duration, }
261#[allow(unused)]
262impl Runner {
263 #[allow(clippy::missing_panics_doc)]
264 #[allow(clippy::new_without_default)]
265 pub fn new(canvas_id: &Option<String>) -> Self {
266 let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
267 let event_loop_proxy: EventLoopProxy<CustomEvent> = event_loop.create_proxy();
268
269 #[cfg(target_arch = "wasm32")]
270 if let Some(ref canvas_id) = canvas_id {
271 Viewer::add_listener_to_canvas_resize(&event_loop, &canvas_id);
272 Viewer::add_listener_to_context(&event_loop, &canvas_id);
273 }
274
275 let time_init = Instant::now();
276 let time_last_frame = Instant::now();
277 Self {
278 event_loop: Some(event_loop),
279 event_loop_proxy,
280 autostart: true,
281 is_running: false,
282 do_render: true,
283 first_time: true,
284 did_warmup: false,
285 frame_is_started: false,
286 time_init,
287 time_last_frame,
288 dt: Duration::ZERO,
289 }
290 }
291 pub fn time_since_init(&self) -> Duration {
292 if self.first_time {
293 Duration::ZERO
294 } else {
295 self.time_init.elapsed()
296 }
297 }
298 pub fn update_dt(&mut self) {
299 if self.first_time {
300 self.dt = Duration::ZERO;
301 } else {
302 self.dt = self.time_last_frame.elapsed();
303 }
304 }
305 pub fn override_dt(&mut self, new_dt: f32) {
306 self.dt = Duration::from_secs_f32(new_dt);
307 }
308 pub fn dt(&self) -> Duration {
309 self.dt
310 }
311}
312
313#[repr(C)]
320pub struct Viewer {
321 pub gpu_res: Option<GpuResources>,
323
324 pub camera: Camera, pub scene: Scene,
327
328 window_size: winit::dpi::PhysicalSize<u32>, canvas_id_parsed: Option<String>,
332
333 pub config: Config,
334 pub runner: Runner,
335 pub plugins: Plugins,
336
337 #[allow(private_interfaces)]
338 pub internal_plugins: InternalPlugins,
339}
340
341impl Viewer {
342 pub fn new(config_path: Option<&str>) -> Self {
343 let config = Config::new(config_path);
344 Self::new_with_config(&config)
345 }
346
347 #[allow(clippy::too_many_lines)]
348 #[allow(clippy::missing_panics_doc)]
349 pub fn new_with_config(config: &Config) -> Self {
350 set_panic_hook();
351
352 if config.core.auto_create_logger {
353 gloss_setup_logger_from_config(config);
354 }
355
356 re_memory::accounting_allocator::set_tracking_callstacks(config.core.enable_memory_profiling_callstacks);
358
359 let canvas_id_parsed = config.core.canvas_id.as_ref().map(|canvas_id| String::from("#") + canvas_id);
360
361 let runner = Runner::new(&canvas_id_parsed);
363
364 let window_size = winit::dpi::PhysicalSize::new(100, 100);
365
366 let mut scene = Scene::new();
367 let camera = Camera::new(GLOSS_CAM_NAME, &mut scene, false); let mut internal_plugins = InternalPlugins::new();
370 internal_plugins.insert_plugin(&SelectorPlugin::new(true));
371
372 Self {
373 gpu_res: None,
374 runner,
375 scene,
376 camera,
377 plugins: Plugins::new(),
378 internal_plugins,
379 canvas_id_parsed: canvas_id_parsed.clone(),
380 config: config.clone(),
381 window_size,
382 }
383 }
384
385 #[cfg(target_arch = "wasm32")]
388 fn add_listener_to_canvas_resize(event_loop: &EventLoop<CustomEvent>, canvas_id: &str) {
389 let event_loop_proxy = event_loop.create_proxy();
395 let canvas_id = String::from(canvas_id); let resize = move || {
399 if let Some(size) = Viewer::get_html_elem_size(&canvas_id) {
400 let event_resize = CustomEvent::Resize(size.width, size.height);
401 event_loop_proxy.send_event(event_resize).ok();
402 }
403 };
404
405 let closure = wasm_bindgen::closure::Closure::wrap(Box::new(move |_: web_sys::Event| {
409 resize();
410 }) as Box<dyn FnMut(_)>);
411 let window = web_sys::window().unwrap();
412
413 window
414 .add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref())
415 .unwrap();
416 closure.forget();
417 }
418
419 #[cfg(target_arch = "wasm32")]
420 fn add_listener_to_context(event_loop: &EventLoop<CustomEvent>, canvas_id: &str) {
421 let canvas_id = String::from(canvas_id); let win = web_sys::window().unwrap();
424 let doc = win.document().unwrap();
425 let element = doc.query_selector(&canvas_id).ok().unwrap().unwrap();
426 let canvas = element.dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
427
428 let event_loop_proxy = event_loop.create_proxy();
430 let context_lost = move || {
431 let event = CustomEvent::ContextLost;
432 info!("SENDING USER EVENT: context loss");
433 event_loop_proxy.send_event(event).ok();
434 };
435
436 let event_loop_proxy = event_loop.create_proxy();
437 let context_restored = move || {
438 let event = CustomEvent::ContextRestored;
439 info!("SENDING USER EVENT: context restored");
440 event_loop_proxy.send_event(event).ok();
441 };
442
443 let closure_lost = wasm_bindgen::closure::Closure::wrap(Box::new(move |_: web_sys::Event| {
444 context_lost();
445 }) as Box<dyn FnMut(_)>);
446 let closure_restored = wasm_bindgen::closure::Closure::wrap(Box::new(move |_: web_sys::Event| {
447 context_restored();
448 }) as Box<dyn FnMut(_)>);
449
450 canvas
453 .add_event_listener_with_callback("webglcontextlost", closure_lost.as_ref().unchecked_ref())
454 .unwrap();
455 canvas
456 .add_event_listener_with_callback("webglcontextrestored", closure_restored.as_ref().unchecked_ref())
457 .unwrap();
458 closure_lost.forget();
459 closure_restored.forget();
460 }
461
462 #[cfg(target_arch = "wasm32")]
465 fn get_html_elem_size(selector: &str) -> Option<winit::dpi::LogicalSize<f32>> {
466 let win = web_sys::window().unwrap();
468 let doc = win.document().unwrap();
469 let element = doc.query_selector(selector).ok()??;
470 let parent_element = element.parent_element()?;
471 let rect = parent_element.get_bounding_client_rect();
472 return Some(winit::dpi::LogicalSize::new(rect.width() as f32, rect.height() as f32));
473 }
474
475 #[cfg(target_arch = "wasm32")]
476 pub fn resize_to_canvas(&self) {
477 if let Some(size) = Self::get_html_elem_size(&self.canvas_id_parsed.as_ref().unwrap()) {
478 warn!("size is {:?}", size);
480 let event_resize = CustomEvent::Resize(size.width, size.height);
481 self.runner.event_loop_proxy.send_event(event_resize).ok();
482 }
483 }
484
485 #[allow(clippy::cast_precision_loss)]
486 fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
487 debug!("resizing new_size is {:?}", new_size);
488 let max_2d_size = self.gpu_res.as_ref().unwrap().gpu.limits().max_texture_dimension_2d;
489 if new_size.width > 16 && new_size.height > 16 && new_size.width < max_2d_size && new_size.height < max_2d_size {
490 let gpu_res = self.gpu_res.as_mut().unwrap();
491 self.window_size = new_size;
493 gpu_res.surface_config.width = new_size.width;
494 gpu_res.surface_config.height = new_size.height;
495 gpu_res.surface.configure(gpu_res.gpu.device(), &gpu_res.surface_config);
496 gpu_res.request_redraw();
497
498 if self.scene.world.has::<Projection>(self.camera.entity).unwrap() {
501 self.camera
502 .set_aspect_ratio(new_size.width as f32 / new_size.height as f32, &mut self.scene);
503 }
504
505 #[cfg(feature = "with-gui")]
507 if let Some(ref mut gui) = gpu_res.gui {
508 gui.resize(new_size.width, new_size.height);
509 }
510 } else {
511 error!("trying to resize to unsuported size of {new_size:?}");
512 }
513 }
515
516 pub fn request_redraw(&mut self) {
519 if let Some(gpu_res) = self.gpu_res.as_mut() {
520 gpu_res.request_redraw();
521 } else {
522 error!("No gpu_res created yet");
523 }
524 }
525
526 #[cfg(not(target_arch = "wasm32"))]
531 pub fn update(&mut self) {
532 self.event_loop_one_iter();
533 let _ = self.render();
534 }
535
536 #[cfg(not(target_arch = "wasm32"))]
537 pub fn update_offscreen_texture(&mut self) {
538 self.event_loop_one_iter();
539 let _ = self.render_to_texture();
540 }
541
542 fn process_custom_resize_events(&mut self, event: &Event<CustomEvent>) -> bool {
545 match event {
546 Event::UserEvent(CustomEvent::Resize(new_width, new_height)) => {
547 debug!("rs: handling resize canvas: {:?}", event);
548 let logical_size = winit::dpi::LogicalSize {
549 width: *new_width,
550 height: *new_height,
551 };
552 let _ = self.gpu_res.as_ref().unwrap().window.request_inner_size(logical_size);
557
558 self.resize(self.gpu_res.as_ref().unwrap().window.inner_size()); true }
567 _ => false, }
569 }
570
571 fn process_custom_context_event(&mut self, event: &Event<CustomEvent>, event_loop: &ActiveEventLoop) -> bool {
572 match event {
573 Event::UserEvent(event) => {
574 match event {
575 CustomEvent::ContextLost => {
576 info!("rs: handling context lost");
577 self.suspend();
578 true }
581 CustomEvent::ContextRestored => {
582 info!("rs: handling context restored");
583 self.resume(event_loop);
584 true }
587 _ => false, }
589 }
590 _ => false, }
592 }
593
594 #[allow(clippy::collapsible_match)]
595 fn process_custom_other_event(&mut self, event: &Event<CustomEvent>, event_loop: &ActiveEventLoop) -> bool {
596 match event {
597 Event::UserEvent(event) => {
598 match event {
599 CustomEvent::ResumeLoop => {
600 info!("rs: handling custom resume loop");
601 self.resume(event_loop);
602 true
603 }
604 CustomEvent::StopLoop => {
605 info!("rs: handling custom stop loop");
606 self.runner.is_running = false;
607 event_loop.exit();
608 true
609 }
610 _ => false, }
612 }
613 _ => false, }
615 }
616
617 #[allow(clippy::too_many_lines)]
620 fn process_window_events(&mut self, event: &WindowEvent, event_loop: &ActiveEventLoop) -> bool {
621 match event {
622 WindowEvent::RedrawRequested => {
623 {
624 let gpu_res = self.gpu_res.as_mut().unwrap();
625 gpu_res.redraw_requested = false;
626 }
627
628 if !self.runner.do_render {
629 return false;
630 }
631
632 debug!("gloss: render");
633
634 if self.runner.frame_is_started {
637 debug!("the frame was already started, we are ignoring this re-render");
638 return true;
639 }
640 self.start_frame();
641
642 match self.render() {
643 Ok(()) => {}
644 Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
646 self.resize(self.gpu_res.as_ref().unwrap().window.inner_size());
647 }
648 Err(wgpu::SurfaceError::OutOfMemory) => {
650 error!("SurfaceError: out of memory");
651 }
653 Err(wgpu::SurfaceError::Timeout) => error!("SurfaceError: timeout"),
655 }
656 debug!("finsihed handing RedrawRequested");
657 true
658 }
659 WindowEvent::Resized(physical_size) => {
660 self.resize(*physical_size);
661 true
662 }
663 WindowEvent::DroppedFile(path_buf) => {
670 info!("Dropped file {:?}", path_buf);
671 let path = path_buf.to_str().unwrap();
672
673 self.render().ok();
676
677 #[allow(unused_mut)]
679 #[cfg(feature = "with-gui")]
680 {
681 let gpu_res = self.gpu_res.as_mut().unwrap();
682 let gui = gpu_res.gui.as_mut().unwrap();
683 if gui.is_hovering() {
684 gui.on_drop(path_buf, &mut self.scene);
685 return true;
686 }
687 }
688
689 let filetype = match path_buf.extension() {
691 Some(extension) => FileType::find_match(extension.to_str().unwrap_or("")),
692 None => FileType::Unknown,
693 };
694 match filetype {
695 FileType::Obj | FileType::Ply => {
696 let builder = builders::build_from_file(path);
697 let name = self.scene.get_unused_name();
698 self.scene.get_or_create_entity(&name).insert_builder(builder);
699 return true;
700 }
701 FileType::Unknown => {
702 info!(
703 "Gloss doesn't know how to handle dropped file {:?}. trying to let plugins handle it",
704 path
705 );
706 }
707 }
708
709 let event = crate::plugin_manager::Event::DroppedFile(RString::from(path));
711 let handled = self.plugins.try_handle_event(&mut self.scene, &mut self.runner, &event);
712
713 if !handled {
714 info!("Neither Gloss nor any of the plugin could load the dropped file {:?}", path);
715 return false;
716 }
717
718 true
719 }
720 WindowEvent::KeyboardInput { event, .. } => {
721 if let PhysicalKey::Code(code) = event.physical_key {
722 if event.state == ElementState::Pressed && !event.repeat {
723 #[allow(clippy::single_match)] match code {
725 KeyCode::KeyH => {
726 #[cfg(feature = "with-gui")]
728 {
729 if let Some(gpu_res) = self.gpu_res.as_mut() {
730 if let Some(gui) = gpu_res.gui.as_mut() {
731 gui.hidden = !gui.hidden;
732 gpu_res.request_redraw();
733 }
734 }
735 }
736 }
737 _ => {}
738 }
739 }
740 }
741 true
742 }
743
744 WindowEvent::CloseRequested {} | WindowEvent::Destroyed {} => {
745 event_loop.exit();
746 true
747 }
748
749 WindowEvent::Occluded(_) => {
750 self.camera.reset_all_touch_presses(&mut self.scene);
751 true
752 }
753 _ => false, }
755 }
756
757 #[allow(clippy::cast_possible_truncation)]
760 fn process_input_events(&mut self, event: &WindowEvent) -> bool {
761 if !self.camera.is_initialized(&self.scene) {
763 return false;
764 }
765
766 let consumed = match event {
767 WindowEvent::MouseInput { button, state, .. } => {
768 if *state == ElementState::Pressed {
769 self.camera.mouse_pressed(button, &mut self.scene);
770 } else {
771 self.camera.mouse_released(&mut self.scene);
772 }
773 true
774 }
775 WindowEvent::MouseWheel { delta, .. } => {
776 self.camera.process_mouse_scroll(delta, &mut self.scene);
777 true
778 }
779 WindowEvent::CursorMoved { position, .. } => {
780 self.camera
781 .process_mouse_move(position.x, position.y, self.window_size.width, self.window_size.height, &mut self.scene);
782 true
783 }
784 WindowEvent::Touch(touch) => {
785 #[allow(clippy::cast_sign_loss)]
786 if touch.phase == TouchPhase::Started {
787 self.camera.touch_pressed(touch, &mut self.scene);
788 }
789 if touch.phase == TouchPhase::Ended || touch.phase == TouchPhase::Cancelled {
790 self.camera.touch_released(touch, &mut self.scene);
791 }
792 if touch.phase == TouchPhase::Moved {
793 self.camera
794 .process_touch_move(touch, self.window_size.width, self.window_size.height, &mut self.scene);
795 }
796 true
797 }
798 _ => false, };
800 let gpu_res = self.gpu_res.as_mut().unwrap();
801
802 if consumed {
803 gpu_res.request_redraw();
804 }
805
806 consumed
807 }
808
809 #[cfg(feature = "with-gui")]
810 fn process_gui_events(&mut self, event: &WindowEvent) -> EventResponse {
811 let gpu_res = self.gpu_res.as_mut().unwrap();
812 if let Some(mut gui) = gpu_res.gui.take() {
815 let response = gui.on_event(&gpu_res.window, event);
816 gpu_res.gui = Some(gui); response
818 } else {
819 EventResponse {
820 repaint: false,
821 consumed: false,
822 }
823 }
824 }
825
826 fn process_all_events(&mut self, event: &Event<CustomEvent>, event_loop: &ActiveEventLoop) {
828 if !self.runner.is_running {
829 if let Event::WindowEvent { ref event, window_id: _ } = event {
832 if event == &WindowEvent::RedrawRequested {
833 let gpu_res = self.gpu_res.as_mut().unwrap();
835 gpu_res.redraw_requested = false;
836 }
837 }
838 if let Event::WindowEvent {
840 event: WindowEvent::Resized(physical_size),
841 window_id: _,
842 } = event
843 {
844 self.resize(*physical_size);
845 }
846
847 return; }
849
850 match event {
856 Event::WindowEvent { ref event, window_id } if *window_id == self.gpu_res.as_ref().unwrap().window.id() => {
857 if self.window_size.height < 16 || self.window_size.width < 16 {
862 warn!("Skipping rendering and trying again to resize. Window size is {:?}", self.window_size);
863 #[cfg(target_arch = "wasm32")]
866 self.resize_to_canvas();
867 let gpu_res = self.gpu_res.as_mut().unwrap();
868 gpu_res.request_redraw();
869 return;
870 }
871
872 self.process_window_events(event, event_loop); cfg_if::cfg_if! {
874 if #[cfg(feature = "with-gui")]{
875 let res = self.process_gui_events(event);
876 if res.consumed {
878 self.gpu_res.as_mut().unwrap().request_redraw();
879 } else {
880 self.process_input_events(event);
881 }
882 if let Some(ref gui) = self.gpu_res.as_mut().unwrap().gui {
885 if gui.wants_pointer_input() {
886 self.camera.mouse_released(&mut self.scene);
887 }
888 }
889 }else{ self.process_input_events(event);
891 }
892 }
893 }
894 _ => {}
895 }
896 }
897
898 #[allow(unused)]
901 #[cfg(not(target_arch = "wasm32"))] fn event_loop_one_iter(&mut self) {
904 use winit::platform::pump_events::EventLoopExtPumpEvents;
910 let mut event_loop = self.runner.event_loop.take().unwrap();
911 self.runner.is_running = true;
912 self.runner.do_render = false; let timeout = Some(Duration::ZERO);
915 event_loop.pump_app_events(timeout, self);
916
917 self.runner.is_running = false;
918
919 self.runner.event_loop = Some(event_loop);
921 }
922
923 pub fn start_frame(&mut self) -> Duration {
926 #[cfg(not(target_arch = "wasm32"))]
928 {
929 if self.gpu_res.is_none() {
930 self.event_loop_one_iter(); }
932 assert!(self.gpu_res.is_some(), "GPU Res has not been created!");
933 }
934 if !self.runner.did_warmup {
936 self.runner.did_warmup = true; self.warmup();
939 self.warmup(); }
941
942 self.runner.update_dt();
943 debug!("after update dt it is {:?}", self.runner.dt());
944 self.runner.time_last_frame = Instant::now();
945
946 self.runner.frame_is_started = true;
947
948 self.runner.dt
949 }
950
951 pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
956 if !self.runner.frame_is_started {
957 error!("The frame was not started so this might contain stale dt. Please use viewer.start_frame() before doing a v.render()");
958 }
959
960 if self.runner.first_time {
964 self.runner.time_init = Instant::now();
965 }
966
967 let gpu_res = self.gpu_res.as_mut().unwrap();
968 self.plugins.run_logic_systems(gpu_res, &mut self.scene, &mut self.runner, true);
969
970 let output = gpu_res.surface.get_current_texture()?;
972 let out_view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
973 let out_width = output.texture.width();
974 let out_height = output.texture.height();
975
976 let dt = self.runner.dt();
978
979 self.camera.on_window_resize(out_width, out_height, &mut self.scene);
980
981 gpu_res
984 .renderer
985 .render_to_view(&out_view, &gpu_res.gpu, &mut self.camera, &mut self.scene, &mut self.config, dt);
986
987 #[cfg(feature = "with-gui")]
990 if let Some(ref mut gui) = gpu_res.gui {
991 gui.render(
992 &gpu_res.window,
993 &gpu_res.gpu,
994 &gpu_res.renderer,
995 &self.runner,
996 &mut self.scene,
997 &self.plugins,
998 &mut self.config,
999 &out_view,
1000 );
1001 }
1002
1003 self.internal_plugins.run_gpu_systems(gpu_res, &mut self.scene, &mut self.runner, true);
1004 self.scene.get_current_cam().unwrap().clear_click(&mut self.scene);
1006
1007 output.present();
1009
1010 self.runner.first_time = false;
1011 self.runner.frame_is_started = false;
1012
1013 Ok(())
1014 }
1015
1016 pub fn render_to_texture(&mut self) -> Result<(), wgpu::SurfaceError> {
1021 if !self.runner.frame_is_started {
1022 error!("The frame was not started so this might contain stale dt. Please use viewer.start_frame() before doing a v.render_to_texture()");
1023 }
1024
1025 if self.runner.first_time {
1029 self.runner.time_init = Instant::now();
1030 }
1031
1032 let gpu_res = self.gpu_res.as_mut().unwrap();
1033 self.plugins.run_logic_systems(gpu_res, &mut self.scene, &mut self.runner, true);
1034
1035 let out_tex = gpu_res.renderer.rendered_tex();
1038 let out_width = out_tex.width();
1039 let out_height = out_tex.height();
1040
1041 let dt = self.runner.dt();
1043
1044 self.camera.on_window_resize(out_width, out_height, &mut self.scene);
1045
1046 gpu_res
1049 .renderer
1050 .render_to_texture(&gpu_res.gpu, &mut self.camera, &mut self.scene, &mut self.config, dt);
1051
1052 #[cfg(feature = "with-gui")]
1055 if let Some(ref mut gui) = gpu_res.gui {
1056 let out_view = &gpu_res.renderer.rendered_tex().view;
1057 gui.render(
1058 &gpu_res.window,
1059 &gpu_res.gpu,
1060 &gpu_res.renderer,
1061 &self.runner,
1062 &mut self.scene,
1063 &self.plugins,
1064 &mut self.config,
1065 out_view,
1066 );
1067 }
1068
1069 self.internal_plugins.run_gpu_systems(gpu_res, &mut self.scene, &mut self.runner, true);
1070 self.scene.get_current_cam().unwrap().clear_click(&mut self.scene);
1072
1073 self.runner.first_time = false;
1074 self.runner.frame_is_started = false;
1075
1076 Ok(())
1077 }
1078
1079 #[cfg(not(target_arch = "wasm32"))]
1108 pub fn run(&mut self) {
1109 let event_loop = self.runner.event_loop.take().unwrap();
1110 self.runner.is_running = self.runner.autostart;
1111 let _ = event_loop.run_app(self);
1112 }
1113
1114 #[cfg(target_arch = "wasm32")]
1115 pub fn run(mut self) {
1116 let event_loop = self.runner.event_loop.take().unwrap();
1117 self.runner.is_running = self.runner.autostart;
1118 let _ = event_loop.spawn_app(self);
1119 }
1120
1121 #[allow(clippy::missing_panics_doc)]
1124 #[allow(unreachable_code)] pub fn run_static_ref(&'static mut self) {
1126 let event_loop = self.runner.event_loop.take().unwrap();
1127 self.runner.is_running = self.runner.autostart;
1128
1129 cfg_if::cfg_if! {
1131 if #[cfg(not(target_arch = "wasm32"))]{
1132 let _ = event_loop.run_app(self);
1133 }else{
1134 let _ = event_loop.spawn_app(self);
1135 }
1136 }
1137 }
1138
1139 #[allow(clippy::missing_panics_doc)]
1143 pub fn recreate_event_loop(&mut self) {
1144 self.stop_event_loop();
1145 self.suspend(); let runner = Runner::new(&self.canvas_id_parsed);
1147 self.runner = runner;
1148 let event_stop = CustomEvent::ResumeLoop;
1150 self.runner.event_loop_proxy.send_event(event_stop).ok();
1151 }
1152
1153 pub fn stop_event_loop(&self) {
1154 let event_stop = CustomEvent::StopLoop;
1155 self.runner.event_loop_proxy.send_event(event_stop).ok();
1156 }
1157
1158 fn resume(&mut self, event_loop: &ActiveEventLoop) {
1159 self.runner.is_running = self.runner.autostart;
1161 if self.gpu_res.is_none() {
1162 self.gpu_res = Some(GpuResources::new(
1163 event_loop,
1164 &self.runner.event_loop_proxy,
1165 self.canvas_id_parsed.as_ref(),
1166 &self.config,
1167 ));
1168 self.scene.world.set_trackers_changed(); self.gpu_res.as_mut().unwrap().request_redraw();
1175 }
1176 #[cfg(target_arch = "wasm32")]
1177 self.resize_to_canvas()
1178 }
1179
1180 pub fn suspend(&mut self) {
1181 info!("RS: suspend");
1184 self.runner.is_running = false;
1185 self.scene.remove_all_gpu_components();
1186 self.gpu_res.take();
1187 self.camera.reset_all_touch_presses(&mut self.scene);
1188 }
1189
1190 pub fn warmup(&mut self) {
1195 debug!("Starting warmup");
1196 self.start_frame();
1198 self.run_manual_plugins(); let _ = self.render();
1204 self.reset_for_first_time();
1205 debug!("finished warmup");
1206 }
1207
1208 pub fn reset_for_first_time(&mut self) {
1209 self.runner.first_time = true;
1210 }
1211
1212 pub fn add_logic_system(&mut self, sys: LogicSystem) {
1213 self.plugins.logic_systems.push(Tuple2(sys, SystemMetadata::default()));
1214 }
1215
1216 #[cfg(feature = "with-gui")]
1217 pub fn add_gui_system(&mut self, sys: GuiSystem) {
1218 self.plugins.gui_systems.push(Tuple2(sys, SystemMetadata::default()));
1219 }
1220
1221 #[allow(clippy::missing_panics_doc)]
1222 pub fn run_manual_plugins(&mut self) {
1223 {
1224 let gpu_res = self.gpu_res.as_mut().unwrap();
1226 self.plugins.run_logic_systems(gpu_res, &mut self.scene, &mut self.runner, false);
1227 }
1229 }
1230
1231 pub fn insert_plugin<T: Plugin + 'static>(&mut self, plugin: &T) {
1232 self.plugins.insert_plugin(plugin);
1233 }
1234
1235 pub fn wait_gpu_finish(&self) -> wgpu::MaintainResult {
1244 self.gpu_res.as_ref().unwrap().gpu.device().poll(wgpu::Maintain::Wait)
1245 }
1246
1247 fn create_window(
1250 event_loop: &ActiveEventLoop,
1251 _event_loop_proxy: &EventLoopProxy<CustomEvent>,
1252 _canvas_id: Option<&String>,
1253 ) -> Result<Window, Box<dyn Error>> {
1254 #[allow(unused_mut)]
1257 let mut window_attributes = Window::default_attributes()
1258 .with_title("Gloss")
1259 .with_inner_size(PhysicalSize::new(1600, 1200))
1260 .with_maximized(true);
1261
1262 #[cfg(target_arch = "wasm32")]
1264 {
1265 use winit::platform::web::WindowAttributesExtWebSys;
1266 let window = web_sys::window().unwrap();
1267 let document = window.document().unwrap();
1268 let canvas = document
1269 .query_selector(&_canvas_id.as_ref().unwrap())
1270 .expect("Cannot query for canvas element.");
1271 if let Some(canvas) = canvas {
1272 let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok();
1273
1274 window_attributes = window_attributes.with_canvas(canvas).with_prevent_default(false).with_append(false)
1276 } else {
1277 panic!("Cannot find element: {:?}.", _canvas_id.as_ref().unwrap());
1278 }
1279 }
1280
1281 let window = event_loop.create_window(window_attributes)?;
1282
1283 Ok(window)
1284 }
1285
1286 pub fn override_dt(&mut self, new_dt: f32) {
1287 self.runner.override_dt(new_dt);
1288 }
1289
1290 pub fn get_final_tex(&self) -> &Texture {
1291 let tex = self.gpu_res.as_ref().unwrap().renderer.rendered_tex();
1292 tex
1293 }
1294
1295 pub fn get_final_depth(&self) -> &Texture {
1296 let depth = self.gpu_res.as_ref().unwrap().renderer.depth_buffer();
1297 depth
1298 }
1299}
1300
1301impl ApplicationHandler<CustomEvent> for Viewer {
1302 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
1303 self.resume(event_loop);
1304 }
1305
1306 fn user_event(&mut self, event_loop: &ActiveEventLoop, event: CustomEvent) {
1307 self.process_custom_context_event(&Event::UserEvent(event), event_loop);
1308 self.process_custom_resize_events(&Event::UserEvent(event));
1309 self.process_custom_other_event(&Event::UserEvent(event), event_loop);
1310 }
1311 fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
1312 if window_id != self.gpu_res.as_ref().unwrap().window.id() {
1313 return;
1314 }
1315 self.process_all_events(&Event::WindowEvent { window_id, event }, event_loop);
1316 }
1317 #[allow(unused_variables)]
1318 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
1319 #[cfg(not(target_arch = "wasm32"))]
1320 {
1321 if let Some(gpu_res) = self.gpu_res.as_mut() {
1322 gpu_res.request_redraw();
1323 }
1324 }
1325 }
1326
1327 fn suspended(&mut self, _event_loop: &ActiveEventLoop) {
1328 debug!("Handling Suspended event");
1329 self.suspend();
1330 }
1331
1332 fn exiting(&mut self, event_loop: &ActiveEventLoop) {
1333 self.runner.is_running = false;
1334 event_loop.exit();
1335 }
1336}
1337
1338pub fn supported_backends() -> wgpu::Backends {
1341 if cfg!(target_arch = "wasm32") {
1342 wgpu::Backends::GL
1344 } else {
1345 wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::VULKAN | wgpu::Backends::METAL)
1347 }
1348}
1349
1350pub fn get_adapter(instance: &wgpu::Instance, surface: Option<&wgpu::Surface>) -> wgpu::Adapter {
1353 #[cfg(not(target_arch = "wasm32"))]
1354 fn remove_from_vec(vec: &mut Vec<wgpu::Adapter>, idx_str: &str) -> wgpu::Adapter {
1355 let idx = idx_str.split(',').next().unwrap().parse::<usize>().unwrap(); assert!(
1358 (0..vec.len()).contains(&idx),
1359 "Tried to index device with idx {} but we only have detected {} devices",
1360 idx,
1361 vec.len()
1362 );
1363
1364 info!("Selecting adapter with idx {}", idx);
1365 vec.remove(idx)
1366 }
1367
1368 cfg_if::cfg_if! {
1369 if #[cfg(target_arch = "wasm32")]{
1370 instance
1371 .request_adapter(&wgpu::RequestAdapterOptions {
1372 power_preference: wgpu::PowerPreference::HighPerformance,
1373 compatible_surface: surface,
1374 force_fallback_adapter: false,
1375 })
1376 .block_on()
1377 .expect("An adapter could not be found. Maybe there's a driver issue on your machine?")
1378 }else {
1379 let mut adapters = enumerate_adapters(instance);
1380
1381 let wgpu_dev_id = std::env::var("WGPU_VISIBLE_DEVICES");
1382 let cuda_dev_id = std::env::var("CUDA_VISIBLE_DEVICES");
1383 match wgpu_dev_id {
1384 Ok(idx) => remove_from_vec(&mut adapters, &idx),
1385 Err(_) => match cuda_dev_id {
1386 Ok(idx) => remove_from_vec(&mut adapters, &idx),
1387 Err(_) => instance
1388 .request_adapter(&wgpu::RequestAdapterOptions {
1389 power_preference: wgpu::PowerPreference::HighPerformance,
1390 compatible_surface: surface,
1391 force_fallback_adapter: false,
1392 })
1393 .block_on()
1394 .expect("An adapter could not be found. Maybe there's a driver issue on your machine?"),
1395 },
1396 }
1397 }
1398 }
1399}
1400
1401#[cfg(not(target_arch = "wasm32"))]
1405pub fn enumerate_adapters(instance: &wgpu::Instance) -> Vec<wgpu::Adapter> {
1406 let mut adapters = instance.enumerate_adapters(wgpu::Backends::all());
1407
1408 adapters.sort_by_key(|x| (x.get_info().device_type as i32 - wgpu::DeviceType::DiscreteGpu as i32).abs());
1410
1411 adapters
1412}
1413
1414#[derive(Debug, Clone, Copy)]
1416pub enum CustomEvent {
1417 Resize(f32, f32),
1418 ContextLost,
1419 ContextRestored,
1420 ResumeLoop,
1421 StopLoop,
1422}