presenting SwapchainFrames
This commit is contained in:
parent
37aaa07edc
commit
93e64e872f
|
@ -28,7 +28,7 @@ impl WindowState {
|
||||||
.with_resizable(true)
|
.with_resizable(true)
|
||||||
.with_inner_size(LogicalSize::new(800, 600)),
|
.with_inner_size(LogicalSize::new(800, 600)),
|
||||||
// TODO: pass down this error and add some kind of error handling UI or dump
|
// 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);
|
_ = (window_id, new_size);
|
||||||
info!("TODO: implement resize events");
|
info!("TODO: implement resize events");
|
||||||
if let Some(ctx) = self.renderer.window_contexts.get_mut(&window_id) {
|
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,
|
width: new_size.width,
|
||||||
height: new_size.height,
|
height: new_size.height,
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,3 +21,4 @@ winit = "0.30.5"
|
||||||
crossbeam = "0.8.4"
|
crossbeam = "0.8.4"
|
||||||
parking_lot = "0.12.3"
|
parking_lot = "0.12.3"
|
||||||
smol.workspace = true
|
smol.workspace = true
|
||||||
|
tracing-test = "0.2.5"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![feature(c_str_module, closure_lifetime_binder, let_chains, negative_impls)]
|
#![feature(c_str_module, closure_lifetime_binder, let_chains, negative_impls)]
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
collections::{BTreeMap, BTreeSet, HashMap},
|
collections::{BTreeMap, BTreeSet, HashMap},
|
||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
|
@ -8,10 +9,12 @@ use std::{
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicU32, AtomicU64},
|
atomic::{AtomicU32, AtomicU64},
|
||||||
Arc, Mutex,
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use parking_lot::{Mutex, MutexGuard, RwLock};
|
||||||
|
|
||||||
use ash::{
|
use ash::{
|
||||||
khr,
|
khr,
|
||||||
prelude::VkResult,
|
prelude::VkResult,
|
||||||
|
@ -22,7 +25,7 @@ use dyn_clone::DynClone;
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
use tinyvec::{array_vec, ArrayVec};
|
use tinyvec::{array_vec, ArrayVec};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use winit::raw_window_handle::DisplayHandle;
|
use winit::raw_window_handle::{DisplayHandle, RawDisplayHandle};
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
mod images;
|
mod images;
|
||||||
|
@ -111,12 +114,12 @@ impl Queue {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_locked<T, F: FnOnce(vk::Queue) -> T>(&self, map: F) -> T {
|
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)
|
map(*lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lock(&self) -> std::sync::MutexGuard<'_, vk::Queue> {
|
pub fn lock(&self) -> MutexGuard<'_, vk::Queue> {
|
||||||
self.0.lock().unwrap()
|
self.0.lock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,6 +457,7 @@ struct DeviceInner {
|
||||||
physical: PhysicalDevice,
|
physical: PhysicalDevice,
|
||||||
device: ash::Device,
|
device: ash::Device,
|
||||||
swapchain: khr::swapchain::Device,
|
swapchain: khr::swapchain::Device,
|
||||||
|
debug_utils: ash::ext::debug_utils::Device,
|
||||||
main_queue: Queue,
|
main_queue: Queue,
|
||||||
compute_queue: Queue,
|
compute_queue: Queue,
|
||||||
transfer_queue: Queue,
|
transfer_queue: Queue,
|
||||||
|
@ -481,6 +485,9 @@ impl Device {
|
||||||
fn swapchain(&self) -> &khr::swapchain::Device {
|
fn swapchain(&self) -> &khr::swapchain::Device {
|
||||||
&self.0.swapchain
|
&self.0.swapchain
|
||||||
}
|
}
|
||||||
|
fn debug_utils(&self) -> &ash::ext::debug_utils::Device {
|
||||||
|
&self.0.debug_utils
|
||||||
|
}
|
||||||
fn queue_families(&self) -> &DeviceQueueFamilies {
|
fn queue_families(&self) -> &DeviceQueueFamilies {
|
||||||
&self.0.physical.queue_families
|
&self.0.physical.queue_families
|
||||||
}
|
}
|
||||||
|
@ -540,20 +547,14 @@ struct RawSwapchain(vk::SwapchainKHR);
|
||||||
impl !Sync for RawSwapchain {}
|
impl !Sync for RawSwapchain {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct SwapchainHandle(parking_lot::Mutex<vk::SwapchainKHR>);
|
struct SwapchainHandle(Mutex<vk::SwapchainKHR>);
|
||||||
|
|
||||||
impl SwapchainHandle {
|
impl SwapchainHandle {
|
||||||
unsafe fn from_handle(swapchain: vk::SwapchainKHR) -> SwapchainHandle {
|
unsafe fn from_handle(swapchain: vk::SwapchainKHR) -> SwapchainHandle {
|
||||||
Self(parking_lot::Mutex::new(swapchain))
|
Self(Mutex::new(swapchain))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lock(
|
fn lock(&self) -> MutexGuard<'_, vk::SwapchainKHR> {
|
||||||
&self,
|
|
||||||
) -> parking_lot::lock_api::MutexGuard<
|
|
||||||
'_,
|
|
||||||
parking_lot::RawMutex,
|
|
||||||
vk::SwapchainKHR,
|
|
||||||
> {
|
|
||||||
self.0.lock()
|
self.0.lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,6 +588,7 @@ pub struct Swapchain {
|
||||||
fences: Vec<Arc<sync::Fence>>,
|
fences: Vec<Arc<sync::Fence>>,
|
||||||
|
|
||||||
current_frame: AtomicU32,
|
current_frame: AtomicU32,
|
||||||
|
present_id: AtomicU64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Swapchain {
|
impl Drop for Swapchain {
|
||||||
|
@ -645,6 +647,8 @@ struct SwapchainParams {
|
||||||
extent: vk::Extent2D,
|
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 struct SwapchainFrame {
|
||||||
pub swapchain: Arc<Swapchain>,
|
pub swapchain: Arc<Swapchain>,
|
||||||
pub index: u32,
|
pub index: u32,
|
||||||
|
@ -654,6 +658,12 @@ pub struct SwapchainFrame {
|
||||||
pub release: vk::Semaphore,
|
pub release: vk::Semaphore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SwapchainFrame {
|
||||||
|
fn present(self) {
|
||||||
|
self.swapchain.clone().present(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Swapchain {
|
impl Swapchain {
|
||||||
const PREFERRED_IMAGES_IN_FLIGHT: u32 = 3;
|
const PREFERRED_IMAGES_IN_FLIGHT: u32 = 3;
|
||||||
|
|
||||||
|
@ -794,28 +804,79 @@ impl Swapchain {
|
||||||
|
|
||||||
let acquire_semaphores = {
|
let acquire_semaphores = {
|
||||||
(0..inflight_frames)
|
(0..inflight_frames)
|
||||||
.map(|_| unsafe {
|
.map(|i| unsafe {
|
||||||
device.dev().create_semaphore(
|
device.dev().create_semaphore(
|
||||||
&vk::SemaphoreCreateInfo::default(),
|
&vk::SemaphoreCreateInfo::default(),
|
||||||
None,
|
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<_>>>()?
|
.collect::<VkResult<Vec<_>>>()?
|
||||||
};
|
};
|
||||||
|
|
||||||
let release_semaphores = {
|
let release_semaphores = {
|
||||||
(0..inflight_frames)
|
(0..inflight_frames)
|
||||||
.map(|_| unsafe {
|
.map(|i| unsafe {
|
||||||
device.dev().create_semaphore(
|
device.dev().create_semaphore(
|
||||||
&vk::SemaphoreCreateInfo::default(),
|
&vk::SemaphoreCreateInfo::default(),
|
||||||
None,
|
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<_>>>()?
|
.collect::<VkResult<Vec<_>>>()?
|
||||||
};
|
};
|
||||||
|
|
||||||
let fences = {
|
let fences = {
|
||||||
(0..inflight_frames)
|
(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<_>>>()?
|
.collect::<VkResult<Vec<_>>>()?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -837,6 +898,7 @@ impl Swapchain {
|
||||||
release_semaphores,
|
release_semaphores,
|
||||||
fences,
|
fences,
|
||||||
current_frame: AtomicU32::new(0),
|
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(
|
fn create_vkswapchainkhr(
|
||||||
device: &Device,
|
device: &Device,
|
||||||
surface: vk::SurfaceKHR,
|
surface: vk::SurfaceKHR,
|
||||||
|
@ -956,6 +1047,23 @@ impl Swapchain {
|
||||||
let (swapchain, images) = unsafe {
|
let (swapchain, images) = unsafe {
|
||||||
let swapchain =
|
let swapchain =
|
||||||
device.swapchain().create_swapchain(&create_info, None)?;
|
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)?;
|
let images = device.swapchain().get_swapchain_images(swapchain)?;
|
||||||
|
|
||||||
(SwapchainHandle::from_handle(swapchain), images)
|
(SwapchainHandle::from_handle(swapchain), images)
|
||||||
|
@ -964,6 +1072,7 @@ impl Swapchain {
|
||||||
Ok((swapchain, images))
|
Ok((swapchain, images))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static SWAPCHAIN_COUNT: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
struct Surface {
|
struct Surface {
|
||||||
instance: Arc<Instance>,
|
instance: Arc<Instance>,
|
||||||
|
@ -971,9 +1080,25 @@ struct Surface {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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(
|
fn create(
|
||||||
instance: Arc<Instance>,
|
instance: Arc<Instance>,
|
||||||
display_handle: winit::raw_window_handle::RawDisplayHandle,
|
display_handle: RawDisplayHandle,
|
||||||
window_handle: winit::raw_window_handle::RawWindowHandle,
|
window_handle: winit::raw_window_handle::RawWindowHandle,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let surface = unsafe {
|
let surface = unsafe {
|
||||||
|
@ -1004,6 +1129,14 @@ pub struct Vulkan {
|
||||||
alloc: VkAllocator,
|
alloc: VkAllocator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for Vulkan {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.device.dev().device_wait_idle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Vulkan {
|
impl Vulkan {
|
||||||
const VALIDATION_LAYER_NAME: &'static core::ffi::CStr =
|
const VALIDATION_LAYER_NAME: &'static core::ffi::CStr =
|
||||||
c"VK_LAYER_KHRONOS_validation";
|
c"VK_LAYER_KHRONOS_validation";
|
||||||
|
@ -1021,7 +1154,7 @@ impl Vulkan {
|
||||||
app_name: &str,
|
app_name: &str,
|
||||||
instance_layers: &[&CStr],
|
instance_layers: &[&CStr],
|
||||||
instance_extensions: &[&CStr],
|
instance_extensions: &[&CStr],
|
||||||
display_handle: winit::raw_window_handle::DisplayHandle,
|
display_handle: Option<RawDisplayHandle>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let entry = unsafe { ash::Entry::load()? };
|
let entry = unsafe { ash::Entry::load()? };
|
||||||
|
|
||||||
|
@ -1059,6 +1192,8 @@ impl Vulkan {
|
||||||
|
|
||||||
let layers =
|
let layers =
|
||||||
Self::get_layers(&entry, &[Self::VALIDATION_LAYER_NAME]).unwrap();
|
Self::get_layers(&entry, &[Self::VALIDATION_LAYER_NAME]).unwrap();
|
||||||
|
|
||||||
|
// optional display handle
|
||||||
let extensions = Self::get_extensions(
|
let extensions = Self::get_extensions(
|
||||||
&entry,
|
&entry,
|
||||||
&layers,
|
&layers,
|
||||||
|
@ -1209,21 +1344,16 @@ impl Vulkan {
|
||||||
instance: &Instance,
|
instance: &Instance,
|
||||||
pdev: vk::PhysicalDevice,
|
pdev: vk::PhysicalDevice,
|
||||||
queue_family: u32,
|
queue_family: u32,
|
||||||
display_handle: winit::raw_window_handle::DisplayHandle,
|
display_handle: RawDisplayHandle,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
match display_handle.as_raw() {
|
match display_handle {
|
||||||
winit::raw_window_handle::RawDisplayHandle::Xlib(
|
RawDisplayHandle::Xlib(_xlib_display_handle) => {
|
||||||
_xlib_display_handle,
|
|
||||||
) => {
|
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
winit::raw_window_handle::RawDisplayHandle::Xcb(
|
RawDisplayHandle::Xcb(_xcb_display_handle) => todo!(),
|
||||||
_xcb_display_handle,
|
RawDisplayHandle::Wayland(wayland_display_handle) => {
|
||||||
) => todo!(),
|
ash::khr::wayland_surface::Instance::new(
|
||||||
winit::raw_window_handle::RawDisplayHandle::Wayland(
|
|
||||||
wayland_display_handle,
|
|
||||||
) => ash::khr::wayland_surface::Instance::new(
|
|
||||||
&instance.entry,
|
&instance.entry,
|
||||||
&instance.instance,
|
&instance.instance,
|
||||||
)
|
)
|
||||||
|
@ -1231,11 +1361,12 @@ impl Vulkan {
|
||||||
pdev,
|
pdev,
|
||||||
queue_family,
|
queue_family,
|
||||||
wayland_display_handle.display.cast().as_mut(),
|
wayland_display_handle.display.cast().as_mut(),
|
||||||
),
|
)
|
||||||
winit::raw_window_handle::RawDisplayHandle::Drm(_) => {
|
}
|
||||||
|
RawDisplayHandle::Drm(_) => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
winit::raw_window_handle::RawDisplayHandle::Windows(_) => {
|
RawDisplayHandle::Windows(_) => {
|
||||||
ash::khr::win32_surface::Instance::new(
|
ash::khr::win32_surface::Instance::new(
|
||||||
&instance.entry,
|
&instance.entry,
|
||||||
&instance.instance,
|
&instance.instance,
|
||||||
|
@ -1252,7 +1383,7 @@ impl Vulkan {
|
||||||
|
|
||||||
fn select_pdev_queue_families(
|
fn select_pdev_queue_families(
|
||||||
instance: &Instance,
|
instance: &Instance,
|
||||||
display_handle: winit::raw_window_handle::DisplayHandle,
|
display_handle: Option<RawDisplayHandle>,
|
||||||
pdev: vk::PhysicalDevice,
|
pdev: vk::PhysicalDevice,
|
||||||
) -> DeviceQueueFamilies {
|
) -> DeviceQueueFamilies {
|
||||||
let queue_families = unsafe {
|
let queue_families = unsafe {
|
||||||
|
@ -1329,12 +1460,16 @@ impl Vulkan {
|
||||||
family.queue_flags.contains(vk::QueueFlags::TRANSFER)
|
family.queue_flags.contains(vk::QueueFlags::TRANSFER)
|
||||||
|| is_compute
|
|| is_compute
|
||||||
|| is_graphics;
|
|| is_graphics;
|
||||||
let is_present = Self::queue_family_supports_presentation(
|
let is_present = display_handle
|
||||||
|
.map(|display_handle| {
|
||||||
|
Self::queue_family_supports_presentation(
|
||||||
instance,
|
instance,
|
||||||
pdev,
|
pdev,
|
||||||
q,
|
q,
|
||||||
display_handle,
|
display_handle,
|
||||||
);
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
QueueFamily {
|
QueueFamily {
|
||||||
num_queues: family.queue_count,
|
num_queues: family.queue_count,
|
||||||
is_compute,
|
is_compute,
|
||||||
|
@ -1359,12 +1494,7 @@ impl Vulkan {
|
||||||
// find present queue first because it is rather more important than a secondary compute queue
|
// find present queue first because it is rather more important than a secondary compute queue
|
||||||
let present =
|
let present =
|
||||||
if !queue_families.0.get(graphics as usize).unwrap().is_present {
|
if !queue_families.0.get(graphics as usize).unwrap().is_present {
|
||||||
// unwrap because we do need a present queue
|
queue_families.find_first(|family| family.is_present)
|
||||||
Some(
|
|
||||||
queue_families
|
|
||||||
.find_first(|family| family.is_present)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -1379,12 +1509,13 @@ impl Vulkan {
|
||||||
async_compute,
|
async_compute,
|
||||||
transfer,
|
transfer,
|
||||||
present: present.or({
|
present: present.or({
|
||||||
if !queue_families.0.get(graphics as usize).unwrap().is_present
|
if display_handle.is_none() {
|
||||||
{
|
// in this case the graphics queue will be used by default
|
||||||
panic!("no present queue available");
|
tracing::info!("no present queue available, using graphics queue as fallback for headless_surface");
|
||||||
|
Some(graphics)
|
||||||
} else {
|
} else {
|
||||||
None
|
tracing::warn!("no present queue available, this is unexpected!");
|
||||||
}
|
None}
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1483,6 +1614,10 @@ impl Vulkan {
|
||||||
&instance.instance,
|
&instance.instance,
|
||||||
&device,
|
&device,
|
||||||
),
|
),
|
||||||
|
debug_utils: ash::ext::debug_utils::Device::new(
|
||||||
|
&instance.instance,
|
||||||
|
&device,
|
||||||
|
),
|
||||||
instance,
|
instance,
|
||||||
main_queue,
|
main_queue,
|
||||||
present_queue,
|
present_queue,
|
||||||
|
@ -1497,7 +1632,7 @@ impl Vulkan {
|
||||||
|
|
||||||
fn choose_physical_device(
|
fn choose_physical_device(
|
||||||
instance: &Instance,
|
instance: &Instance,
|
||||||
display_handle: winit::raw_window_handle::DisplayHandle,
|
display_handle: Option<RawDisplayHandle>,
|
||||||
requirements: &PhysicalDeviceFeatures,
|
requirements: &PhysicalDeviceFeatures,
|
||||||
extra_properties: Vec<Box<dyn ExtendsDeviceProperties2Debug>>,
|
extra_properties: Vec<Box<dyn ExtendsDeviceProperties2Debug>>,
|
||||||
) -> Result<PhysicalDevice> {
|
) -> Result<PhysicalDevice> {
|
||||||
|
@ -1580,7 +1715,7 @@ impl Vulkan {
|
||||||
entry: &ash::Entry,
|
entry: &ash::Entry,
|
||||||
layers: &[&'a CStr],
|
layers: &[&'a CStr],
|
||||||
extensions: &[&'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>)>
|
) -> core::result::Result<Vec<&'a CStr>, (Vec<&'a CStr>, Vec<&'a CStr>)>
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -1606,10 +1741,11 @@ impl Vulkan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(required_extension_names) =
|
let Ok(required_extension_names) = display_handle
|
||||||
ash_window::enumerate_required_extensions(
|
.map(|display_handle| {
|
||||||
display_handle.as_raw(),
|
ash_window::enumerate_required_extensions(display_handle)
|
||||||
)
|
})
|
||||||
|
.unwrap_or(Ok(&[]))
|
||||||
else {
|
else {
|
||||||
return Err((out_extensions, unsupported_extensions));
|
return Err((out_extensions, unsupported_extensions));
|
||||||
};
|
};
|
||||||
|
@ -1665,9 +1801,47 @@ impl Vulkan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use winit::raw_window_handle::RawWindowHandle;
|
||||||
|
|
||||||
pub struct WindowContext {
|
pub struct WindowContext {
|
||||||
|
window_handle: RawWindowHandle,
|
||||||
surface: Arc<Surface>,
|
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 {
|
impl WindowContext {
|
||||||
|
@ -1675,11 +1849,14 @@ impl WindowContext {
|
||||||
instance: Arc<Instance>,
|
instance: Arc<Instance>,
|
||||||
device: Device,
|
device: Device,
|
||||||
extent: vk::Extent2D,
|
extent: vk::Extent2D,
|
||||||
window: winit::raw_window_handle::RawWindowHandle,
|
window_handle: winit::raw_window_handle::RawWindowHandle,
|
||||||
display: winit::raw_window_handle::RawDisplayHandle,
|
display: RawDisplayHandle,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let surface =
|
let surface = Arc::new(Surface::create(
|
||||||
Arc::new(Surface::create(instance.clone(), display, window)?);
|
instance.clone(),
|
||||||
|
display,
|
||||||
|
window_handle,
|
||||||
|
)?);
|
||||||
|
|
||||||
let swapchain = Arc::new(Swapchain::new(
|
let swapchain = Arc::new(Swapchain::new(
|
||||||
instance,
|
instance,
|
||||||
|
@ -1690,14 +1867,53 @@ impl WindowContext {
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
window_handle,
|
||||||
surface,
|
surface,
|
||||||
current_swapchain: swapchain,
|
current_swapchain: RwLock::new(swapchain),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recreate_swapchain(&mut self, extent: vk::Extent2D) -> Result<()> {
|
/// spawns a task that continuously requests images from the current
|
||||||
self.current_swapchain =
|
/// swapchain, sending them to a channel. returns the receiver of the
|
||||||
Arc::new(self.current_swapchain.recreate(extent)?);
|
/// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1705,18 +1921,18 @@ impl WindowContext {
|
||||||
|
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
vulkan: Vulkan,
|
vulkan: Vulkan,
|
||||||
display: winit::raw_window_handle::RawDisplayHandle,
|
display: RawDisplayHandle,
|
||||||
pub window_contexts: HashMap<winit::window::WindowId, WindowContext>,
|
pub window_contexts: HashMap<winit::window::WindowId, WindowContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use vk::Extent2D;
|
pub use vk::Extent2D;
|
||||||
|
|
||||||
impl Renderer {
|
impl Renderer {
|
||||||
pub fn new(display: DisplayHandle) -> Result<Self> {
|
pub fn new(display: RawDisplayHandle) -> Result<Self> {
|
||||||
let vulkan = Vulkan::new("Vidya", &[], &[], display)?;
|
let vulkan = Vulkan::new("Vidya", &[], &[], Some(display))?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
vulkan,
|
vulkan,
|
||||||
display: display.as_raw(),
|
display,
|
||||||
window_contexts: HashMap::new(),
|
window_contexts: HashMap::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1736,15 +1952,14 @@ impl Renderer {
|
||||||
commands::SingleUseCommand::new(dev.clone(), pool.pool())?;
|
commands::SingleUseCommand::new(dev.clone(), pool.pool())?;
|
||||||
let buffer = cmd.command_buffer();
|
let buffer = cmd.command_buffer();
|
||||||
|
|
||||||
let extent = ctx.current_swapchain.extent;
|
let (frame, suboptimal) = smol::block_on(
|
||||||
|
ctx.current_swapchain.read().clone().acquire_image(),
|
||||||
let (frame, suboptimal) =
|
)?;
|
||||||
smol::block_on(ctx.current_swapchain.clone().acquire_image())?;
|
|
||||||
|
|
||||||
if suboptimal {
|
if suboptimal {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"swapchain ({:?}) is suboptimal!",
|
"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())?),
|
Arc::new(sync::Fence::create(dev.clone())?),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let present_id = {
|
frame.present();
|
||||||
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]
|
|
||||||
};
|
|
||||||
|
|
||||||
future.block()?;
|
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