engine/context/src/core/context.rs

517 lines
14 KiB
Rust
Raw Normal View History

2024-08-23 11:22:09 +00:00
#![allow(unused)]
use super::configs::WindowConfig;
use super::osspecific::osspecific::OsSpecific;
use super::vulkancore::VulkanCore;
#[cfg(feature = "sound")]
use audio::SoundHandler;
use ecs::World;
2024-08-23 11:22:09 +00:00
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<RwLock<Box<dyn RenderCore>>>,
#[cfg(feature = "sound")]
sound_handler: Mutex<SoundHandler>,
os_specific: OsSpecific,
fallback: Option<Box<dyn Fn(anyhow::Error) -> Result<()> + Send + Sync>>,
2024-08-23 11:22:09 +00:00
}
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(&self) -> MutexGuard<'_, SoundHandler> {
self.sound_handler.lock().unwrap()
}
pub fn run<C>(&self, world: &mut World) -> Result<()> {
2024-08-23 11:22:09 +00:00
'running: loop {
let render_core = self.render_core.clone();
2024-08-23 11:22:09 +00:00
match self.presentation.poll_events(
|event| {
// TODO
// if let Some(ctx_obj) = world.resources.get_mut_opt::<C>() {
// ctx_obj.event(event)?;
// }
2024-08-23 11:22:09 +00:00
Ok(())
},
|w, h| render_core.write().unwrap().resize(world, w, h),
2024-08-23 11:22:09 +00:00
) {
Ok(res) => {
if !res {
break 'running;
}
}
Err(err) => {
if let Some(fallback) = &self.fallback {
2024-08-23 11:22:09 +00:00
(fallback)(err)?;
}
}
}
if !self.render_core_mut().next_frame(world)? {
2024-08-23 11:22:09 +00:00
break 'running;
}
}
self.render_core_mut().clear_post_processing_routines();
Ok(())
}
pub fn render_core(&self) -> impl Deref<Target = Box<dyn RenderCore>> + '_ {
self.render_core.read().unwrap()
}
pub fn render_core_mut(&self) -> impl DerefMut<Target = Box<dyn RenderCore>> + '_ {
self.render_core.write().unwrap()
}
pub fn set_fallback<F>(&mut self, fallback: F)
2024-08-23 11:22:09 +00:00
where
F: Fn(anyhow::Error) -> Result<()> + 'static + Send + Sync,
{
self.fallback = Some(Box::new(fallback));
2024-08-23 11:22:09 +00:00
}
pub fn close(&self) -> Result<()> {
Ok(self.presentation.event_system().quit()?)
}
pub fn device(&self) -> &Arc<Device> {
&self.core.device()
}
pub fn queue(&self) -> &Arc<Mutex<Queue>> {
self.core.queue()
}
pub fn controllers(&self) -> RwLockReadGuard<'_, Vec<Arc<RwLock<Controller>>>> {
self.presentation.event_system().controllers()
}
pub fn active_controller(&self) -> Result<Option<Arc<RwLock<Controller>>>> {
Ok(self.presentation.event_system().active_controller()?)
}
pub fn set_active_controller(&self, controller: &Arc<RwLock<Controller>>) {
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 ContextInterface for Context {
fn device(&self) -> &Arc<Device> {
self.device()
}
fn queue(&self) -> &Arc<Mutex<Queue>> {
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<Vec<Arc<Image>>> {
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<String, f32>,
#[cfg(any(feature = "openvr", feature = "openxr"))]
vr_mode: Option<VRMode>,
#[cfg(feature = "openxr")]
openxr_runtime_json: Option<String>,
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<String, f32>) -> 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<VkImageUsageFlagBits>,
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<S: TScene + 'static>(self) -> Result<Context> {
2024-08-23 11:22:09 +00:00
if self.enable_backtrace {
// set environment variable for Rust-debug-trace
unsafe { set_var("RUST_BACKTRACE", "1") };
2024-08-23 11:22:09 +00:00
}
#[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::<S>(
2024-08-23 11:22:09 +00:00
&presentation,
core.device(),
core.queue(),
self.render_core_create_info,
)?;
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(Context {
2024-08-23 11:22:09 +00:00
core,
presentation,
render_core: Arc::new(RwLock::new(render_core)),
#[cfg(feature = "sound")]
sound_handler: Mutex::new(self.create_sound_handler()?),
os_specific,
fallback: None,
})
2024-08-23 11:22:09 +00:00
}
#[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<VRMode> {
#[cfg(any(feature = "openvr", feature = "openxr"))]
// if we requested a VR mode, check if it is available
return self
.vr_mode
.map(|vr_mode| {
2024-08-23 11:22:09 +00:00
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)
2024-08-23 11:22:09 +00:00
}
// 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)
2024-08-23 11:22:09 +00:00
}
// use default desktop, as last resort
else {
println!("No VRMode present, fallback to Window");
None
2024-08-23 11:22:09 +00:00
}
})
.flatten();
2024-08-23 11:22:09 +00:00
#[cfg(not(any(feature = "openvr", feature = "openxr")))]
None
}
#[cfg(feature = "sound")]
fn create_sound_handler(&self) -> Result<SoundHandler> {
SoundHandler::new(self.volume_info.clone())
}
}