#![allow(unused)] use super::configs::WindowConfig; use super::osspecific::osspecific::OsSpecific; use super::vulkancore::VulkanCore; #[cfg(feature = "sound")] use audio::SoundHandler; use crate::prelude::*; use anyhow::Result; use presentation::{input::eventsystem::Event, prelude::*}; use std::collections::HashMap; use std::env::set_var; use std::mem::{self, swap}; use std::ops::{Deref, DerefMut}; use std::path::Path; use std::rc::Rc; use std::sync::{Arc, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::time::{Duration, Instant}; pub trait ContextObject { fn name(&self) -> &str; fn update(&mut self) -> Result<()>; fn event(&mut self, event: Event) -> Result<()>; } pub struct Context { core: VulkanCore, pub(crate) presentation: PresentationCore, render_core: Arc>>, #[cfg(feature = "sound")] sound_handler: Mutex, os_specific: OsSpecific, application_start_time: Instant, context_object: Arc>>>, fallback: Mutex Result<()> + Send + Sync>>>, push_events: Mutex Result<()> + Send + Sync>>>, // queue timer last_check: Mutex, } impl Context { pub fn new() -> ContextBuilder { ContextBuilder::default() } pub fn set_context_object(&self, context_object: Option) where C: ContextObject + Send + Sync + 'static, { let tmp = self.context_object.clone(); self.push_event(move || { *tmp.write().unwrap() = context_object.map(|c| Box::new(c) as Box); Ok(()) }) } pub fn window_config(&self) -> WindowConfig<'_> { match self.presentation.backend() { PresentationBackend::Window(wsi) => WindowConfig::new(wsi), PresentationBackend::OpenXR(_xri) => { panic!("OpenXR backend has no window config") } PresentationBackend::OpenVR(_vri) => { panic!("OpenVR backend has no window config") } } } pub fn push_event(&self, event: impl FnOnce() -> Result<()> + 'static + Send + Sync) { self.push_events.lock().unwrap().push(Box::new(event)); } #[cfg(feature = "sound")] pub fn sound(&self) -> MutexGuard<'_, SoundHandler> { self.sound_handler.lock().unwrap() } pub fn run(&self) -> Result<()> { 'running: loop { match self.presentation.poll_events( |event| { if let Some(ctx_obj) = &mut *self.context_object.write().unwrap() { ctx_obj.event(event)?; } Ok(()) }, { let render_core = self.render_core.clone(); move |w, h| render_core.write().unwrap().resize(w, h) }, ) { Ok(res) => { if !res { break 'running; } } Err(err) => { if let Some(fallback) = self.fallback.lock().unwrap().as_ref() { (fallback)(err)?; } } } if let Err(err) = self.update() { if let Some(fallback) = &self.fallback.lock().unwrap().as_ref() { (fallback)(err)?; } } if !self.render_core_mut().next_frame()? { break 'running; } } *self.context_object.write().unwrap() = None; self.render_core_mut().clear_post_processing_routines(); Ok(()) } pub fn render_core(&self) -> impl Deref> + '_ { self.render_core.read().unwrap() } pub fn render_core_mut(&self) -> impl DerefMut> + '_ { self.render_core.write().unwrap() } pub fn set_fallback(&self, fallback: F) where F: Fn(anyhow::Error) -> Result<()> + 'static + Send + Sync, { *self.fallback.lock().unwrap() = Some(Box::new(fallback)); } pub fn close(&self) -> Result<()> { Ok(self.presentation.event_system().quit()?) } pub fn device(&self) -> &Arc { &self.core.device() } pub fn queue(&self) -> &Arc> { self.core.queue() } pub fn time(&self) -> Duration { self.application_start_time.elapsed() } pub fn controllers(&self) -> RwLockReadGuard<'_, Vec>>> { self.presentation.event_system().controllers() } pub fn active_controller(&self) -> Result>>> { Ok(self.presentation.event_system().active_controller()?) } pub fn set_active_controller(&self, controller: &Arc>) { self.presentation .event_system() .set_active_controller(controller) } } impl std::fmt::Debug for Context { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Context {{ TODO }}") } } impl Context { #[inline] fn update(&self) -> Result<()> { if let Some(ctx_obj) = &mut *self.context_object.write().unwrap() { if let Err(err) = ctx_obj.update() { return Err(err); } } let mut events = Vec::new(); { let mut push_events_lock = self.push_events.lock().unwrap(); mem::swap(&mut events, &mut push_events_lock); } for event in events { event()?; } let one_second = Duration::from_secs(1); let mut last_check = self.last_check.lock().unwrap(); if (self.time() - *last_check) > one_second { *last_check += one_second; #[cfg(feature = "sound")] { self.sound().check_clear_queue()?; } } Ok(()) } } impl ContextInterface for Context { fn device(&self) -> &Arc { self.device() } fn queue(&self) -> &Arc> { self.queue() } fn format(&self) -> VkFormat { self.render_core().format() } fn image_layout(&self) -> VkImageLayout { self.render_core().image_layout() } fn image_count(&self) -> usize { self.render_core().image_count() } fn images(&self) -> TargetMode>> { self.render_core().images() } fn width(&self) -> u32 { self.render_core().width() } fn height(&self) -> u32 { self.render_core().height() } #[cfg(feature = "sound")] fn sound_handler(&self) -> MutexGuard<'_, SoundHandler> { self.sound() } } pub struct ContextBuilder { #[cfg(feature = "sound")] volume_info: HashMap, #[cfg(any(feature = "openvr", feature = "openxr"))] vr_mode: Option, #[cfg(feature = "openxr")] openxr_runtime_json: Option, enable_backtrace: bool, // app info app_info: ApplicationInfo, // window information window_create_info: WindowCreateInfo, // os specifics os_specific_config: OsSpecificConfig, // vulkan core settings device_features: DeviceFeatures, sample_count: VkSampleCountFlags, enable_raytracing: bool, render_core_create_info: RenderCoreCreateInfo, // vulkan debug extension selection vulkan_debug_info: VulkanDebugInfo, // input settings enable_mouse: bool, enable_keyboard: bool, enable_controller: bool, controller_deadzones: ControllerDeadzones, } impl Default for ContextBuilder { fn default() -> Self { ContextBuilder { #[cfg(feature = "sound")] volume_info: HashMap::new(), #[cfg(any(feature = "openvr", feature = "openxr"))] vr_mode: None, #[cfg(feature = "openxr")] openxr_runtime_json: None, enable_backtrace: false, // app info app_info: ApplicationInfo { application_name: "not set".to_string(), application_version: 0, engine_name: "not set".to_string(), engine_version: 0, }, // window information window_create_info: WindowCreateInfo { title: "Vulkan Application".to_string(), width: 800, height: 600, fullscreen: false, requested_display: None, }, // os specifics os_specific_config: OsSpecificConfig::default(), // vulkan core settings device_features: DeviceFeatures::default(), sample_count: VK_SAMPLE_COUNT_1_BIT, enable_raytracing: false, render_core_create_info: RenderCoreCreateInfo { format: VK_FORMAT_R8G8B8A8_UNORM, usage: 0.into(), vsync: false, }, // vulkan debug extension selection vulkan_debug_info: VulkanDebugInfo::default(), // input settings enable_mouse: false, enable_keyboard: false, enable_controller: false, controller_deadzones: ControllerDeadzones::default(), } } } impl ContextBuilder { #[cfg(feature = "sound")] pub fn set_volume_info(mut self, volume_info: HashMap) -> Self { self.volume_info = volume_info; self } #[cfg(any(feature = "openvr", feature = "openxr"))] pub fn set_vr_mode(mut self, vr_mode: VRMode) -> Self { self.vr_mode = Some(vr_mode); self } #[cfg(feature = "openxr")] pub fn set_openxr_json(mut self, openxr_json_path: &str) -> Self { self.openxr_runtime_json = Some(openxr_json_path.to_string()); self } pub fn set_app_info(mut self, app_info: ApplicationInfo) -> Self { self.app_info = app_info; self } pub fn set_window_info(mut self, window_info: WindowCreateInfo) -> Self { self.window_create_info = window_info; self } pub fn set_os_specific_info(mut self, os_specific: OsSpecificConfig) -> Self { self.os_specific_config = os_specific; self } pub fn set_device_features(mut self, device_features: DeviceFeatures) -> Self { self.device_features = device_features; self } pub fn set_sample_count(mut self, sample_count: VkSampleCountFlags) -> Self { self.sample_count = sample_count; self } pub fn enable_ray_tracing(mut self) -> Self { self.enable_raytracing = true; self } pub fn set_render_core_info( mut self, format: VkFormat, usage: impl Into, vsync: bool, ) -> Self { self.render_core_create_info = RenderCoreCreateInfo { format, usage: usage.into(), vsync, }; self } pub fn enable_backtrace(mut self) -> Self { self.enable_backtrace = true; self } pub fn set_vulkan_debug_info(mut self, vulkan_debug_info: VulkanDebugInfo) -> Self { self.vulkan_debug_info = vulkan_debug_info; self } pub fn enable_mouse(mut self) -> Self { self.enable_mouse = true; self } pub fn enable_keyboard(mut self) -> Self { self.enable_keyboard = true; self } pub fn enable_controller(mut self) -> Self { self.enable_controller = true; self } pub fn set_controller_deadzones(mut self, controller_deadzones: ControllerDeadzones) -> Self { self.controller_deadzones = controller_deadzones; self } pub fn build(self, create_scene: F) -> Result> where SCENE: TScene + 'static, F: FnOnce( &Arc, &Arc>, (f32, f32), &TargetMode>>, ) -> Result, { if self.enable_backtrace { // set environment variable for Rust-debug-trace set_var("RUST_BACKTRACE", "1"); } #[cfg(feature = "openxr")] self.use_openxr_json(); let vr_mode = self.get_vr_mode(); let presentation = PresentationCore::new(vr_mode, &self.window_create_info, self.app_info.clone())?; presentation .event_system() .set_controller_axis_enable_deadzone(self.controller_deadzones.axis_enable_deadzone); presentation .event_system() .set_controller_axis_disable_deadzone(self.controller_deadzones.axis_disable_deadzone); presentation .event_system() .set_controller_trigger_enable_deadzone( self.controller_deadzones.trigger_enable_deadzone, ); presentation .event_system() .set_controller_trigger_disable_deadzone( self.controller_deadzones.trigger_disable_deadzone, ); // vulkan core objects (VkInstance, VkDevice, ...) let core = VulkanCore::new( &presentation, &self.vulkan_debug_info, &self.app_info, self.device_features, )?; let os_specific = OsSpecific::new(&self.os_specific_config); let (render_core, _target_mode) = create_render_core( &presentation, core.device(), core.queue(), self.render_core_create_info, create_scene, )?; if self.enable_mouse { presentation.event_system().enable_mouse(); } if self.enable_keyboard { presentation.event_system().enable_keyboard(); } if self.enable_controller { presentation.event_system().enable_controller(); } Ok(Arc::new(Context { core, presentation, render_core: Arc::new(RwLock::new(render_core)), #[cfg(feature = "sound")] sound_handler: Mutex::new(self.create_sound_handler()?), os_specific, application_start_time: Instant::now(), context_object: Arc::new(RwLock::new(None)), fallback: Mutex::new(None), push_events: Mutex::new(Vec::new()), last_check: Mutex::new(Duration::from_secs(0)), })) } #[cfg(feature = "openxr")] fn use_openxr_json(&self) { if let Some(openxr_json) = &self.openxr_runtime_json { // set environment variable for OpenXR set_var("XR_RUNTIME_JSON", openxr_json); } } fn get_vr_mode(&self) -> Option { #[cfg(any(feature = "openvr", feature = "openxr"))] // if we requested a VR mode, check if it is available match self.vr_mode { Some(vr_mode) => { let available_vr_modes = PresentationCore::enabled_vr_modes(); // if requested VR mode is enabled, use it if available_vr_modes.contains(&vr_mode) { return Some(vr_mode); } // fallback to the first available else if !available_vr_modes.is_empty() { let mode = available_vr_modes[0]; println!( "Requested VRMode ({:?}) is not available, using {:?} instead.", vr_mode, mode ); return Some(mode); } // use default desktop, as last resort else { println!("No VRMode present, fallback to Window"); return None; } } None => { return None; } } #[cfg(not(any(feature = "openvr", feature = "openxr")))] None } #[cfg(feature = "sound")] fn create_sound_handler(&self) -> Result { SoundHandler::new(self.volume_info.clone()) } }