use std::{
cell::{Cell, RefCell, UnsafeCell},
collections::{HashMap, VecDeque},
+ ffi::CStr,
marker::PhantomData,
os::raw::c_char,
ptr::NonNull,
use vulkan_sys as vk;
use crate::{
- delay_queue::DelayQueue, frame_counter::FrameCounter, Bind, BindGroupLayout,
- BindGroupLayoutDesc, Buffer, BufferArg, BufferDesc, BufferImageCopy, BufferUsageFlags,
- CmdEncoder, ComputePipelineDesc, Device, Extent2d, Extent3d, Frame, GlobalBarrier,
- GpuConcurrent, GraphicsPipelineDesc, Image, ImageBarrier, ImageBlit, ImageDesc, ImageDimension,
- ImageFormat, ImageLayout, ImageTiling, ImageUsageFlags, ImageViewDesc, IndexType,
- MemoryLocation, Offset2d, Offset3d, PersistentBuffer, Pipeline, Sampler, SamplerAddressMode,
- SamplerCompareOp, SamplerDesc, SamplerFilter, SwapchainOutOfDateError, ThreadToken,
- TransientBuffer, TypedBind,
+ frame_counter::FrameCounter, Bind, BindGroupLayout, BindGroupLayoutDesc, Buffer, BufferArg,
+ BufferDesc, BufferImageCopy, BufferUsageFlags, CmdEncoder, ComputePipelineDesc, Device,
+ Extent2d, Extent3d, Frame, GlobalBarrier, GpuConcurrent, GraphicsPipelineDesc, Image,
+ ImageBarrier, ImageBlit, ImageDesc, ImageDimension, ImageFormat, ImageLayout, ImageTiling,
+ ImageUsageFlags, ImageViewDesc, IndexType, MemoryLocation, Offset2d, Offset3d,
+ PersistentBuffer, Pipeline, Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc,
+ SamplerFilter, SwapchainOutOfDateError, ThreadToken, TransientBuffer, TypedBind,
};
mod allocator;
/// recycling of resources.
num_frames: usize,
- /// How many frames to delay swapchain destruction. There's no correct answer
- /// here (spec bug) we're just picking a big number and hoping for the best.
- swapchain_destroy_delay: usize,
+ /// How many frames to delay swapchain semaphore release and swapchain
+ /// destruction. There's no correct answer here (spec bug) we're just picking a
+ /// big number and hoping for the best.
+ ///
+ /// This will not be used if VK_EXT_swapchain_maintenance1 is available.
+ swapchain_semaphore_destroy_delay: u64,
/// How large should transient buffers be, this will limit the maximum size of
/// transient allocations.
const VULKAN_CONSTANTS: VulkanConstants = VulkanConstants {
num_frames: 2,
- swapchain_destroy_delay: 8,
+ swapchain_semaphore_destroy_delay: 8,
transient_buffer_size: 4 * 1024 * 1024,
tlsf_default_super_block_size: 128 * 1024 * 1024,
tlsf_small_super_block_divisor: 16,
}
}
-type SwapchainDestroyQueue = DelayQueue<(vk::SwapchainKHR, vk::SurfaceKHR, Box<[vk::ImageView]>)>;
-
pub(crate) struct VulkanDevice {
instance: vk::Instance,
physical_device: vk::PhysicalDevice,
bind_group_layout_pool: Mutex<Pool<VulkanBindGroupLayout>>,
pipeline_pool: Mutex<Pool<VulkanPipeline>>,
+ recycled_fences: Mutex<VecDeque<vk::Fence>>,
recycled_semaphores: Mutex<VecDeque<vk::Semaphore>>,
recycled_descriptor_pools: Mutex<VecDeque<vk::DescriptorPool>>,
-
recycled_transient_buffers: Mutex<VecDeque<VulkanTransientBuffer>>,
allocator: VulkanAllocator,
cstr!("libvulkan.so.1").as_ptr(),
libc::RTLD_NOW | libc::RTLD_LOCAL,
);
- libc::dlsym(module, cstr!("vkGetInstanceProcAddr").as_ptr())
+ libc::dlsym(module, (c"vkGetInstanceProcAddr").as_ptr())
};
let global_fn = unsafe { vk::GlobalFunctions::new(get_proc_addr) };
}
#[cfg(debug_assertions)]
- let enabled_layers = &[cstr!("VK_LAYER_KHRONOS_validation").as_ptr()];
+ let enabled_layers = &[(c"VK_LAYER_KHRONOS_validation").as_ptr()];
#[cfg(not(debug_assertions))]
let enabled_layers = &[];
let mut enabled_extensions = vec![];
- let wsi_support =
- VulkanWsi::check_instance_extensions(&extension_properties, &mut enabled_extensions);
+ let mut has_get_surface_capabilities2 = false;
+ for extension in &extension_properties {
+ let extension_name = CStr::from_bytes_until_nul(&extension.extension_name).unwrap();
+ if extension_name.to_str().unwrap() == "VK_KHR_get_surface_capabilities2" {
+ has_get_surface_capabilities2 = true;
+ enabled_extensions.push(extension_name);
+ break;
+ }
+ }
+
+ assert!(has_get_surface_capabilities2);
+
+ let mut wsi_support = default();
+ VulkanWsi::check_instance_extensions(
+ &extension_properties,
+ &mut enabled_extensions,
+ &mut wsi_support,
+ );
let enabled_extensions = enabled_extensions
.iter()
let instance_fn = vk::InstanceFunctions::new(&global_fn, instance, vk::VERSION_1_2);
- let wsi = Box::new(VulkanWsi::new(&global_fn, instance, &wsi_support));
-
let physical_devices = vk_vec(|count, ptr| unsafe {
instance_fn.enumerate_physical_devices(instance, count, ptr)
});
let mut enabled_extensions = vec![];
- VulkanWsi::check_device_extensions(&extension_properties, &mut enabled_extensions);
+ VulkanWsi::check_device_extensions(
+ &extension_properties,
+ &mut enabled_extensions,
+ &mut wsi_support,
+ );
let enabled_extensions = enabled_extensions
.iter()
let device_fn = vk::DeviceFunctions::new(&instance_fn, device, vk::VERSION_1_3);
+ let wsi = Box::new(VulkanWsi::new(&global_fn, instance, wsi_support));
+
let universal_queue = unsafe {
let mut queue = vk::Queue::default();
device_fn.get_device_queue(device, queue_family_index, 0, &mut queue);
bind_group_layout_pool: default(),
pipeline_pool: default(),
+ recycled_fences: default(),
recycled_semaphores: default(),
recycled_descriptor_pools: default(),
recycled_transient_buffers: default(),
}
}
+ fn request_fence(&self) -> vk::Fence {
+ if let Some(fence) = self.recycled_fences.lock().pop_front() {
+ let fences = &[fence];
+ vk_check!(self.device_fn.reset_fences(self.device, fences));
+ fence
+ } else {
+ let mut fence = vk::Fence::null();
+ let create_info = vk::FenceCreateInfo::default();
+ vk_check!(self
+ .device_fn
+ .create_fence(self.device, &create_info, None, &mut fence));
+ fence
+ }
+ }
+
fn request_semaphore(&self) -> vk::Semaphore {
if let Some(semaphore) = self.recycled_semaphores.lock().pop_front() {
semaphore
}
}
+ for fence in self.recycled_fences.get_mut() {
+ unsafe { self.device_fn.destroy_fence(device, *fence, None) }
+ }
+
for semaphore in self
.recycled_semaphores
.get_mut()
use crate::{
backend::vulkan::{vk_vec, vulkan_format, VulkanImageHolder, VulkanImageSwapchain},
- delay_queue::DelayQueue,
vk_check, Frame, Image, ImageFormat, SwapchainOutOfDateError,
};
-use super::{SwapchainDestroyQueue, VulkanDevice, VulkanFrame, VULKAN_CONSTANTS};
+use super::{VulkanDevice, VulkanFrame, VULKAN_CONSTANTS};
#[derive(Default)]
struct VulkanPresentInfo {
capabilities: vk::SurfaceCapabilitiesKHR,
}
-#[derive(Default)]
+#[derive(Default, Clone, Copy, Debug)]
pub struct VulkanWsiSupport {
wayland: bool,
xlib: bool,
xcb: bool,
+ surface_maintenance1: bool,
+ swapchain_maintenance1: bool,
+}
+
+struct RecycleSwapchainSemaphore {
+ fence: vk::Fence,
+ semaphore: vk::Semaphore,
+ swapchain: vk::SwapchainKHR,
+}
+
+struct DestroySwapchain {
+ swapchain: vk::SwapchainKHR,
+ surface: vk::SurfaceKHR,
+ image_views: Box<[vk::ImageView]>,
}
pub struct VulkanWsi {
+ support: VulkanWsiSupport,
+
surfaces: Mutex<HashMap<RawWindow, vk::SurfaceKHR>>,
swapchains: Mutex<HashMap<vk::SurfaceKHR, VulkanSwapchain>>,
suboptimal_swapchains: Mutex<HashSet<vk::SwapchainKHR>>,
- swapchain_destroy_queue: Mutex<SwapchainDestroyQueue>,
+ recycle_swapchain_semaphores: Mutex<Vec<RecycleSwapchainSemaphore>>,
+ destroy_swapchains: Mutex<Vec<DestroySwapchain>>,
xcb_surface_fn: Option<vk::XcbSurfaceKHRFunctions>,
xlib_surface_fn: Option<vk::XlibSurfaceKHRFunctions>,
pub fn check_instance_extensions<'a>(
extension_properties: &'a [vk::ExtensionProperties],
enabled_extensions: &mut Vec<&'a CStr>,
- ) -> VulkanWsiSupport {
- let mut wsi_support: VulkanWsiSupport = default();
-
+ wsi_support: &mut VulkanWsiSupport,
+ ) {
for extension in extension_properties {
let extension_name = CStr::from_bytes_until_nul(&extension.extension_name).unwrap();
match extension_name.to_str().unwrap() {
+ "VK_EXT_surface_maintenance1" => {
+ wsi_support.surface_maintenance1 = true;
+ enabled_extensions.push(extension_name);
+ }
"VK_KHR_wayland_surface" => {
wsi_support.wayland = true;
enabled_extensions.push(extension_name);
if wsi_support.wayland || wsi_support.xlib || wsi_support.xcb {
enabled_extensions.push(cstr!("VK_KHR_surface"));
}
-
- wsi_support
}
/// Check available WSI device extensions, and append required extensions to
pub fn check_device_extensions<'a>(
extension_properties: &'a [vk::ExtensionProperties],
enabled_extensions: &mut Vec<&'a CStr>,
+ wsi_support: &mut VulkanWsiSupport,
) {
+ let mut khr_swapchain_support = false;
for extension in extension_properties {
let extension_name = CStr::from_bytes_until_nul(&extension.extension_name).unwrap();
- if extension_name.to_str().unwrap() == "VK_KHR_swapchain" {
- enabled_extensions.push(extension_name);
- return;
+ match extension_name.to_str().unwrap() {
+ "VK_KHR_swapchain" => {
+ khr_swapchain_support = true;
+ enabled_extensions.push(extension_name);
+ }
+ "VK_EXT_swapchain_maintenance1" => {
+ wsi_support.swapchain_maintenance1 = true;
+ enabled_extensions.push(extension_name);
+ }
+ _ => {}
}
}
-
- panic!("VK_KHR_swapchain not supported")
+ assert!(khr_swapchain_support);
}
pub fn new(
global_fn: &vk::GlobalFunctions,
instance: vk::Instance,
- wsi_support: &VulkanWsiSupport,
+ support: VulkanWsiSupport,
) -> Self {
- let xcb_surface_fn = if wsi_support.xcb {
+ let xcb_surface_fn = if support.xcb {
Some(vk::XcbSurfaceKHRFunctions::new(global_fn, instance))
} else {
None
};
- let xlib_surface_fn = if wsi_support.xlib {
+ let xlib_surface_fn = if support.xlib {
Some(vk::XlibSurfaceKHRFunctions::new(global_fn, instance))
} else {
None
};
- let wayland_surface_fn = if wsi_support.wayland {
+ let wayland_surface_fn = if support.wayland {
Some(vk::WaylandSurfaceKHRFunctions::new(global_fn, instance))
} else {
None
let swapchain_fn = vk::SwapchainKHRFunctions::new(global_fn, instance, vk::VERSION_1_1);
VulkanWsi {
+ support,
+
surfaces: default(),
swapchains: default(),
suboptimal_swapchains: default(),
- swapchain_destroy_queue: Mutex::new(DelayQueue::new(
- VULKAN_CONSTANTS.swapchain_destroy_delay,
- )),
+
+ recycle_swapchain_semaphores: default(),
+ destroy_swapchains: default(),
+
xcb_surface_fn,
xlib_surface_fn,
wayland_surface_fn,
swapchain,
image_views,
} => {
- let destroy_image_views =
+ let detach_image_views =
|images: &mut Pool<VulkanImageHolder>| -> Box<[vk::ImageView]> {
let mut vulkan_image_views = Vec::new();
for &image_view in image_views.iter() {
|| height != *current_height
|| suboptimal_swapchains.remove(&swapchain)
{
- let image_views = destroy_image_views(&mut image_pool);
+ let image_views = detach_image_views(&mut image_pool);
old_swapchain = swapchain;
- self.wsi.swapchain_destroy_queue.lock().push((
- old_swapchain,
- vk::SurfaceKHR::null(),
+ self.wsi.destroy_swapchains.lock().push(DestroySwapchain {
+ swapchain: old_swapchain,
+ surface: vk::SurfaceKHR::null(),
image_views,
- ));
+ });
vulkan_swapchain.state = VulkanSwapchainState::Vacant;
continue;
suboptimal_swapchains.insert(swapchain);
}
vk::Result::ErrorOutOfDateKHR => {
- let image_views = destroy_image_views(&mut image_pool);
+ let image_views = detach_image_views(&mut image_pool);
old_swapchain = swapchain;
- self.wsi.swapchain_destroy_queue.lock().push((
- old_swapchain,
- vk::SurfaceKHR::null(),
+
+ self.wsi.destroy_swapchains.lock().push(DestroySwapchain {
+ swapchain: old_swapchain,
+ surface: vk::SurfaceKHR::null(),
image_views,
- ));
+ });
vulkan_swapchain.state = VulkanSwapchainState::Vacant;
return Err(SwapchainOutOfDateError(()));
}
}
- self.wsi.swapchain_destroy_queue.lock().push((
+ self.wsi.destroy_swapchains.lock().push(DestroySwapchain {
swapchain,
surface,
- vulkan_image_views.into_boxed_slice(),
- ));
+ image_views: vulkan_image_views.into_boxed_slice(),
+ });
}
}
}
!present_swapchain.acquire.is_null(),
"acquiring a swapchain image multiple times"
);
- present_swapchain.release = self.request_transient_semaphore(frame);
+ present_swapchain.release = self.request_semaphore();
wait_semaphores.push(vk::SemaphoreSubmitInfo {
semaphore: present_swapchain.acquire,
}
pub fn wsi_begin_frame(&self) {
- self.wsi
- .swapchain_destroy_queue
- .lock()
- .expire(|(swapchain, surface, image_views)| {
- self.destroy_swapchain_deferred(surface, swapchain, &image_views);
+ let mut recycle_swapchain_semaphores = self.wsi.recycle_swapchain_semaphores.lock();
+
+ if self.wsi.support.swapchain_maintenance1 {
+ recycle_swapchain_semaphores.retain(|recycle| {
+ // With VK_EXT_swapchain_maintenance1 we can just check the fence.
+ if unsafe {
+ self.device_fn.get_fence_status(self.device, recycle.fence)
+ == vk::Result::NotReady
+ } {
+ return true;
+ }
+
+ self.recycled_fences.lock().push_back(recycle.fence);
+ self.recycled_semaphores.lock().push_back(recycle.semaphore);
+
+ false
});
+ } else {
+ recycle_swapchain_semaphores.retain_mut(|recycle| {
+ // Without VK_EXT_swapchain_maintenance1 we use the fence to store a counter.
+ // When the counter hits zero we cleanup.
+ if !recycle.fence.is_null() {
+ recycle.fence = vk::Fence::from_raw(recycle.fence.as_raw() - 1);
+ return true;
+ }
+
+ self.recycled_semaphores.lock().push_back(recycle.semaphore);
+
+ false
+ });
+ }
+
+ self.wsi.destroy_swapchains.lock().retain(|destroy| {
+ let found_associated_semaphore = recycle_swapchain_semaphores
+ .iter()
+ .any(|recycle| destroy.swapchain == recycle.swapchain);
+
+ if !found_associated_semaphore {
+ self.destroy_swapchain_deferred(
+ destroy.surface,
+ destroy.swapchain,
+ &destroy.image_views,
+ );
+ }
+
+ found_associated_semaphore
+ });
}
pub fn wsi_end_frame(&self, frame: &mut VulkanFrame) {
);
}
+ let fences: &[_] = if self.wsi.support.swapchain_maintenance1 {
+ arena.alloc_slice_fill_with(presents.len(), |_| self.request_fence())
+ } else {
+ arena.alloc_slice_fill_with(presents.len(), |_| {
+ vk::Fence::from_raw(VULKAN_CONSTANTS.swapchain_semaphore_destroy_delay)
+ })
+ };
let wait_semaphores: &[_] = arena.alloc_slice_fill_iter(presents.iter().map(|x| x.release));
let swapchains: &[_] = arena.alloc_slice_fill_iter(presents.iter().map(|x| x.swapchain));
let image_indices: &[_] =
arena.alloc_slice_fill_iter(presents.iter().map(|x| x.image_index));
-
let results = arena.alloc_slice_fill_copy(swapchains.len(), vk::Result::Success);
+ for i in 0..presents.len() {
+ self.wsi
+ .recycle_swapchain_semaphores
+ .lock()
+ .push(RecycleSwapchainSemaphore {
+ fence: fences[i],
+ semaphore: wait_semaphores[i],
+ swapchain: swapchains[i],
+ });
+ }
+
+ let present_fence_info = vk::SwapchainPresentFenceInfoEXT {
+ fences: fences.into(),
+ ..default()
+ };
+
let present_info = vk::PresentInfoKHR {
+ _next: if self.wsi.support.swapchain_maintenance1 {
+ &present_fence_info as *const _ as *const core::ffi::c_void
+ } else {
+ core::ptr::null()
+ },
wait_semaphores: wait_semaphores.into(),
swapchains: (swapchains, image_indices).into(),
results: results.as_mut_ptr(),
}
pub fn wsi_drop(&mut self) {
+ for recyle in self.wsi.recycle_swapchain_semaphores.get_mut().drain(..) {
+ if self.wsi.support.swapchain_maintenance1 {
+ let fences = &[recyle.fence];
+ vk_check!(self.device_fn.wait_for_fences(
+ self.device,
+ fences,
+ vk::Bool32::True,
+ !0
+ ));
+ unsafe {
+ self.device_fn
+ .destroy_fence(self.device, recyle.fence, None)
+ };
+ }
+
+ unsafe {
+ self.device_fn
+ .destroy_semaphore(self.device, recyle.semaphore, None);
+ }
+ }
+
let destroyed_swapchains = self
.wsi
- .swapchain_destroy_queue
+ .destroy_swapchains
.get_mut()
.drain(..)
.collect::<Vec<_>>();
- for (_, (swapchain, surface, image_views)) in destroyed_swapchains {
- self.destroy_swapchain_deferred(surface, swapchain, &image_views);
+
+ for destroy in destroyed_swapchains {
+ self.destroy_swapchain_deferred(
+ destroy.surface,
+ destroy.swapchain,
+ &destroy.image_views,
+ );
}
for (&surface, swapchain) in self.wsi.swapchains.get_mut().iter() {