presenting SwapchainFrames
This commit is contained in:
		
							parent
							
								
									37aaa07edc
								
							
						
					
					
						commit
						93e64e872f
					
				| 
						 | 
				
			
			@ -28,7 +28,7 @@ impl WindowState {
 | 
			
		|||
                .with_resizable(true)
 | 
			
		||||
                .with_inner_size(LogicalSize::new(800, 600)),
 | 
			
		||||
            // TODO: pass down this error and add some kind of error handling UI or dump
 | 
			
		||||
            renderer: Renderer::new(display).expect("renderer"),
 | 
			
		||||
            renderer: Renderer::new(display.as_raw()).expect("renderer"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ impl WindowState {
 | 
			
		|||
        _ = (window_id, new_size);
 | 
			
		||||
        info!("TODO: implement resize events");
 | 
			
		||||
        if let Some(ctx) = self.renderer.window_contexts.get_mut(&window_id) {
 | 
			
		||||
            ctx.recreate_swapchain(renderer::Extent2D {
 | 
			
		||||
            ctx.recreate_with(renderer::Extent2D {
 | 
			
		||||
                width: new_size.width,
 | 
			
		||||
                height: new_size.height,
 | 
			
		||||
            })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,3 +21,4 @@ winit = "0.30.5"
 | 
			
		|||
crossbeam = "0.8.4"
 | 
			
		||||
parking_lot = "0.12.3"
 | 
			
		||||
smol.workspace = true
 | 
			
		||||
tracing-test = "0.2.5"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
#![feature(c_str_module, closure_lifetime_binder, let_chains, negative_impls)]
 | 
			
		||||
#![allow(unused)]
 | 
			
		||||
use std::{
 | 
			
		||||
    borrow::Borrow,
 | 
			
		||||
    collections::{BTreeMap, BTreeSet, HashMap},
 | 
			
		||||
    ffi::{CStr, CString},
 | 
			
		||||
    fmt::Debug,
 | 
			
		||||
| 
						 | 
				
			
			@ -8,10 +9,12 @@ use std::{
 | 
			
		|||
    ops::Deref,
 | 
			
		||||
    sync::{
 | 
			
		||||
        atomic::{AtomicU32, AtomicU64},
 | 
			
		||||
        Arc, Mutex,
 | 
			
		||||
        Arc,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use parking_lot::{Mutex, MutexGuard, RwLock};
 | 
			
		||||
 | 
			
		||||
use ash::{
 | 
			
		||||
    khr,
 | 
			
		||||
    prelude::VkResult,
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +25,7 @@ use dyn_clone::DynClone;
 | 
			
		|||
use rand::{Rng, SeedableRng};
 | 
			
		||||
use tinyvec::{array_vec, ArrayVec};
 | 
			
		||||
use tracing::info;
 | 
			
		||||
use winit::raw_window_handle::DisplayHandle;
 | 
			
		||||
use winit::raw_window_handle::{DisplayHandle, RawDisplayHandle};
 | 
			
		||||
 | 
			
		||||
mod commands;
 | 
			
		||||
mod images;
 | 
			
		||||
| 
						 | 
				
			
			@ -111,12 +114,12 @@ impl Queue {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn with_locked<T, F: FnOnce(vk::Queue) -> T>(&self, map: F) -> T {
 | 
			
		||||
        let lock = self.0.lock().expect("mutex lock poison");
 | 
			
		||||
        let lock = self.0.lock();
 | 
			
		||||
        map(*lock)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn lock(&self) -> std::sync::MutexGuard<'_, vk::Queue> {
 | 
			
		||||
        self.0.lock().unwrap()
 | 
			
		||||
    pub fn lock(&self) -> MutexGuard<'_, vk::Queue> {
 | 
			
		||||
        self.0.lock()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -454,6 +457,7 @@ struct DeviceInner {
 | 
			
		|||
    physical: PhysicalDevice,
 | 
			
		||||
    device: ash::Device,
 | 
			
		||||
    swapchain: khr::swapchain::Device,
 | 
			
		||||
    debug_utils: ash::ext::debug_utils::Device,
 | 
			
		||||
    main_queue: Queue,
 | 
			
		||||
    compute_queue: Queue,
 | 
			
		||||
    transfer_queue: Queue,
 | 
			
		||||
| 
						 | 
				
			
			@ -481,6 +485,9 @@ impl Device {
 | 
			
		|||
    fn swapchain(&self) -> &khr::swapchain::Device {
 | 
			
		||||
        &self.0.swapchain
 | 
			
		||||
    }
 | 
			
		||||
    fn debug_utils(&self) -> &ash::ext::debug_utils::Device {
 | 
			
		||||
        &self.0.debug_utils
 | 
			
		||||
    }
 | 
			
		||||
    fn queue_families(&self) -> &DeviceQueueFamilies {
 | 
			
		||||
        &self.0.physical.queue_families
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -540,20 +547,14 @@ struct RawSwapchain(vk::SwapchainKHR);
 | 
			
		|||
impl !Sync for RawSwapchain {}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
struct SwapchainHandle(parking_lot::Mutex<vk::SwapchainKHR>);
 | 
			
		||||
struct SwapchainHandle(Mutex<vk::SwapchainKHR>);
 | 
			
		||||
 | 
			
		||||
impl SwapchainHandle {
 | 
			
		||||
    unsafe fn from_handle(swapchain: vk::SwapchainKHR) -> SwapchainHandle {
 | 
			
		||||
        Self(parking_lot::Mutex::new(swapchain))
 | 
			
		||||
        Self(Mutex::new(swapchain))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn lock(
 | 
			
		||||
        &self,
 | 
			
		||||
    ) -> parking_lot::lock_api::MutexGuard<
 | 
			
		||||
        '_,
 | 
			
		||||
        parking_lot::RawMutex,
 | 
			
		||||
        vk::SwapchainKHR,
 | 
			
		||||
    > {
 | 
			
		||||
    fn lock(&self) -> MutexGuard<'_, vk::SwapchainKHR> {
 | 
			
		||||
        self.0.lock()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -587,6 +588,7 @@ pub struct Swapchain {
 | 
			
		|||
    fences: Vec<Arc<sync::Fence>>,
 | 
			
		||||
 | 
			
		||||
    current_frame: AtomicU32,
 | 
			
		||||
    present_id: AtomicU64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Swapchain {
 | 
			
		||||
| 
						 | 
				
			
			@ -645,6 +647,8 @@ struct SwapchainParams {
 | 
			
		|||
    extent: vk::Extent2D,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[must_use = "This struct represents an acquired image from the swapchain and
 | 
			
		||||
 must be presented in order to free resources on the device."]
 | 
			
		||||
pub struct SwapchainFrame {
 | 
			
		||||
    pub swapchain: Arc<Swapchain>,
 | 
			
		||||
    pub index: u32,
 | 
			
		||||
| 
						 | 
				
			
			@ -654,6 +658,12 @@ pub struct SwapchainFrame {
 | 
			
		|||
    pub release: vk::Semaphore,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SwapchainFrame {
 | 
			
		||||
    fn present(self) {
 | 
			
		||||
        self.swapchain.clone().present(self);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Swapchain {
 | 
			
		||||
    const PREFERRED_IMAGES_IN_FLIGHT: u32 = 3;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -794,28 +804,79 @@ impl Swapchain {
 | 
			
		|||
 | 
			
		||||
        let acquire_semaphores = {
 | 
			
		||||
            (0..inflight_frames)
 | 
			
		||||
                .map(|_| unsafe {
 | 
			
		||||
                .map(|i| unsafe {
 | 
			
		||||
                    device.dev().create_semaphore(
 | 
			
		||||
                        &vk::SemaphoreCreateInfo::default(),
 | 
			
		||||
                        None,
 | 
			
		||||
                    )
 | 
			
		||||
                    ).inspect(|r| {
 | 
			
		||||
                        #[cfg(debug_assertions)]
 | 
			
		||||
 | 
			
		||||
                        {
 | 
			
		||||
                            let name = CString::new(format!(
 | 
			
		||||
                                "semaphore-{:x}_{i}-acquire",
 | 
			
		||||
                                swapchain.0.lock().as_raw()
 | 
			
		||||
                            ))
 | 
			
		||||
                                .unwrap();
 | 
			
		||||
                            unsafe {
 | 
			
		||||
                                device.debug_utils().set_debug_utils_object_name(
 | 
			
		||||
                                    &vk::DebugUtilsObjectNameInfoEXT::default()
 | 
			
		||||
                                        .object_handle(*r)
 | 
			
		||||
                                        .object_name(&name),
 | 
			
		||||
                                );}
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<VkResult<Vec<_>>>()?
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let release_semaphores = {
 | 
			
		||||
            (0..inflight_frames)
 | 
			
		||||
                .map(|_| unsafe {
 | 
			
		||||
                .map(|i| unsafe {
 | 
			
		||||
                    device.dev().create_semaphore(
 | 
			
		||||
                        &vk::SemaphoreCreateInfo::default(),
 | 
			
		||||
                        None,
 | 
			
		||||
                    )
 | 
			
		||||
                    ).inspect(|r| {
 | 
			
		||||
                        #[cfg(debug_assertions)]
 | 
			
		||||
 | 
			
		||||
                        {
 | 
			
		||||
                            let name = CString::new(format!(
 | 
			
		||||
                                "semaphore-{:x}_{i}-release",
 | 
			
		||||
                                swapchain.0.lock().as_raw()
 | 
			
		||||
                            ))
 | 
			
		||||
                                .unwrap();
 | 
			
		||||
                            unsafe {
 | 
			
		||||
                                device.debug_utils().set_debug_utils_object_name(
 | 
			
		||||
                                    &vk::DebugUtilsObjectNameInfoEXT::default()
 | 
			
		||||
                                        .object_handle(*r)
 | 
			
		||||
                                        .object_name(&name),
 | 
			
		||||
                                );}
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
                .collect::<VkResult<Vec<_>>>()?
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let fences = {
 | 
			
		||||
            (0..inflight_frames)
 | 
			
		||||
                .map(|_| Ok(Arc::new(sync::Fence::create(device.clone())?)))
 | 
			
		||||
                .map(|i| Ok(Arc::new(sync::Fence::create(device.clone())
 | 
			
		||||
                    .inspect(|r| {
 | 
			
		||||
                        #[cfg(debug_assertions)]
 | 
			
		||||
 | 
			
		||||
                        {
 | 
			
		||||
                            let name = CString::new(format!(
 | 
			
		||||
                                "fence-{:x}_{i}",
 | 
			
		||||
                                swapchain.0.lock().as_raw()
 | 
			
		||||
                            ))
 | 
			
		||||
                                .unwrap();
 | 
			
		||||
                            unsafe {
 | 
			
		||||
                                device.debug_utils().set_debug_utils_object_name(
 | 
			
		||||
                                    &vk::DebugUtilsObjectNameInfoEXT::default()
 | 
			
		||||
                                        .object_handle(r.fence())
 | 
			
		||||
                                        .object_name(&name),
 | 
			
		||||
                                );}
 | 
			
		||||
                        }
 | 
			
		||||
                    })
 | 
			
		||||
?)))
 | 
			
		||||
                .collect::<VkResult<Vec<_>>>()?
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -837,6 +898,7 @@ impl Swapchain {
 | 
			
		|||
            release_semaphores,
 | 
			
		||||
            fences,
 | 
			
		||||
            current_frame: AtomicU32::new(0),
 | 
			
		||||
            present_id: AtomicU64::new(1),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -919,6 +981,35 @@ impl Swapchain {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn present(&self, frame: SwapchainFrame) -> Result<()> {
 | 
			
		||||
        let swpchain = self.swapchain.lock();
 | 
			
		||||
        let queue = self.device.present_queue().lock();
 | 
			
		||||
 | 
			
		||||
        let wait_semaphores = [frame.release];
 | 
			
		||||
 | 
			
		||||
        // TODO: make this optional for devices with no support for present_wait/present_id
 | 
			
		||||
        let present_id = self
 | 
			
		||||
            .present_id
 | 
			
		||||
            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
 | 
			
		||||
        let mut present_id = vk::PresentIdKHR::default()
 | 
			
		||||
            .present_ids(core::slice::from_ref(&present_id));
 | 
			
		||||
 | 
			
		||||
        let present_info = vk::PresentInfoKHR::default()
 | 
			
		||||
            .image_indices(core::slice::from_ref(&frame.index))
 | 
			
		||||
            .swapchains(core::slice::from_ref(&swpchain))
 | 
			
		||||
            .wait_semaphores(&wait_semaphores)
 | 
			
		||||
            .push_next(&mut present_id);
 | 
			
		||||
 | 
			
		||||
        // call winits pre_present_notify here
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device
 | 
			
		||||
                .swapchain()
 | 
			
		||||
                .queue_present(*queue, &present_info)?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn create_vkswapchainkhr(
 | 
			
		||||
        device: &Device,
 | 
			
		||||
        surface: vk::SurfaceKHR,
 | 
			
		||||
| 
						 | 
				
			
			@ -956,6 +1047,23 @@ impl Swapchain {
 | 
			
		|||
        let (swapchain, images) = unsafe {
 | 
			
		||||
            let swapchain =
 | 
			
		||||
                device.swapchain().create_swapchain(&create_info, None)?;
 | 
			
		||||
 | 
			
		||||
            #[cfg(debug_assertions)]
 | 
			
		||||
            {
 | 
			
		||||
                let name = CString::new(format!(
 | 
			
		||||
                    "swapchain-{}_{}",
 | 
			
		||||
                    surface.as_raw(),
 | 
			
		||||
                    SWAPCHAIN_COUNT
 | 
			
		||||
                        .fetch_add(1, std::sync::atomic::Ordering::Relaxed)
 | 
			
		||||
                ))
 | 
			
		||||
                .unwrap();
 | 
			
		||||
                device.debug_utils().set_debug_utils_object_name(
 | 
			
		||||
                    &vk::DebugUtilsObjectNameInfoEXT::default()
 | 
			
		||||
                        .object_handle(swapchain)
 | 
			
		||||
                        .object_name(&name),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let images = device.swapchain().get_swapchain_images(swapchain)?;
 | 
			
		||||
 | 
			
		||||
            (SwapchainHandle::from_handle(swapchain), images)
 | 
			
		||||
| 
						 | 
				
			
			@ -964,6 +1072,7 @@ impl Swapchain {
 | 
			
		|||
        Ok((swapchain, images))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
static SWAPCHAIN_COUNT: AtomicU64 = AtomicU64::new(0);
 | 
			
		||||
 | 
			
		||||
struct Surface {
 | 
			
		||||
    instance: Arc<Instance>,
 | 
			
		||||
| 
						 | 
				
			
			@ -971,9 +1080,25 @@ struct Surface {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl Surface {
 | 
			
		||||
    fn headless(instance: Arc<Instance>) -> Result<Self> {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let headless_instance = ash::ext::headless_surface::Instance::new(
 | 
			
		||||
                &instance.entry,
 | 
			
		||||
                &instance.instance,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            let surface = headless_instance.create_headless_surface(
 | 
			
		||||
                &vk::HeadlessSurfaceCreateInfoEXT::default(),
 | 
			
		||||
                None,
 | 
			
		||||
            )?;
 | 
			
		||||
 | 
			
		||||
            Ok(Self { instance, surface })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn create(
 | 
			
		||||
        instance: Arc<Instance>,
 | 
			
		||||
        display_handle: winit::raw_window_handle::RawDisplayHandle,
 | 
			
		||||
        display_handle: RawDisplayHandle,
 | 
			
		||||
        window_handle: winit::raw_window_handle::RawWindowHandle,
 | 
			
		||||
    ) -> Result<Self> {
 | 
			
		||||
        let surface = unsafe {
 | 
			
		||||
| 
						 | 
				
			
			@ -1004,6 +1129,14 @@ pub struct Vulkan {
 | 
			
		|||
    alloc: VkAllocator,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Vulkan {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.device.dev().device_wait_idle();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Vulkan {
 | 
			
		||||
    const VALIDATION_LAYER_NAME: &'static core::ffi::CStr =
 | 
			
		||||
        c"VK_LAYER_KHRONOS_validation";
 | 
			
		||||
| 
						 | 
				
			
			@ -1021,7 +1154,7 @@ impl Vulkan {
 | 
			
		|||
        app_name: &str,
 | 
			
		||||
        instance_layers: &[&CStr],
 | 
			
		||||
        instance_extensions: &[&CStr],
 | 
			
		||||
        display_handle: winit::raw_window_handle::DisplayHandle,
 | 
			
		||||
        display_handle: Option<RawDisplayHandle>,
 | 
			
		||||
    ) -> Result<Self> {
 | 
			
		||||
        let entry = unsafe { ash::Entry::load()? };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1059,6 +1192,8 @@ impl Vulkan {
 | 
			
		|||
 | 
			
		||||
        let layers =
 | 
			
		||||
            Self::get_layers(&entry, &[Self::VALIDATION_LAYER_NAME]).unwrap();
 | 
			
		||||
 | 
			
		||||
        // optional display handle
 | 
			
		||||
        let extensions = Self::get_extensions(
 | 
			
		||||
            &entry,
 | 
			
		||||
            &layers,
 | 
			
		||||
| 
						 | 
				
			
			@ -1209,33 +1344,29 @@ impl Vulkan {
 | 
			
		|||
        instance: &Instance,
 | 
			
		||||
        pdev: vk::PhysicalDevice,
 | 
			
		||||
        queue_family: u32,
 | 
			
		||||
        display_handle: winit::raw_window_handle::DisplayHandle,
 | 
			
		||||
        display_handle: RawDisplayHandle,
 | 
			
		||||
    ) -> bool {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            match display_handle.as_raw() {
 | 
			
		||||
                winit::raw_window_handle::RawDisplayHandle::Xlib(
 | 
			
		||||
                    _xlib_display_handle,
 | 
			
		||||
                ) => {
 | 
			
		||||
            match display_handle {
 | 
			
		||||
                RawDisplayHandle::Xlib(_xlib_display_handle) => {
 | 
			
		||||
                    todo!()
 | 
			
		||||
                }
 | 
			
		||||
                winit::raw_window_handle::RawDisplayHandle::Xcb(
 | 
			
		||||
                    _xcb_display_handle,
 | 
			
		||||
                ) => todo!(),
 | 
			
		||||
                winit::raw_window_handle::RawDisplayHandle::Wayland(
 | 
			
		||||
                    wayland_display_handle,
 | 
			
		||||
                ) => ash::khr::wayland_surface::Instance::new(
 | 
			
		||||
                    &instance.entry,
 | 
			
		||||
                    &instance.instance,
 | 
			
		||||
                )
 | 
			
		||||
                .get_physical_device_wayland_presentation_support(
 | 
			
		||||
                    pdev,
 | 
			
		||||
                    queue_family,
 | 
			
		||||
                    wayland_display_handle.display.cast().as_mut(),
 | 
			
		||||
                ),
 | 
			
		||||
                winit::raw_window_handle::RawDisplayHandle::Drm(_) => {
 | 
			
		||||
                RawDisplayHandle::Xcb(_xcb_display_handle) => todo!(),
 | 
			
		||||
                RawDisplayHandle::Wayland(wayland_display_handle) => {
 | 
			
		||||
                    ash::khr::wayland_surface::Instance::new(
 | 
			
		||||
                        &instance.entry,
 | 
			
		||||
                        &instance.instance,
 | 
			
		||||
                    )
 | 
			
		||||
                    .get_physical_device_wayland_presentation_support(
 | 
			
		||||
                        pdev,
 | 
			
		||||
                        queue_family,
 | 
			
		||||
                        wayland_display_handle.display.cast().as_mut(),
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                RawDisplayHandle::Drm(_) => {
 | 
			
		||||
                    todo!()
 | 
			
		||||
                }
 | 
			
		||||
                winit::raw_window_handle::RawDisplayHandle::Windows(_) => {
 | 
			
		||||
                RawDisplayHandle::Windows(_) => {
 | 
			
		||||
                    ash::khr::win32_surface::Instance::new(
 | 
			
		||||
                        &instance.entry,
 | 
			
		||||
                        &instance.instance,
 | 
			
		||||
| 
						 | 
				
			
			@ -1252,7 +1383,7 @@ impl Vulkan {
 | 
			
		|||
 | 
			
		||||
    fn select_pdev_queue_families(
 | 
			
		||||
        instance: &Instance,
 | 
			
		||||
        display_handle: winit::raw_window_handle::DisplayHandle,
 | 
			
		||||
        display_handle: Option<RawDisplayHandle>,
 | 
			
		||||
        pdev: vk::PhysicalDevice,
 | 
			
		||||
    ) -> DeviceQueueFamilies {
 | 
			
		||||
        let queue_families = unsafe {
 | 
			
		||||
| 
						 | 
				
			
			@ -1329,12 +1460,16 @@ impl Vulkan {
 | 
			
		|||
                        family.queue_flags.contains(vk::QueueFlags::TRANSFER)
 | 
			
		||||
                            || is_compute
 | 
			
		||||
                            || is_graphics;
 | 
			
		||||
                    let is_present = Self::queue_family_supports_presentation(
 | 
			
		||||
                        instance,
 | 
			
		||||
                        pdev,
 | 
			
		||||
                        q,
 | 
			
		||||
                        display_handle,
 | 
			
		||||
                    );
 | 
			
		||||
                    let is_present = display_handle
 | 
			
		||||
                        .map(|display_handle| {
 | 
			
		||||
                            Self::queue_family_supports_presentation(
 | 
			
		||||
                                instance,
 | 
			
		||||
                                pdev,
 | 
			
		||||
                                q,
 | 
			
		||||
                                display_handle,
 | 
			
		||||
                            )
 | 
			
		||||
                        })
 | 
			
		||||
                        .unwrap_or(false);
 | 
			
		||||
                    QueueFamily {
 | 
			
		||||
                        num_queues: family.queue_count,
 | 
			
		||||
                        is_compute,
 | 
			
		||||
| 
						 | 
				
			
			@ -1359,12 +1494,7 @@ impl Vulkan {
 | 
			
		|||
        // find present queue first because it is rather more important than a secondary compute queue
 | 
			
		||||
        let present =
 | 
			
		||||
            if !queue_families.0.get(graphics as usize).unwrap().is_present {
 | 
			
		||||
                // unwrap because we do need a present queue
 | 
			
		||||
                Some(
 | 
			
		||||
                    queue_families
 | 
			
		||||
                        .find_first(|family| family.is_present)
 | 
			
		||||
                        .unwrap(),
 | 
			
		||||
                )
 | 
			
		||||
                queue_families.find_first(|family| family.is_present)
 | 
			
		||||
            } else {
 | 
			
		||||
                None
 | 
			
		||||
            };
 | 
			
		||||
| 
						 | 
				
			
			@ -1379,12 +1509,13 @@ impl Vulkan {
 | 
			
		|||
            async_compute,
 | 
			
		||||
            transfer,
 | 
			
		||||
            present: present.or({
 | 
			
		||||
                if !queue_families.0.get(graphics as usize).unwrap().is_present
 | 
			
		||||
                {
 | 
			
		||||
                    panic!("no present queue available");
 | 
			
		||||
                if display_handle.is_none() {
 | 
			
		||||
                    // in this case the graphics queue will be used by default
 | 
			
		||||
                    tracing::info!("no present queue available, using graphics queue as fallback for headless_surface");
 | 
			
		||||
                    Some(graphics)
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
                    tracing::warn!("no present queue available, this is unexpected!");
 | 
			
		||||
                    None}
 | 
			
		||||
            }),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1483,6 +1614,10 @@ impl Vulkan {
 | 
			
		|||
                    &instance.instance,
 | 
			
		||||
                    &device,
 | 
			
		||||
                ),
 | 
			
		||||
                debug_utils: ash::ext::debug_utils::Device::new(
 | 
			
		||||
                    &instance.instance,
 | 
			
		||||
                    &device,
 | 
			
		||||
                ),
 | 
			
		||||
                instance,
 | 
			
		||||
                main_queue,
 | 
			
		||||
                present_queue,
 | 
			
		||||
| 
						 | 
				
			
			@ -1497,7 +1632,7 @@ impl Vulkan {
 | 
			
		|||
 | 
			
		||||
    fn choose_physical_device(
 | 
			
		||||
        instance: &Instance,
 | 
			
		||||
        display_handle: winit::raw_window_handle::DisplayHandle,
 | 
			
		||||
        display_handle: Option<RawDisplayHandle>,
 | 
			
		||||
        requirements: &PhysicalDeviceFeatures,
 | 
			
		||||
        extra_properties: Vec<Box<dyn ExtendsDeviceProperties2Debug>>,
 | 
			
		||||
    ) -> Result<PhysicalDevice> {
 | 
			
		||||
| 
						 | 
				
			
			@ -1580,7 +1715,7 @@ impl Vulkan {
 | 
			
		|||
        entry: &ash::Entry,
 | 
			
		||||
        layers: &[&'a CStr],
 | 
			
		||||
        extensions: &[&'a CStr],
 | 
			
		||||
        display_handle: winit::raw_window_handle::DisplayHandle,
 | 
			
		||||
        display_handle: Option<RawDisplayHandle>,
 | 
			
		||||
    ) -> core::result::Result<Vec<&'a CStr>, (Vec<&'a CStr>, Vec<&'a CStr>)>
 | 
			
		||||
    {
 | 
			
		||||
        unsafe {
 | 
			
		||||
| 
						 | 
				
			
			@ -1606,10 +1741,11 @@ impl Vulkan {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let Ok(required_extension_names) =
 | 
			
		||||
                ash_window::enumerate_required_extensions(
 | 
			
		||||
                    display_handle.as_raw(),
 | 
			
		||||
                )
 | 
			
		||||
            let Ok(required_extension_names) = display_handle
 | 
			
		||||
                .map(|display_handle| {
 | 
			
		||||
                    ash_window::enumerate_required_extensions(display_handle)
 | 
			
		||||
                })
 | 
			
		||||
                .unwrap_or(Ok(&[]))
 | 
			
		||||
            else {
 | 
			
		||||
                return Err((out_extensions, unsupported_extensions));
 | 
			
		||||
            };
 | 
			
		||||
| 
						 | 
				
			
			@ -1665,9 +1801,47 @@ impl Vulkan {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
use winit::raw_window_handle::RawWindowHandle;
 | 
			
		||||
 | 
			
		||||
pub struct WindowContext {
 | 
			
		||||
    window_handle: RawWindowHandle,
 | 
			
		||||
    surface: Arc<Surface>,
 | 
			
		||||
    current_swapchain: Arc<Swapchain>,
 | 
			
		||||
    // this mutex is for guarding the swapchain against being replaced
 | 
			
		||||
    // underneath WindowContext's functions
 | 
			
		||||
    current_swapchain: RwLock<Arc<Swapchain>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for WindowContext {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.current_swapchain
 | 
			
		||||
                .read()
 | 
			
		||||
                .device
 | 
			
		||||
                .dev()
 | 
			
		||||
                .device_wait_idle();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe impl Send for WindowContext {}
 | 
			
		||||
unsafe impl Sync for WindowContext {}
 | 
			
		||||
 | 
			
		||||
impl Borrow<RawWindowHandle> for WindowContext {
 | 
			
		||||
    fn borrow(&self) -> &RawWindowHandle {
 | 
			
		||||
        &self.window_handle
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl PartialEq for WindowContext {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.window_handle == other.window_handle
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl Eq for WindowContext {}
 | 
			
		||||
 | 
			
		||||
impl core::hash::Hash for WindowContext {
 | 
			
		||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.window_handle.hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WindowContext {
 | 
			
		||||
| 
						 | 
				
			
			@ -1675,11 +1849,14 @@ impl WindowContext {
 | 
			
		|||
        instance: Arc<Instance>,
 | 
			
		||||
        device: Device,
 | 
			
		||||
        extent: vk::Extent2D,
 | 
			
		||||
        window: winit::raw_window_handle::RawWindowHandle,
 | 
			
		||||
        display: winit::raw_window_handle::RawDisplayHandle,
 | 
			
		||||
        window_handle: winit::raw_window_handle::RawWindowHandle,
 | 
			
		||||
        display: RawDisplayHandle,
 | 
			
		||||
    ) -> Result<Self> {
 | 
			
		||||
        let surface =
 | 
			
		||||
            Arc::new(Surface::create(instance.clone(), display, window)?);
 | 
			
		||||
        let surface = Arc::new(Surface::create(
 | 
			
		||||
            instance.clone(),
 | 
			
		||||
            display,
 | 
			
		||||
            window_handle,
 | 
			
		||||
        )?);
 | 
			
		||||
 | 
			
		||||
        let swapchain = Arc::new(Swapchain::new(
 | 
			
		||||
            instance,
 | 
			
		||||
| 
						 | 
				
			
			@ -1690,14 +1867,53 @@ impl WindowContext {
 | 
			
		|||
        )?);
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            window_handle,
 | 
			
		||||
            surface,
 | 
			
		||||
            current_swapchain: swapchain,
 | 
			
		||||
            current_swapchain: RwLock::new(swapchain),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn recreate_swapchain(&mut self, extent: vk::Extent2D) -> Result<()> {
 | 
			
		||||
        self.current_swapchain =
 | 
			
		||||
            Arc::new(self.current_swapchain.recreate(extent)?);
 | 
			
		||||
    /// spawns a task that continuously requests images from the current
 | 
			
		||||
    /// swapchain, sending them to a channel.  returns the receiver of the
 | 
			
		||||
    /// channel, and a handle to the task, allowing for cancellation.
 | 
			
		||||
    fn images(
 | 
			
		||||
        self: Arc<Self>,
 | 
			
		||||
    ) -> (
 | 
			
		||||
        smol::channel::Receiver<SwapchainFrame>,
 | 
			
		||||
        smol::Task<std::result::Result<(), Error>>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let (tx, rx) = smol::channel::bounded(8);
 | 
			
		||||
        let task = smol::spawn(async move {
 | 
			
		||||
            loop {
 | 
			
		||||
                let frame = self.acquire_image().await?;
 | 
			
		||||
                tx.send(frame)
 | 
			
		||||
                    .await
 | 
			
		||||
                    .expect("channel closed on swapchain acquiring frame");
 | 
			
		||||
            }
 | 
			
		||||
            Result::Ok(())
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        (rx, task)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn acquire_image(&self) -> Result<SwapchainFrame> {
 | 
			
		||||
        // clone swapchain to keep it alive
 | 
			
		||||
        let swapchain = self.current_swapchain.read().clone();
 | 
			
		||||
        let (frame, suboptimal) = swapchain.clone().acquire_image().await?;
 | 
			
		||||
        if suboptimal {
 | 
			
		||||
            let mut lock = self.current_swapchain.write();
 | 
			
		||||
            // only recreate our swapchain if it is still same, or else it might have already been recreated.
 | 
			
		||||
            if Arc::ptr_eq(&swapchain, &lock) {
 | 
			
		||||
                *lock = Arc::new(lock.recreate(lock.extent)?);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(frame)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn recreate_with(&self, extent: vk::Extent2D) -> Result<()> {
 | 
			
		||||
        let mut swapchain = self.current_swapchain.write();
 | 
			
		||||
        *swapchain = Arc::new(swapchain.recreate(extent)?);
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1705,18 +1921,18 @@ impl WindowContext {
 | 
			
		|||
 | 
			
		||||
pub struct Renderer {
 | 
			
		||||
    vulkan: Vulkan,
 | 
			
		||||
    display: winit::raw_window_handle::RawDisplayHandle,
 | 
			
		||||
    display: RawDisplayHandle,
 | 
			
		||||
    pub window_contexts: HashMap<winit::window::WindowId, WindowContext>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub use vk::Extent2D;
 | 
			
		||||
 | 
			
		||||
impl Renderer {
 | 
			
		||||
    pub fn new(display: DisplayHandle) -> Result<Self> {
 | 
			
		||||
        let vulkan = Vulkan::new("Vidya", &[], &[], display)?;
 | 
			
		||||
    pub fn new(display: RawDisplayHandle) -> Result<Self> {
 | 
			
		||||
        let vulkan = Vulkan::new("Vidya", &[], &[], Some(display))?;
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            vulkan,
 | 
			
		||||
            display: display.as_raw(),
 | 
			
		||||
            display,
 | 
			
		||||
            window_contexts: HashMap::new(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1736,15 +1952,14 @@ impl Renderer {
 | 
			
		|||
                commands::SingleUseCommand::new(dev.clone(), pool.pool())?;
 | 
			
		||||
            let buffer = cmd.command_buffer();
 | 
			
		||||
 | 
			
		||||
            let extent = ctx.current_swapchain.extent;
 | 
			
		||||
 | 
			
		||||
            let (frame, suboptimal) =
 | 
			
		||||
                smol::block_on(ctx.current_swapchain.clone().acquire_image())?;
 | 
			
		||||
            let (frame, suboptimal) = smol::block_on(
 | 
			
		||||
                ctx.current_swapchain.read().clone().acquire_image(),
 | 
			
		||||
            )?;
 | 
			
		||||
 | 
			
		||||
            if suboptimal {
 | 
			
		||||
                tracing::warn!(
 | 
			
		||||
                    "swapchain ({:?}) is suboptimal!",
 | 
			
		||||
                    ctx.current_swapchain.swapchain
 | 
			
		||||
                    ctx.current_swapchain.read().swapchain
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1826,42 +2041,11 @@ impl Renderer {
 | 
			
		|||
                    Arc::new(sync::Fence::create(dev.clone())?),
 | 
			
		||||
                )?;
 | 
			
		||||
 | 
			
		||||
                let present_id = {
 | 
			
		||||
                    let swapchain = ctx.current_swapchain.swapchain.lock();
 | 
			
		||||
                    let queue = dev.present_queue().lock();
 | 
			
		||||
 | 
			
		||||
                    let wait_semaphores = [frame.release];
 | 
			
		||||
                    let swapchains = [*swapchain];
 | 
			
		||||
                    let indices = [frame.index];
 | 
			
		||||
                    static PRESENT_ID: AtomicU64 = AtomicU64::new(1);
 | 
			
		||||
                    let mut present_ids = [PRESENT_ID
 | 
			
		||||
                        .fetch_add(1, std::sync::atomic::Ordering::Release)];
 | 
			
		||||
                    let mut present_id =
 | 
			
		||||
                        vk::PresentIdKHR::default().present_ids(&present_ids);
 | 
			
		||||
                    let present_info = vk::PresentInfoKHR::default()
 | 
			
		||||
                        .image_indices(core::slice::from_ref(&frame.index))
 | 
			
		||||
                        .swapchains(core::slice::from_ref(&swapchain))
 | 
			
		||||
                        .wait_semaphores(&wait_semaphores)
 | 
			
		||||
                        .push_next(&mut present_id);
 | 
			
		||||
                    dev.swapchain().queue_present(*queue, &present_info)?;
 | 
			
		||||
 | 
			
		||||
                    present_ids[0]
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                frame.present();
 | 
			
		||||
                future.block()?;
 | 
			
		||||
                ctx.current_swapchain.swapchain.with_locked(|swapchain| {
 | 
			
		||||
                    khr::present_wait::Device::new(
 | 
			
		||||
                        &self.vulkan.instance.instance,
 | 
			
		||||
                        self.vulkan.device.dev(),
 | 
			
		||||
                    )
 | 
			
		||||
                    .wait_for_present(
 | 
			
		||||
                        swapchain,
 | 
			
		||||
                        present_id,
 | 
			
		||||
                        u64::MAX,
 | 
			
		||||
                    )
 | 
			
		||||
                })?;
 | 
			
		||||
 | 
			
		||||
                dev.dev().device_wait_idle();
 | 
			
		||||
                // wait for idle here is unnecessary.
 | 
			
		||||
                // dev.dev().device_wait_idle();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2534,3 +2718,58 @@ pub mod utils {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test_swapchain {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    fn create_headless_vk() -> Result<(Vulkan, WindowContext)> {
 | 
			
		||||
        let vk = Vulkan::new(
 | 
			
		||||
            "testing",
 | 
			
		||||
            &[],
 | 
			
		||||
            &[ash::ext::headless_surface::NAME, khr::surface::NAME],
 | 
			
		||||
            None,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        let surface = Arc::new(Surface::headless(vk.instance.clone())?);
 | 
			
		||||
 | 
			
		||||
        let swapchain = Arc::new(Swapchain::new(
 | 
			
		||||
            vk.instance.clone(),
 | 
			
		||||
            vk.device.clone(),
 | 
			
		||||
            surface.clone(),
 | 
			
		||||
            vk.device.phy(),
 | 
			
		||||
            vk::Extent2D::default().width(1).height(1),
 | 
			
		||||
        )?);
 | 
			
		||||
 | 
			
		||||
        let window_ctx = WindowContext {
 | 
			
		||||
            window_handle: RawWindowHandle::Web(
 | 
			
		||||
                winit::raw_window_handle::WebWindowHandle::new(0),
 | 
			
		||||
            ),
 | 
			
		||||
            surface,
 | 
			
		||||
            current_swapchain: RwLock::new(swapchain),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok((vk, window_ctx))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[tracing_test::traced_test]
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn async_swapchain_acquiring() {
 | 
			
		||||
        let (vk, ctx) = create_headless_vk().expect("init");
 | 
			
		||||
        let ctx = Arc::new(ctx);
 | 
			
		||||
        let (rx, handle) = ctx.images();
 | 
			
		||||
 | 
			
		||||
        let mut count = 0;
 | 
			
		||||
        loop {
 | 
			
		||||
            let mut now = std::time::Instant::now();
 | 
			
		||||
            let frame = rx.recv_blocking().expect("recv");
 | 
			
		||||
            frame.present();
 | 
			
		||||
            tracing::info!("mspf: {}ms", now.elapsed().as_secs_f64() / 1000.0);
 | 
			
		||||
            count += 1;
 | 
			
		||||
            if count > 1000 {
 | 
			
		||||
                handle.cancel();
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue