#![allow(unused)] use super::configs::WindowConfig; use super::osspecific::osspecific::OsSpecific; use super::vulkancore::VulkanCore; #[cfg(feature = "sound")] use audio::SoundHandler; use ecs::World; 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 struct Context { core: VulkanCore, pub(crate) presentation: PresentationCore, render_core: Arc>, #[cfg(feature = "sound")] sound_handler: SoundHandler, os_specific: OsSpecific, fallback: Option Result<()> + Send + Sync>>, } impl Context { pub fn new() -> ContextBuilder { ContextBuilder::default() } 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") } } } #[cfg(feature = "sound")] pub fn sound(&mut self) -> &mut SoundHandler { &mut self.sound_handler } pub fn next_frame(&mut self, world: &mut World, mut f: F) -> Result where C: Send + Sync + 'static, F: FnMut(&mut World, &mut C, Event<'_>) -> Result<()> + Send + Sync + 'static, { let render_core = self.render_core.clone(); let consumer = world.resources.get_mut_unchecked::(); let w = unsafe { remove_life_time_mut(world) }; match self.presentation.poll_events( |event| f(w, consumer, event), |w, h| render_core.write().unwrap().resize(world, w, h), ) { Ok(res) => { if !res { self.device().wait_idle()?; return Ok(false); } } Err(err) => { if let Some(fallback) = &self.fallback { (fallback)(err)?; } } } if !self.render_core_mut().next_frame(world)? { return Ok(false); } Ok(true) } 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(&mut self, fallback: F) where F: Fn(anyhow::Error) -> Result<()> + 'static + Send + Sync, { self.fallback = 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 controllers(&self) -> impl Iterator { self.presentation.event_system().controllers() } pub fn joysticks(&self) -> impl Iterator { self.presentation.event_system().joysticks() } } impl std::fmt::Debug for Context { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Context {{ TODO }}") } } 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(&mut self) -> &mut 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) -> Result { if self.enable_backtrace { // set environment variable for Rust-debug-trace unsafe { set_var("RUST_BACKTRACE", "1") }; } #[cfg(feature = "openxr")] self.use_openxr_json(); let vr_mode = self.get_vr_mode(); let mut presentation = PresentationCore::new(vr_mode, &self.window_create_info, self.app_info.clone())?; presentation .event_system_mut() .set_controller_axis_enable_deadzone(self.controller_deadzones.axis_enable_deadzone); presentation .event_system_mut() .set_controller_axis_disable_deadzone(self.controller_deadzones.axis_disable_deadzone); presentation .event_system_mut() .set_controller_trigger_enable_deadzone( self.controller_deadzones.trigger_enable_deadzone, ); presentation .event_system_mut() .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, )?; if self.enable_mouse { presentation.event_system_mut().enable_mouse(); } if self.enable_keyboard { presentation.event_system_mut().enable_keyboard(); } if self.enable_controller { presentation.event_system_mut().enable_controller(); } Ok(Context { core, presentation, render_core: Arc::new(RwLock::new(render_core)), #[cfg(feature = "sound")] sound_handler: self.create_sound_handler()?, os_specific, fallback: None, }) } #[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 return self .vr_mode .map(|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) { 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 ); Some(mode) } // use default desktop, as last resort else { println!("No VRMode present, fallback to Window"); None } }) .flatten(); #[cfg(not(any(feature = "openvr", feature = "openxr")))] None } #[cfg(feature = "sound")] fn create_sound_handler(&self) -> Result { SoundHandler::new(self.volume_info.clone()) } }