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