From 3318b64c35845b15c8e12c1a5109be4b89109284 Mon Sep 17 00:00:00 2001 From: Jedjoud10 Date: Sat, 7 Oct 2023 10:06:25 -0400 Subject: [PATCH] Removed context traits and moved settings to SurfaceSettings --- examples/example_runner/lib.rs | 21 +-- src/core/app_info.rs | 114 ++++++-------- src/core/device.rs | 9 +- src/core/init.rs | 273 ++++++++------------------------- src/core/instance.rs | 20 +-- src/core/physical_device.rs | 17 +- src/prelude.rs | 2 +- src/wsi/frame.rs | 21 +-- src/wsi/surface.rs | 50 +++--- src/wsi/swapchain.rs | 33 ++-- src/wsi/window.rs | 36 +---- tests/framework/mod.rs | 11 +- 12 files changed, 196 insertions(+), 411 deletions(-) diff --git a/examples/example_runner/lib.rs b/examples/example_runner/lib.rs index 164357a..4e50e2b 100644 --- a/examples/example_runner/lib.rs +++ b/examples/example_runner/lib.rs @@ -2,7 +2,7 @@ use std::fs; use std::fs::File; -use std::io::{Read}; +use std::io::Read; use std::path::Path; use anyhow::{bail, Result}; @@ -307,7 +307,7 @@ impl ExampleRunner { pub fn new( name: impl Into, window: Option<&WindowContext>, - make_settings: impl Fn(AppBuilder) -> AppSettings, + make_settings: impl Fn(AppBuilder) -> AppSettings, ) -> Result { std::env::set_var("RUST_LOG", "trace"); pretty_env_logger::init(); @@ -315,7 +315,6 @@ impl ExampleRunner { .version((1, 0, 0)) .name(name) .validation(true) - .present_mode(vk::PresentModeKHR::MAILBOX) .scratch_chunk_size(1 * 1024u64) // 1 KiB scratch memory per internal buffer .gpu(GPURequirements { dedicated: false, @@ -338,15 +337,17 @@ impl ExampleRunner { ..Default::default() }); - match window { - None => {} - Some(window) => { - settings = settings.window(&window.window); - } - }; + if let Some(window) = window { + settings = settings.surface(Some(SurfaceSettings { + surface_format: None, + present_mode: Some(vk::PresentModeKHR::MAILBOX), + window: &window.window, + })); + } + let settings = make_settings(settings); - let (instance, physical_device, surface, device, allocator, pool, exec, frame, Some(debug_messenger)) = initialize(&settings, window.is_none())? else { + let (instance, physical_device, surface, device, allocator, pool, exec, frame, Some(debug_messenger)) = initialize(&settings)? else { panic!("Asked for debug messenger but didnt get one") }; diff --git a/src/core/app_info.rs b/src/core/app_info.rs index 50d4b3a..f900ff5 100644 --- a/src/core/app_info.rs +++ b/src/core/app_info.rs @@ -4,8 +4,7 @@ use ash::vk; #[cfg(feature = "fsr2")] use fsr2_sys::FfxFsr2InitializationFlagBits; -use crate::core::queue::QueueType; -use crate::WindowInterface; +use crate::{core::queue::QueueType, Window}; /// Structure holding a queue with specific capabilities to request from the physical device. /// @@ -64,7 +63,7 @@ pub struct QueueRequest { /// ``` #[derive(Default, Debug)] pub struct GPURequirements { - /// Whether a dedicated GPU is required. Setting this to true will discard integrated GPUs. +/// Whether a dedicated GPU is required. Setting this to true will discard integrated GPUs. pub dedicated: bool, /// Minimum amount of video memory required, in bytes. Note that this might count shared memory if RAM is shared. pub min_video_memory: usize, @@ -89,6 +88,22 @@ pub struct GPURequirements { pub device_extensions: Vec, } +/// Extra data that is stored within the AppSettings whenever we want to enable renderable Surfaces +#[derive(Derivative)] +#[derivative(Debug)] +pub struct SurfaceSettings<'a> { + /// Optionally a preferred surface format. This is ignored for a headless context. If set to None, a fallback surface format will be chosen. + /// This format is `{BGRA8_SRGB, NONLINEAR_SRGB}` if it is available. Otherwise, the format is implementation-defined. + pub surface_format: Option, + /// Optionally a preferred present mode. This is ignored for a headless context. If set to None, this will fall back to + /// [`VK_PRESENT_MODE_FIFO_KHR`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentModeKHR.html), + /// as this is guaranteed to always be supported. + pub present_mode: Option, + /// The window that we will use for rendering. Do note that this doesn't necessarily need to be a winit window + #[derivative(Debug = "ignore")] + pub window: &'a dyn Window, +} + /// Holds context settings for the FSR2 library #[cfg(feature = "fsr2")] #[derive(Debug)] @@ -116,23 +131,15 @@ impl Default for Fsr2Settings { /// Application settings used to initialize the phobos context. #[derive(Debug)] -pub struct AppSettings<'a, Window: WindowInterface> { +pub struct AppSettings<'a> { /// Application name. Possibly displayed in debugging tools, task manager, etc. pub name: String, /// Application version. pub version: (u32, u32, u32), /// Enable Vulkan validation layers for additional debug output. For developing this should almost always be on. pub enable_validation: bool, - /// Optionally a reference to an object implementing a windowing system. If this is not `None`, it will be used to create a - /// [`VkSurfaceKHR`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html) to present to. - pub window: Option<&'a Window>, - /// Optionally a preferred surface format. This is ignored for a headless context. If set to None, a fallback surface format will be chosen. - /// This format is `{BGRA8_SRGB, NONLINEAR_SRGB}` if it is available. Otherwise, the format is implementation-defined. - pub surface_format: Option, - /// Optionally a preferred present mode. This is ignored for a headless context. If set to None, this will fall back to - /// [`VK_PRESENT_MODE_FIFO_KHR`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentModeKHR.html), - /// as this is guaranteed to always be supported. - pub present_mode: Option, + /// Set to None for headless rendering, or to Some with the approriate settings + pub surface_settings: Option>, /// Minimum requirements the selected physical device should have. pub gpu_requirements: GPURequirements, /// Minimum size of scratch allocator chunks. This is the minimum size of [`ScratchAllocator`](crate::ScratchAllocator) chunks @@ -145,61 +152,40 @@ pub struct AppSettings<'a, Window: WindowInterface> { pub fsr2_settings: Fsr2Settings, } -impl<'a, Window: WindowInterface> Default for AppSettings<'a, Window> { - /// Create some default app settings. One thing to note is that this sets scratch allocator size to 1 byte. - /// This is because passing 0 as the size of a scratch allocator is invalid, and wrapping them in `Option` is - /// also not great for convenience. - fn default() -> Self { - AppSettings { - name: String::from(""), - version: (0, 0, 0), - enable_validation: false, - window: None, - surface_format: None, - present_mode: None, - gpu_requirements: GPURequirements::default(), - scratch_chunk_size: 32768, - raytracing: false, - #[cfg(feature = "fsr2")] - fsr2_settings: Fsr2Settings::default(), - } - } -} - /// The app builder is a convenience struct to easily create [`AppSettings`](crate::AppSettings). /// /// For information about each of the fields, see [`AppSettings`](crate::AppSettings) /// # Example /// ``` /// # use phobos::*; -/// # use phobos::wsi::window::HeadlessWindowInterface; /// /// // No window, so we have to specify the headless window interface type. -/// let info: AppSettings<_> = AppBuilder::::new() +/// let info: AppSettings<_> = AppBuilder::<()>::new() /// .name("My phobos application") /// .present_mode(vk::PresentModeKHR::FIFO) /// .scratch_size(1024) /// .validation(true) /// .build(); /// ``` -pub struct AppBuilder<'a, Window: WindowInterface> { - inner: AppSettings<'a, Window>, +pub struct AppBuilder<'a> { + inner: AppSettings<'a>, } -impl<'a, Window: WindowInterface> Default for AppBuilder<'a, Window> { - /// Create a new app builder with default settings. - fn default() -> Self { - Self { - inner: AppSettings::default(), - } - } -} - -impl<'a, Window: WindowInterface> AppBuilder<'a, Window> { +impl<'a> AppBuilder<'a> { /// Create a new app builder with default settings. pub fn new() -> Self { AppBuilder { - inner: AppSettings::default(), + inner: AppSettings { + name: String::from(""), + version: (0, 0, 0), + enable_validation: false, + gpu_requirements: GPURequirements::default(), + scratch_chunk_size: 32768, + raytracing: false, + #[cfg(feature = "fsr2")] + fsr2_settings: Fsr2Settings::default(), + surface_settings: None, + } } } @@ -221,30 +207,18 @@ impl<'a, Window: WindowInterface> AppBuilder<'a, Window> { self } - /// Set the window interface. - pub fn window(mut self, window: &'a Window) -> Self { - self.inner.window = Some(window); - self - } - - /// The surface format to use (if using a window context). - pub fn surface_format(mut self, format: vk::SurfaceFormatKHR) -> Self { - self.inner.surface_format = Some(format); - self - } - - /// The present mode to use (if using a window context). - pub fn present_mode(mut self, mode: vk::PresentModeKHR) -> Self { - self.inner.present_mode = Some(mode); - self - } - /// The gpu requirements that the physical device must satisfy. pub fn gpu(mut self, gpu: GPURequirements) -> Self { self.inner.gpu_requirements = gpu; self } + /// Enable or disable windowed surface + pub fn surface(mut self, surface: Option>) -> Self { + self.inner.surface_settings = surface; + self + } + /// Scratch allocator chunk size for each of the internally allocated buffers. pub fn scratch_chunk_size(mut self, size: impl Into) -> Self { let size = size.into(); @@ -284,7 +258,7 @@ impl<'a, Window: WindowInterface> AppBuilder<'a, Window> { } /// Build the resulting application settings. - pub fn build(self) -> AppSettings<'a, Window> { + pub fn build(self) -> AppSettings<'a> { self.inner } -} +} \ No newline at end of file diff --git a/src/core/device.rs b/src/core/device.rs index 7da65e3..fce842e 100644 --- a/src/core/device.rs +++ b/src/core/device.rs @@ -15,8 +15,9 @@ use ash::vk; #[cfg(feature = "fsr2")] use fsr2_sys::FfxDimensions2D; -use crate::{AppSettings, Error, Instance, PhysicalDevice, WindowInterface}; +use crate::{AppSettings, Error, Instance, PhysicalDevice}; use crate::core::traits::Nameable; +use derivative::Derivative; #[cfg(feature = "fsr2")] use crate::fsr2::Fsr2Context; #[cfg(feature = "fsr2")] @@ -113,10 +114,10 @@ impl Device { /// Create a new Vulkan device. This is the main interface point with the Vulkan API. /// # Errors /// * Can fail if vulkan device init fails. This is possible if an optional feature was enabled that is not supported. - pub fn new( + pub fn new( instance: &Instance, physical_device: &PhysicalDevice, - settings: &AppSettings, + settings: &AppSettings, ) -> Result { let mut priorities = Vec::::new(); let queue_create_infos = physical_device @@ -196,7 +197,7 @@ impl Device { } // Add required extensions - if settings.window.is_some() { + if settings.surface_settings.is_some() { extension_names.push(CString::from(khr::Swapchain::name())); } diff --git a/src/core/init.rs b/src/core/init.rs index d1c6d8e..acc392c 100644 --- a/src/core/init.rs +++ b/src/core/init.rs @@ -1,232 +1,83 @@ //! Exposes methods to make initialization of the library easier without losing flexibility. -use std::marker::PhantomData; - use anyhow::Result; use crate::{ Allocator, AppSettings, DebugMessenger, DefaultAllocator, Device, ExecutionManager, - FrameManager, Instance, PhysicalDevice, Surface, WindowInterface, + FrameManager, Instance, PhysicalDevice, Surface, SurfaceSettings, }; use crate::pool::{ResourcePool, ResourcePoolCreateInfo}; -/// ZST implementing initialization without a window -pub struct HeadlessContext; - -/// ZST implementing initialization with a window -pub struct WindowedContext { - _phantom: PhantomData, -} - -/// Trait that helps with initializing the context more easily. -pub trait ContextInit { - /// The result type with all created objects. - type Output; - - /// Initialize the context with the default allocator - fn init(settings: &AppSettings) -> Result>; +/// Struct that contains all common Phobos resources to be used at initialization +pub type Phobos = (Instance, PhysicalDevice, Option, Device, A, ResourcePool, ExecutionManager, Option>, Option); - /// Initialize the context with a custom allocator - fn init_with_allocator< - A: Allocator + 'static, - F: FnOnce(&Instance, &PhysicalDevice, &Device) -> Result, - >( - settings: &AppSettings, - make_alloc: F, - ) -> Result>; +/// Initialize the context with the default allocator +pub fn initialize(settings: &AppSettings) -> Result> { + initialize_with_allocator(settings, |instance, physical_device, device| { + DefaultAllocator::new(instance, device, physical_device) + }) } -impl ContextInit for HeadlessContext { - type Output = ( - Instance, - PhysicalDevice, - Device, - A, - ResourcePool, - ExecutionManager, - Option, - ); - - /// Initialize the headless context with the default allocator - fn init(settings: &AppSettings) -> Result> { - Self::init_with_allocator(settings, |instance, physical_device, device| { - DefaultAllocator::new(instance, device, physical_device) - }) - } - - /// Initialize the headless context with a custom allocator - fn init_with_allocator< - A: Allocator + 'static, - F: FnOnce(&Instance, &PhysicalDevice, &Device) -> Result, - >( - settings: &AppSettings, - make_alloc: F, - ) -> Result> { - let instance = Instance::new(settings)?; - let physical_device = PhysicalDevice::select(&instance, None, settings)?; - let device = Device::new(&instance, &physical_device, settings)?; - let allocator = make_alloc(&instance, &physical_device, &device)?; - let pool_info = ResourcePoolCreateInfo { - device: device.clone(), - allocator: allocator.clone(), - scratch_chunk_size: settings.scratch_chunk_size, - }; - let pool = ResourcePool::new(pool_info)?; - let exec = ExecutionManager::new(device.clone(), &physical_device, pool.clone())?; - let debug_messenger = if settings.enable_validation { - Some(DebugMessenger::new(&instance)?) - } else { - None - }; - - Ok((instance, physical_device, device, allocator, pool, exec, debug_messenger)) - } -} +/// Initialize the context with a custom allocator +pub fn initialize_with_allocator< + A: Allocator + 'static, + F: FnOnce(&Instance, &PhysicalDevice, &Device) -> Result, +>( + settings: &AppSettings, + make_alloc: F, +) -> Result> { + let instance = Instance::new(settings)?; + -impl ContextInit for WindowedContext { - /// All created vulkan objects - type Output = ( - Instance, - PhysicalDevice, - Surface, - Device, - A, - ResourcePool, - ExecutionManager, - FrameManager, - Option, - ); + let mut surface = if let Some(SurfaceSettings { window, .. }) = settings.surface_settings.as_ref() { + Some(Surface::new(&instance, *window)?) + } else { + None + }; - /// Initialize the windowed context with a default allocator - fn init(settings: &AppSettings) -> Result> { - WindowedContext::init_with_allocator(settings, |instance, physical_device, device| { - DefaultAllocator::new(instance, device, physical_device) - }) + let physical_device = PhysicalDevice::select(&instance, surface.as_ref(), settings)?; + + if let Some(surface) = surface.as_mut() { + surface.query_details(&physical_device)?; } - /// Initialize the windowed context with a custom allocator - fn init_with_allocator< - A: Allocator + 'static, - F: FnOnce(&Instance, &PhysicalDevice, &Device) -> Result, - >( - settings: &AppSettings, - make_alloc: F, - ) -> Result> { - let instance = Instance::new(settings)?; - let (surface, physical_device) = - { PhysicalDevice::select_with_surface(&instance, settings)? }; - let device = Device::new(&instance, &physical_device, settings)?; - let allocator = make_alloc(&instance, &physical_device, &device)?; - let pool_info = ResourcePoolCreateInfo { - device: device.clone(), - allocator: allocator.clone(), - scratch_chunk_size: settings.scratch_chunk_size, - }; - let pool = ResourcePool::new(pool_info)?; - let exec = ExecutionManager::new(device.clone(), &physical_device, pool.clone())?; - let frame = FrameManager::new_with_swapchain( + let device = Device::new(&instance, &physical_device, settings)?; + let allocator = make_alloc(&instance, &physical_device, &device)?; + let pool_info = ResourcePoolCreateInfo { + device: device.clone(), + allocator: allocator.clone(), + scratch_chunk_size: settings.scratch_chunk_size, + }; + let pool = ResourcePool::new(pool_info)?; + let exec = ExecutionManager::new(device.clone(), &physical_device, pool.clone())?; + + let frame = if let Some(surface_settings) = settings.surface_settings.as_ref() { + Some(FrameManager::new_with_swapchain( &instance, device.clone(), pool.clone(), - settings, - &surface, - )?; - let debug_messenger = if settings.enable_validation { - Some(DebugMessenger::new(&instance)?) - } else { - None - }; - - Ok(( - instance, - physical_device, - surface, - device, - allocator, - pool, - exec, - frame, - debug_messenger, - )) - } -} - -/// Initialize all phobos objects with a custom allocator -pub fn initialize_with_allocator< - W: WindowInterface, - A: Allocator + 'static, - F: FnOnce(&Instance, &PhysicalDevice, &Device) -> Result, ->( - settings: &AppSettings, - headless: bool, - make_alloc: F, -) -> Result<( - Instance, - PhysicalDevice, - Option, - Device, - A, - ResourcePool, - ExecutionManager, - Option>, - Option, -)> { - if headless { - let (instance, physical_device, device, allocator, pool, exec, debug_messenger) = - HeadlessContext::init_with_allocator(settings, make_alloc)?; - Ok(( - instance, - physical_device, - None, - device, - allocator, - pool, - exec, - None, - debug_messenger, - )) + surface_settings, + &surface.as_ref().unwrap(), + )?) } else { - let ( - instance, - physical_device, - surface, - device, - allocator, - pool, - exec, - frame, - debug_messenger, - ) = WindowedContext::init_with_allocator(settings, make_alloc)?; - Ok(( - instance, - physical_device, - Some(surface), - device, - allocator, - pool, - exec, - Some(frame), - debug_messenger, - )) - } -} + None + }; -/// Initialize all phobos objects with the default allocator. -pub fn initialize( - settings: &AppSettings, - headless: bool, -) -> Result<( - Instance, - PhysicalDevice, - Option, - Device, - DefaultAllocator, - ResourcePool, - ExecutionManager, - Option>, - Option, -)> { - initialize_with_allocator(settings, headless, |instance, physical_device, device| { - DefaultAllocator::new(instance, device, physical_device) - }) -} + let debug_messenger = if settings.enable_validation { + Some(DebugMessenger::new(&instance)?) + } else { + None + }; + + Ok(( + instance, + physical_device, + surface, + device, + allocator, + pool, + exec, + frame, + debug_messenger, + )) +} \ No newline at end of file diff --git a/src/core/instance.rs b/src/core/instance.rs index efdbe2f..694a2f7 100644 --- a/src/core/instance.rs +++ b/src/core/instance.rs @@ -8,7 +8,7 @@ use anyhow::Result; use ash; use ash::vk; -use crate::{AppSettings, WindowInterface}; +use crate::{AppSettings, SurfaceSettings}; use crate::util::string::unwrap_to_raw_strings; /// Represents the loaded vulkan instance. @@ -29,7 +29,7 @@ impl Instance { /// * Can fail if the Vulkan loader was not found. Check for valid Vulkan drivers. /// * Can fail if an instance extension or layer was requested that is not supported. This can happen when /// validation is enabled through [`AppSettings`], but the Vulkan SDK is not installed. - pub fn new(settings: &AppSettings) -> Result { + pub fn new(settings: &AppSettings) -> Result { let entry = unsafe { ash::Entry::load()? }; let instance = create_vk_instance(&entry, settings)?; #[cfg(feature = "log-objects")] @@ -66,9 +66,9 @@ impl Deref for Instance { } } -fn create_vk_instance( +fn create_vk_instance( entry: &ash::Entry, - settings: &AppSettings, + settings: &AppSettings, ) -> Result { let app_name = CString::new(settings.name.clone())?; let engine_name = CString::new("Phobos")?; @@ -93,12 +93,7 @@ fn create_vk_instance( extensions.push(CString::from(ash::extensions::ext::DebugUtils::name())); } - info!("Enabled instance extensions:"); - for ext in &extensions { - info!("{:?}", ext); - } - - if let Some(window) = settings.window { + if let Some(SurfaceSettings { window, .. }) = settings.surface_settings { extensions.extend( ash_window::enumerate_required_extensions(window.raw_display_handle())? .to_vec() @@ -107,6 +102,11 @@ fn create_vk_instance( ); } + info!("Enabled instance extensions:"); + for ext in &extensions { + info!("{:?}", ext); + } + let layers_raw = unwrap_to_raw_strings(layers.as_slice()); let extensions_raw = unwrap_to_raw_strings(extensions.as_slice()); diff --git a/src/core/physical_device.rs b/src/core/physical_device.rs index af24183..ca50f98 100644 --- a/src/core/physical_device.rs +++ b/src/core/physical_device.rs @@ -5,7 +5,7 @@ use std::ffi::CStr; use anyhow::Result; use ash::vk; -use crate::{AppSettings, Error, Instance, Surface, WindowInterface}; +use crate::{AppSettings, Error, Instance, Surface}; use crate::core::queue::{QueueInfo, QueueType}; use crate::util::string::wrap_c_str; @@ -39,10 +39,10 @@ pub struct PhysicalDevice { impl PhysicalDevice { /// Selects the best available physical device from the given requirements and parameters. - pub fn select( + pub fn select( instance: &Instance, surface: Option<&Surface>, - settings: &AppSettings, + settings: &AppSettings, ) -> Result { let devices = unsafe { instance.enumerate_physical_devices()? }; if devices.is_empty() { @@ -206,17 +206,6 @@ impl PhysicalDevice { .ok_or_else(|| anyhow::Error::from(Error::NoGPU)) } - /// Selects the best available physical device and creates a surface on it. - pub fn select_with_surface( - instance: &Instance, - settings: &AppSettings, - ) -> Result<(Surface, Self)> { - let mut surface = Surface::new(&instance, &settings)?; - let physical_device = PhysicalDevice::select(&instance, Some(&surface), &settings)?; - surface.query_details(&physical_device)?; - Ok((surface, physical_device)) - } - /// Get all queue families available on this device. This is different from /// [`Device::queue_families()`](crate::Device::queue_families) since this knows about properties of each family, while the /// device function only knows about family indices. diff --git a/src/prelude.rs b/src/prelude.rs index 4956cd6..6d05f18 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -55,5 +55,5 @@ pub mod traits { pub use crate::command_buffer::traits::*; pub use crate::graph::pass_graph::GraphViz; pub use crate::graph::record::RecordGraphToCommandBuffer; - pub use crate::wsi::window::{WindowInterface, WindowSize}; + pub use crate::wsi::window::{Window, WindowSize}; } diff --git a/src/wsi/frame.rs b/src/wsi/frame.rs index 99eeff0..d6e0a10 100644 --- a/src/wsi/frame.rs +++ b/src/wsi/frame.rs @@ -70,8 +70,8 @@ use anyhow::Result; use ash::vk; use crate::{ - Allocator, AppSettings, CmdBuffer, DefaultAllocator, Device, Error, ExecutionManager, Fence, - Image, ImageView, Instance, Semaphore, Surface, Swapchain, WindowInterface, + Allocator, CmdBuffer, DefaultAllocator, Device, Error, ExecutionManager, Fence, + Image, ImageView, Instance, Semaphore, Surface, Swapchain, SurfaceSettings, }; use crate::pool::{Poolable, Pooled, ResourcePool}; use crate::sync::domain::ExecutionDomain; @@ -79,6 +79,8 @@ use crate::sync::submit_batch::SubmitBatch; use crate::util::deferred_delete::DeletionQueue; use crate::wsi::swapchain::SwapchainImage; +use super::window::Window; + /// Information stored for each in-flight frame. #[derive(Derivative)] #[derivative(Debug)] @@ -169,9 +171,9 @@ impl FrameManager { } } - fn resize_swapchain( + fn resize_swapchain( &mut self, - window: &Window, + window: &dyn Window, surface: &Surface, ) -> Result { let mut new_swapchain = Swapchain { @@ -318,29 +320,28 @@ impl FrameManager { } /// Initialize frame manager and create a swapchain. - pub fn new_with_swapchain( + pub fn new_with_swapchain( instance: &Instance, device: Device, pool: ResourcePool, - settings: &AppSettings, + surface_settings: &SurfaceSettings, surface: &Surface, ) -> Result { - let swapchain = Swapchain::new(instance, device.clone(), settings, surface)?; + let swapchain = Swapchain::new(instance, device.clone(), surface_settings, surface)?; FrameManager::new(device, pool, swapchain) } /// Obtain a new frame context to run commands in. /// This will call the provided callback function to obtain a [`SubmitBatch`](crate::sync::submit_batch::SubmitBatch) /// which contains the commands to be submitted for this frame. - pub async fn new_frame( + pub async fn new_frame( &mut self, exec: ExecutionManager, - window: &Window, + window: &dyn Window, surface: &Surface, f: F, ) -> Result<()> where - Window: WindowInterface, D: ExecutionDomain + 'static, F: FnOnce(InFlightContext) -> Result>, { // Advance deletion queue by one frame diff --git a/src/wsi/surface.rs b/src/wsi/surface.rs index 8306e81..97f1de3 100644 --- a/src/wsi/surface.rs +++ b/src/wsi/surface.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use anyhow::Result; use ash::vk; -use crate::{AppSettings, Error, Instance, PhysicalDevice, WindowInterface}; +use crate::{Instance, PhysicalDevice, Window}; /// Contains all information about a [`VkSurfaceKHR`](vk::SurfaceKHR) #[derive(Derivative)] @@ -26,34 +26,30 @@ pub struct Surface { impl Surface { /// Create a new surface. - pub fn new( + pub fn new( instance: &Instance, - settings: &AppSettings, + window: &dyn Window ) -> Result { - if let Some(window) = settings.window { - let functions = - ash::extensions::khr::Surface::new(unsafe { instance.loader() }, instance); - let handle = unsafe { - ash_window::create_surface( - instance.loader(), - instance, - window.raw_display_handle(), - window.raw_window_handle(), - None, - )? - }; - #[cfg(feature = "log-objects")] - trace!("Created new VkSurfaceKHR {handle:p}"); - Ok(Surface { - handle, - functions, - capabilities: Default::default(), - formats: vec![], - present_modes: vec![], - }) - } else { - Err(anyhow::Error::from(Error::NoWindow)) - } + let functions = + ash::extensions::khr::Surface::new(unsafe { instance.loader() }, instance); + let handle = unsafe { + ash_window::create_surface( + instance.loader(), + instance, + window.raw_display_handle(), + window.raw_window_handle(), + None, + )? + }; + #[cfg(feature = "log-objects")] + trace!("Created new VkSurfaceKHR {handle:p}"); + Ok(Surface { + handle, + functions, + capabilities: Default::default(), + formats: vec![], + present_modes: vec![], + }) } /// Query support for features, capabilities and formats for this surface. diff --git a/src/wsi/swapchain.rs b/src/wsi/swapchain.rs index 310ebc6..3f14aaa 100644 --- a/src/wsi/swapchain.rs +++ b/src/wsi/swapchain.rs @@ -5,7 +5,7 @@ use std::ops::Deref; use anyhow::Result; use ash::vk; -use crate::{AppSettings, Device, Error, Instance, Surface, WindowInterface}; +use crate::{Device, Error, Instance, Surface, SurfaceSettings}; use crate::image::*; #[derive(Debug)] @@ -37,15 +37,15 @@ pub struct Swapchain { impl Swapchain { /// Create a new swapchain. - pub fn new( + pub fn new( instance: &Instance, device: Device, - settings: &AppSettings, + surface_settings: &SurfaceSettings, surface: &Surface, ) -> Result { - let format = choose_surface_format(settings, surface)?; - let present_mode = choose_present_mode(settings, surface); - let extent = choose_swapchain_extent(settings, surface); + let format = choose_surface_format(surface_settings, surface)?; + let present_mode = choose_present_mode(surface_settings, surface); + let extent = choose_swapchain_extent(surface_settings, surface); let image_count = { let mut count = surface.capabilities().min_image_count + 1; @@ -160,8 +160,8 @@ impl Drop for Swapchain { } } -fn choose_surface_format( - settings: &AppSettings, +fn choose_surface_format( + surface_settings: &SurfaceSettings, surface: &Surface, ) -> Result { // In case requested format isn't found, try this. If that one isn't found we fall back to the first available format. @@ -170,7 +170,7 @@ fn choose_surface_format( color_space: vk::ColorSpaceKHR::SRGB_NONLINEAR, }; - if let Some(preferred_format) = settings.surface_format { + if let Some(preferred_format) = surface_settings.surface_format { if surface.formats().contains(&preferred_format) { return Ok(preferred_format); } @@ -186,21 +186,22 @@ fn choose_surface_format( .ok_or_else(|| anyhow::Error::from(Error::NoSurfaceFormat)) } -fn choose_present_mode( - settings: &AppSettings, +fn choose_present_mode( + surface_settings: &SurfaceSettings, surface: &Surface, ) -> vk::PresentModeKHR { - if let Some(mode) = settings.present_mode { + if let Some(mode) = surface_settings.present_mode { if surface.present_modes().contains(&mode) { return mode; } } + // VSync, guaranteed to be supported vk::PresentModeKHR::FIFO } -fn choose_swapchain_extent( - settings: &AppSettings, +fn choose_swapchain_extent( + surface_settings: &SurfaceSettings, surface: &Surface, ) -> vk::Extent2D { if surface.capabilities().current_extent.width != u32::MAX { @@ -208,11 +209,11 @@ fn choose_swapchain_extent( } vk::Extent2D { - width: settings.window.unwrap().width().clamp( + width: surface_settings.window.width().clamp( surface.capabilities().min_image_extent.width, surface.capabilities().max_image_extent.width, ), - height: settings.window.unwrap().height().clamp( + height: surface_settings.window.height().clamp( surface.capabilities().min_image_extent.height, surface.capabilities().max_image_extent.height, ), diff --git a/src/wsi/window.rs b/src/wsi/window.rs index c26df46..5433d55 100644 --- a/src/wsi/window.rs +++ b/src/wsi/window.rs @@ -1,7 +1,7 @@ //! Utilities for generic window handling use raw_window_handle::{ - HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, + HasRawDisplayHandle, HasRawWindowHandle, }; #[cfg(feature = "winit")] use winit; @@ -14,31 +14,6 @@ pub trait WindowSize { fn height(&self) -> u32; } -/// Used as a dummy window interface in case of a headless context. Calling any of the `raw_xxx_handle()` functions on this will result in a panic. -pub struct HeadlessWindowInterface; - -unsafe impl HasRawWindowHandle for HeadlessWindowInterface { - fn raw_window_handle(&self) -> RawWindowHandle { - panic!("Called raw_window_handle() on headless window context."); - } -} - -unsafe impl HasRawDisplayHandle for HeadlessWindowInterface { - fn raw_display_handle(&self) -> RawDisplayHandle { - panic!("Called raw_display_handle() on headless window context."); - } -} - -impl WindowSize for HeadlessWindowInterface { - fn width(&self) -> u32 { - panic!("called width() on headless window context."); - } - - fn height(&self) -> u32 { - panic!("Called height() on headless window context"); - } -} - #[cfg(feature = "winit")] impl WindowSize for winit::window::Window { fn width(&self) -> u32 { @@ -50,9 +25,6 @@ impl WindowSize for winit::window::Window { } } -/// Blanket trait combining all requirements for a window interface. To be a window interface, a type T must implement the following traits: -/// - [`HasRawWindowHandle`](raw_window_handle::HasRawWindowHandle) -/// - [`HasRawDisplayHandle`](raw_window_handle::HasRawDisplayHandle) -/// - [`WindowSize`] -pub trait WindowInterface: HasRawWindowHandle + HasRawDisplayHandle + WindowSize {} -impl WindowInterface for T {} +/// Generic "window" trait that is applied to all raw_window_handle providers and those who implement window size +pub trait Window: WindowSize + HasRawDisplayHandle + HasRawWindowHandle {} +impl Window for T {} \ No newline at end of file diff --git a/tests/framework/mod.rs b/tests/framework/mod.rs index 7b1d191..4406f5a 100644 --- a/tests/framework/mod.rs +++ b/tests/framework/mod.rs @@ -9,7 +9,6 @@ use phobos::{ PhysicalDevice, QueueRequest, QueueType, }; use phobos::pool::ResourcePool; -use phobos::wsi::window::HeadlessWindowInterface; #[derive(Clone, Debug)] pub struct Context { @@ -33,7 +32,7 @@ pub fn make_context() -> Result> { pub fn make_context_with_queues( queues: impl Into>, ) -> Result> { - let settings = AppBuilder::::new() + let settings = AppBuilder::new() .name("phobos test framework") .version((0, 0, 1)) .validation(false) @@ -51,7 +50,7 @@ pub fn make_context_with_queues( }) .build(); let (instance, phys_device, None, device, allocator, pool, exec, None, None) = - phobos::initialize(&settings, true)? else { + phobos::initialize(&settings)? else { panic!("test framework: requested headless non-debug context but got debug context or a window."); }; @@ -66,11 +65,11 @@ pub fn make_context_with_queues( } pub fn make_context_with_settings< - F: FnOnce(AppBuilder) -> AppBuilder, + F: FnOnce(AppBuilder) -> AppBuilder, >( callback: F, ) -> Result> { - let builder = AppBuilder::::new() + let builder = AppBuilder::new() .name("phobos test framework") .version((0, 0, 1)) .validation(false) @@ -92,7 +91,7 @@ pub fn make_context_with_settings< let settings = callback(builder).build(); let (instance, phys_device, None, device, allocator, pool, exec, None, None) = - phobos::initialize(&settings, true)? else { + phobos::initialize(&settings)? else { panic!("test framework: requested headless non-debug context but got debug context or a window."); };