-use std::{
- collections::HashMap,
- ffi::{c_void, CStr, CString},
- mem::MaybeUninit,
- os::raw::c_char,
-};
+use std::{collections::HashMap, ffi::CString, mem::MaybeUninit, sync::Arc};
-use crate::{App, Button, Event, Key, ModifierFlags, PressedState, Window};
+use crate::{App, Button, Event, Key, ModifierFlags, PressedState, Window, WindowId};
-use narcissus_core::{Handle, Mutex, Pool};
+use narcissus_core::{
+ raw_window::{AsRawWindow, RawWindow, WaylandWindow, XlibWindow},
+ Mutex, Upcast,
+};
use sdl2_sys as sdl;
-struct SdlWindow(*mut sdl::Window);
+fn sdl_window_id(window_id: u32) -> WindowId {
+ WindowId(window_id as u64)
+}
+
+struct SdlWindow {
+ window: *mut sdl::Window,
+}
+
+impl Window for SdlWindow {
+ fn id(&self) -> WindowId {
+ sdl_window_id(unsafe { sdl::SDL_GetWindowID(self.window) })
+ }
+
+ fn extent(&self) -> (u32, u32) {
+ let mut width = 0;
+ let mut height = 0;
+ unsafe {
+ sdl::SDL_Vulkan_GetDrawableSize(self.window, &mut width, &mut height);
+ }
+ (width as u32, height as u32)
+ }
+}
+
+impl AsRawWindow for SdlWindow {
+ fn as_raw_window(&self) -> RawWindow {
+ let wm_info = unsafe {
+ let mut wm_info = MaybeUninit::<sdl::SysWMinfo>::zeroed();
+ std::ptr::write(
+ std::ptr::addr_of_mut!((*wm_info.as_mut_ptr()).version),
+ sdl::Version::current(),
+ );
+ let res = sdl::SDL_GetWindowWMInfo(self.window, wm_info.as_mut_ptr());
+ assert_eq!(res, sdl::Bool::True);
+ wm_info.assume_init()
+ };
+
+ match wm_info.subsystem {
+ sdl::SysWMType::X11 => RawWindow::Xlib(XlibWindow {
+ display: unsafe { wm_info.info.x11.display },
+ window: unsafe { wm_info.info.x11.window },
+ }),
+ sdl::SysWMType::WAYLAND => RawWindow::Wayland(WaylandWindow {
+ display: unsafe { wm_info.info.wayland.display },
+ surface: unsafe { wm_info.info.wayland.surface },
+ }),
+ _ => panic!("unspported wm system"),
+ }
+ }
+}
+
+impl Upcast<dyn AsRawWindow> for SdlWindow {
+ fn upcast(&self) -> &(dyn AsRawWindow + 'static) {
+ self
+ }
+}
pub struct SdlApp {
- windows: Mutex<Pool<SdlWindow>>,
- window_id_to_handle: Mutex<HashMap<u32, Window>>,
+ windows: Mutex<HashMap<WindowId, Arc<SdlWindow>>>,
}
impl SdlApp {
pub fn new() -> Result<Self, ()> {
unsafe { sdl::SDL_Init(sdl::INIT_VIDEO) };
Ok(Self {
- windows: Mutex::new(Pool::new()),
- window_id_to_handle: Mutex::new(HashMap::new()),
+ windows: Mutex::new(HashMap::new()),
})
}
-
- fn window_from_window_id(&self, window_id: u32) -> Window {
- self.window_id_to_handle
- .lock()
- .get(&window_id)
- .copied()
- .unwrap_or_else(|| Window(Handle::null()))
- }
}
impl Drop for SdlApp {
fn drop(&mut self) {
for window in self.windows.get_mut().values() {
- unsafe { sdl::SDL_DestroyWindow(window.0) };
+ unsafe { sdl::SDL_DestroyWindow(window.window) };
}
unsafe { sdl::SDL_Quit() };
}
}
impl App for SdlApp {
- fn create_window(&self, desc: &crate::WindowDesc) -> Window {
+ fn create_window(&self, desc: &crate::WindowDesc) -> Arc<dyn Window> {
let title = CString::new(desc.title).unwrap();
let window = unsafe {
sdl::SDL_CreateWindow(
)
};
assert!(!window.is_null());
- let window_id = unsafe { sdl::SDL_GetWindowID(window) };
-
- let mut window_id_to_handle = self.window_id_to_handle.lock();
- let mut windows = self.windows.lock();
-
- let handle = Window(windows.insert(SdlWindow(window)));
- window_id_to_handle.insert(window_id, handle);
- handle
+ let window_id = WindowId(unsafe { sdl::SDL_GetWindowID(window) } as u64);
+ let window = Arc::new(SdlWindow { window });
+ self.windows.lock().insert(window_id, window.clone());
+ window
}
- fn destroy_window(&self, window: Window) {
- if let Some(window) = self.windows.lock().remove(window.0) {
- unsafe { sdl::SDL_DestroyWindow(window.0) };
+ fn destroy_window(&self, window: Arc<dyn Window>) {
+ let window_id = window.id();
+ drop(window);
+ if let Some(mut window) = self.windows.lock().remove(&window_id) {
+ let window = Arc::get_mut(&mut window)
+ .expect("tried to destroy a window while there are outstanding references");
+ unsafe { sdl::SDL_DestroyWindow(window.window) };
}
}
- fn vk_get_loader(&self) -> *mut c_void {
- unsafe {
- sdl::SDL_Vulkan_LoadLibrary(std::ptr::null());
- sdl::SDL_Vulkan_GetVkGetInstanceProcAddr()
- }
- }
-
- fn vk_instance_extensions(&self) -> Vec<&'static CStr> {
- let mut count: u32 = 0;
- let ret = unsafe {
- sdl::SDL_Vulkan_GetInstanceExtensions(
- std::ptr::null_mut(),
- &mut count,
- std::ptr::null_mut(),
- )
- };
- assert_eq!(ret, 1, "failed to query instance extensions");
- if count == 0 {
- return Vec::new();
- }
-
- let mut names: Vec<*const c_char> = vec![std::ptr::null(); count as usize];
- let ret = unsafe {
- sdl::SDL_Vulkan_GetInstanceExtensions(
- std::ptr::null_mut(),
- &mut count,
- names.as_mut_ptr(),
- )
- };
- assert_eq!(ret, 1, "failed to query instance extensions");
-
- names
- .iter()
- .map(|&val| unsafe { CStr::from_ptr(val) })
- .collect()
- }
-
- fn vk_create_surface(&self, window: Window, instance: u64) -> u64 {
- let windows = self.windows.lock();
- let window = windows.get(window.0).unwrap();
- let mut surface = !0;
- let ret = unsafe { sdl::SDL_Vulkan_CreateSurface(window.0, instance, &mut surface) };
- assert_eq!(ret, sdl::Bool::True, "failed to create vulkan surface");
- surface
- }
-
- fn vk_get_surface_extent(&self, window: Window) -> (u32, u32) {
- let windows = self.windows.lock();
- let window = windows.get(window.0).unwrap();
- let mut w = 0;
- let mut h = 0;
- unsafe {
- sdl::SDL_Vulkan_GetDrawableSize(window.0, &mut w, &mut h);
- }
- (w as u32, h as u32)
- }
-
fn poll_event(&self) -> Option<Event> {
let mut event = MaybeUninit::uninit();
if unsafe { sdl::SDL_PollEvent(event.as_mut_ptr()) } == 0 {
sdl::WindowEventId::Hidden => Event::Unknown,
sdl::WindowEventId::Exposed => Event::Unknown,
sdl::WindowEventId::Moved => Event::Unknown,
- sdl::WindowEventId::Resized => {
- let handle = self.window_from_window_id(unsafe { event.window.window_id });
- Event::Resize {
- window: handle,
- width: unsafe { event.window.data1 } as u32,
- height: unsafe { event.window.data2 } as u32,
- }
- }
+ sdl::WindowEventId::Resized => Event::Resize {
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
+ width: unsafe { event.window.data1 } as u32,
+ height: unsafe { event.window.data2 } as u32,
+ },
sdl::WindowEventId::SizeChanged => Event::Unknown,
sdl::WindowEventId::Minimized => Event::Unknown,
sdl::WindowEventId::Maximized => Event::Unknown,
sdl::WindowEventId::Restored => Event::Unknown,
- sdl::WindowEventId::Enter => {
- let handle = self.window_from_window_id(unsafe { event.window.window_id });
- Event::MouseEnter {
- window: handle,
- x: unsafe { event.window.data1 },
- y: unsafe { event.window.data2 },
- }
- }
- sdl::WindowEventId::Leave => {
- let handle = self.window_from_window_id(unsafe { event.window.window_id });
- Event::MouseLeave {
- window: handle,
- x: unsafe { event.window.data1 },
- y: unsafe { event.window.data2 },
- }
- }
- sdl::WindowEventId::FocusGained => {
- let handle = self.window_from_window_id(unsafe { event.window.window_id });
- Event::FocusIn { window: handle }
- }
- sdl::WindowEventId::FocusLost => {
- let handle = self.window_from_window_id(unsafe { event.window.window_id });
- Event::FocusOut { window: handle }
- }
- sdl::WindowEventId::Close => {
- let handle = self.window_from_window_id(unsafe { event.window.window_id });
- Event::Close { window: handle }
- }
+ sdl::WindowEventId::Enter => Event::MouseEnter {
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
+ x: unsafe { event.window.data1 },
+ y: unsafe { event.window.data2 },
+ },
+ sdl::WindowEventId::Leave => Event::MouseLeave {
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
+ x: unsafe { event.window.data1 },
+ y: unsafe { event.window.data2 },
+ },
+ sdl::WindowEventId::FocusGained => Event::FocusIn {
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
+ },
+ sdl::WindowEventId::FocusLost => Event::FocusOut {
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
+ },
+ sdl::WindowEventId::Close => Event::Close {
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
+ },
sdl::WindowEventId::TakeFocus => Event::Unknown,
sdl::WindowEventId::HitTest => Event::Unknown,
sdl::WindowEventId::IccprofChanged => Event::Unknown,
sdl::WindowEventId::DisplayChanged => Event::Unknown,
},
sdl::EventType::KEYUP | sdl::EventType::KEYDOWN => {
- let handle = self.window_from_window_id(unsafe { event.key.window_id });
let scancode = unsafe { event.key.keysym.scancode };
let modifiers = unsafe { event.key.keysym.modifiers };
let state = unsafe { event.key.state };
let modifiers = map_sdl_modifiers(modifiers);
let pressed = map_sdl_pressed_state(state);
Event::KeyPress {
- window: handle,
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
key,
pressed,
modifiers,
}
}
sdl::EventType::MOUSEBUTTONUP | sdl::EventType::MOUSEBUTTONDOWN => {
- let handle = self.window_from_window_id(unsafe { event.button.window_id });
let button = unsafe { event.button.button };
let state = unsafe { event.button.state };
let button = map_sdl_button(button);
let pressed = map_sdl_pressed_state(state);
Event::ButtonPress {
- window: handle,
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
button,
pressed,
}
}
- sdl::EventType::MOUSEMOTION => {
- let handle = self.window_from_window_id(unsafe { event.window.window_id });
- Event::MouseMotion {
- window: handle,
- x: unsafe { event.window.data1 },
- y: unsafe { event.window.data2 },
- }
- }
+ sdl::EventType::MOUSEMOTION => Event::MouseMotion {
+ window_id: sdl_window_id(unsafe { event.window.window_id }),
+ x: unsafe { event.window.data1 },
+ y: unsafe { event.window.data2 },
+ },
_ => Event::Unknown,
};
Some(e)
}
+
+ fn window(&self, window_id: WindowId) -> Arc<dyn Window> {
+ self.windows.lock().get(&window_id).unwrap().clone()
+ }
}
fn map_sdl_button(button: sdl::MouseButton) -> Button {
use std::{
cell::UnsafeCell,
- collections::{hash_map, HashMap, VecDeque},
+ collections::{hash_map::Entry, HashMap, VecDeque},
marker::PhantomData,
os::raw::{c_char, c_void},
ptr::NonNull,
sync::atomic::{AtomicU64, AtomicUsize, Ordering},
};
-use narcissus_app::{App, Window};
use narcissus_core::{
- cstr, default, manual_arc, manual_arc::ManualArc, Arena, HybridArena, Mutex, PhantomUnsend,
- Pool,
+ cstr, cstr_from_bytes_until_nul, default, manual_arc,
+ manual_arc::ManualArc,
+ raw_window::{AsRawWindow, RawWindow},
+ Arena, HybridArena, Mutex, PhantomUnsend, Pool,
};
use vulkan_sys as vk;
ImageDimension, ImageFormat, ImageLayout, ImageSubresourceLayers, ImageSubresourceRange,
ImageUsageFlags, ImageViewDesc, IndexType, LoadOp, MemoryLocation, Offset2d, Offset3d,
Pipeline, PolygonMode, Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc,
- SamplerFilter, ShaderStageFlags, StencilOp, StencilOpState, StoreOp, ThreadToken, Topology,
- TypedBind,
+ SamplerFilter, ShaderStageFlags, StencilOp, StencilOpState, StoreOp, SwapchainOutOfDateError,
+ ThreadToken, Topology, TypedBind,
};
const NUM_FRAMES: usize = 2;
/// There's no correct answer here (spec bug) we're just picking a big number and hoping for the best.
const SWAPCHAIN_DESTROY_DELAY_FRAMES: usize = 8;
+mod libc {
+ use std::os::raw::{c_char, c_int, c_void};
+
+ pub const RTLD_NOW: c_int = 0x2;
+ pub const RTLD_LOCAL: c_int = 0;
+
+ extern "C" {
+ pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
+ pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
+ }
+}
+
macro_rules! vk_check {
($e:expr) => ({
#[allow(unused_unsafe)]
}
struct VulkanImageSwapchain {
- window: Window,
+ surface: vk::SurfaceKHR,
image: vk::Image,
view: vk::ImageView,
}
}
struct VulkanSwapchain {
- window: Window,
- surface: vk::SurfaceKHR,
surface_format: vk::SurfaceFormatKHR,
state: VulkanSwapchainState,
struct VulkanCmdBuffer {
command_buffer: vk::CommandBuffer,
bound_pipeline: Option<VulkanBoundPipeline>,
- swapchains_touched: HashMap<Window, (vk::Image, vk::PipelineStageFlags2)>,
+ swapchains_touched: HashMap<vk::SurfaceKHR, (vk::Image, vk::PipelineStageFlags2)>,
}
struct VulkanCmdBufferPool {
per_thread: GpuConcurrent<VulkanPerThread>,
- present_swapchains: Mutex<HashMap<Window, VulkanPresentInfo>>,
+ present_swapchains: Mutex<HashMap<vk::SurfaceKHR, VulkanPresentInfo>>,
destroyed_allocations: Mutex<VecDeque<VulkanMemory>>,
destroyed_buffers: Mutex<VecDeque<vk::Buffer>>,
}
}
-type SwapchainDestroyQueue = DelayQueue<(
- Window,
- vk::SwapchainKHR,
- vk::SurfaceKHR,
- Box<[vk::ImageView]>,
-)>;
-
-pub(crate) struct VulkanDevice<'app> {
- app: &'app dyn App,
+type SwapchainDestroyQueue = DelayQueue<(vk::SwapchainKHR, vk::SurfaceKHR, Box<[vk::ImageView]>)>;
+pub(crate) struct VulkanDevice {
instance: vk::Instance,
physical_device: vk::PhysicalDevice,
physical_device_memory_properties: Box<vk::PhysicalDeviceMemoryProperties>,
frame_counter: FrameCounter,
frames: Box<[UnsafeCell<VulkanFrame>; NUM_FRAMES]>,
- swapchains: Mutex<HashMap<Window, VulkanSwapchain>>,
+ surfaces: Mutex<HashMap<RawWindow, vk::SurfaceKHR>>,
+
+ swapchains: Mutex<HashMap<vk::SurfaceKHR, VulkanSwapchain>>,
destroyed_swapchains: Mutex<SwapchainDestroyQueue>,
image_pool: Mutex<Pool<VulkanImageHolder>>,
_global_fn: vk::GlobalFunctions,
instance_fn: vk::InstanceFunctions,
+ xcb_surface_fn: Option<vk::XcbSurfaceKHRFunctions>,
+ xlib_surface_fn: Option<vk::XlibSurfaceKHRFunctions>,
+ wayland_surface_fn: Option<vk::WaylandSurfaceKHRFunctions>,
surface_fn: vk::SurfaceKHRFunctions,
swapchain_fn: vk::SwapchainKHRFunctions,
device_fn: vk::DeviceFunctions,
}
-impl<'app> VulkanDevice<'app> {
- pub(crate) fn new(app: &'app dyn App) -> Self {
- let get_proc_addr = app.vk_get_loader();
+impl VulkanDevice {
+ pub(crate) fn new() -> Self {
+ let get_proc_addr = unsafe {
+ let module = libc::dlopen(
+ cstr!("libvulkan.so.1").as_ptr(),
+ libc::RTLD_NOW | libc::RTLD_LOCAL,
+ );
+ libc::dlsym(module, cstr!("vkGetInstanceProcAddr").as_ptr())
+ };
+
let global_fn = unsafe { vk::GlobalFunctions::new(get_proc_addr) };
let api_version = {
#[cfg(not(debug_assertions))]
let enabled_layers = &[];
- let enabled_extensions = app.vk_instance_extensions();
+ let extension_properties = vk_vec(|count, ptr| unsafe {
+ global_fn.enumerate_instance_extension_properties(std::ptr::null(), count, ptr)
+ });
+
+ let mut has_wayland_support = false;
+ let mut has_xlib_support = false;
+ let mut has_xcb_support = false;
+
+ let mut enabled_extensions = vec![];
+ for extension in &extension_properties {
+ let extension_name = cstr_from_bytes_until_nul(&extension.extension_name).unwrap();
+
+ match extension_name.to_str().unwrap() {
+ "VK_KHR_wayland_surface" => {
+ has_wayland_support = true;
+ enabled_extensions.push(extension_name);
+ }
+ "VK_KHR_xlib_surface" => {
+ has_xlib_support = true;
+ enabled_extensions.push(extension_name);
+ }
+ "VK_KHR_xcb_surface" => {
+ has_xcb_support = true;
+ enabled_extensions.push(extension_name);
+ }
+ _ => {}
+ }
+ }
+
+ // If we found any surface extensions, we need to additionally enable VK_KHR_surface.
+ if !enabled_extensions.is_empty() {
+ enabled_extensions.push(cstr!("VK_KHR_surface"));
+ }
+
let enabled_extensions = enabled_extensions
.iter()
.map(|x| x.as_ptr())
};
let instance_fn = vk::InstanceFunctions::new(&global_fn, instance, vk::VERSION_1_2);
+
+ let xcb_surface_fn = if has_xcb_support {
+ Some(vk::XcbSurfaceKHRFunctions::new(&global_fn, instance))
+ } else {
+ None
+ };
+
+ let xlib_surface_fn = if has_xlib_support {
+ Some(vk::XlibSurfaceKHRFunctions::new(&global_fn, instance))
+ } else {
+ None
+ };
+
+ let wayland_surface_fn = if has_wayland_support {
+ Some(vk::WaylandSurfaceKHRFunctions::new(&global_fn, instance))
+ } else {
+ None
+ };
+
let surface_fn = vk::SurfaceKHRFunctions::new(&global_fn, instance);
let swapchain_fn = vk::SwapchainKHRFunctions::new(&global_fn, instance, vk::VERSION_1_1);
}));
Self {
- app,
-
instance,
physical_device,
physical_device_memory_properties: Box::new(physical_device_memory_properties),
frame_counter: FrameCounter::new(),
frames,
- swapchains: Mutex::new(HashMap::new()),
+ surfaces: default(),
+ swapchains: default(),
destroyed_swapchains: Mutex::new(DelayQueue::new(SWAPCHAIN_DESTROY_DELAY_FRAMES)),
image_pool: default(),
_global_fn: global_fn,
instance_fn,
+ xcb_surface_fn,
+ xlib_surface_fn,
+ wayland_surface_fn,
surface_fn,
swapchain_fn,
device_fn,
}
}
- fn destroy_swapchain(
+ fn destroy_swapchain_deferred(
&self,
- window: Window,
surface: vk::SurfaceKHR,
swapchain: vk::SwapchainKHR,
image_views: &[vk::ImageView],
) {
- let app = self.app;
let device_fn = &self.device_fn;
let swapchain_fn = &self.swapchain_fn;
let surface_fn = &self.surface_fn;
if !surface.is_null() {
unsafe { surface_fn.destroy_surface(instance, surface, None) }
}
- if !window.is_null() {
- app.destroy_window(window);
- }
}
}
-impl<'driver> Device for VulkanDevice<'driver> {
+impl Device for VulkanDevice {
fn create_buffer(&self, desc: &BufferDesc) -> Buffer {
let mut usage = vk::BufferUsageFlags::default();
if desc.usage.contains(BufferUsageFlags::UNIFORM) {
}
}
- fn destroy_window(&self, window: Window) {
- if let Some(VulkanSwapchain {
- window: _,
- surface,
- surface_format: _,
- state,
- _formats: _,
- _present_modes: _,
- capabilities: _,
- }) = self.swapchains.lock().remove(&window)
- {
- let mut image_pool = self.image_pool.lock();
+ fn create_cmd_buffer(&self, frame: &Frame, thread_token: &mut ThreadToken) -> CmdBuffer {
+ let frame = self.frame(frame);
+ let per_thread = frame.per_thread.get_mut(thread_token);
+ let cmd_buffer_pool = &mut per_thread.cmd_buffer_pool;
- if let VulkanSwapchainState::Occupied {
- width: _,
- height: _,
- suboptimal: _,
- swapchain,
- image_views,
- } = state
- {
- let mut vulkan_image_views = Vec::new();
- for &image_view in image_views.iter() {
- match image_pool.remove(image_view.0) {
- Some(VulkanImageHolder::Swapchain(VulkanImageSwapchain {
- window: _,
- image: _,
- view,
- })) => vulkan_image_views.push(view),
- _ => panic!("swapchain image in wrong state"),
- }
- }
+ // We have consumed all available command buffers, need to allocate a new one.
+ if cmd_buffer_pool.next_free_index >= cmd_buffer_pool.command_buffers.len() {
+ let mut cmd_buffers = [vk::CommandBuffer::null(); 4];
+ let allocate_info = vk::CommandBufferAllocateInfo {
+ command_pool: cmd_buffer_pool.command_pool,
+ level: vk::CommandBufferLevel::Primary,
+ command_buffer_count: cmd_buffers.len() as u32,
+ ..default()
+ };
+ vk_check!(self.device_fn.allocate_command_buffers(
+ self.device,
+ &allocate_info,
+ cmd_buffers.as_mut_ptr()
+ ));
+ cmd_buffer_pool.command_buffers.extend(cmd_buffers.iter());
+ }
- self.destroyed_swapchains.lock().push((
- window,
- swapchain,
- surface,
- vulkan_image_views.into_boxed_slice(),
- ));
+ let index = cmd_buffer_pool.next_free_index;
+ cmd_buffer_pool.next_free_index += 1;
+ let command_buffer = cmd_buffer_pool.command_buffers[index];
+
+ vk_check!(self.device_fn.begin_command_buffer(
+ command_buffer,
+ &vk::CommandBufferBeginInfo {
+ flags: vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT,
+ ..default()
}
+ ));
+
+ let vulkan_cmd_buffer = per_thread.arena.alloc(VulkanCmdBuffer {
+ command_buffer,
+ bound_pipeline: None,
+ swapchains_touched: HashMap::new(),
+ });
+
+ CmdBuffer {
+ cmd_buffer_addr: vulkan_cmd_buffer as *mut _ as usize,
+ _phantom: &PhantomData,
+ phantom_unsend: PhantomUnsend {},
}
}
- fn acquire_swapchain(
+ fn cmd_barrier(
&self,
- frame: &Frame,
- window: Window,
- format: ImageFormat,
- ) -> (u32, u32, Image) {
- let format = vulkan_format(format);
-
- let mut swapchains = self.swapchains.lock();
- let mut vulkan_swapchain = swapchains.entry(window).or_insert_with(|| {
- let surface = self.app.vk_create_surface(window, self.instance.as_raw());
- let surface = vk::SurfaceKHR::from_raw(surface);
-
- let mut supported = vk::Bool32::False;
- vk_check!(self.surface_fn.get_physical_device_surface_support(
- self.physical_device,
- self.universal_queue_family_index,
- surface,
- &mut supported
- ));
-
- assert_eq!(
- supported,
- vk::Bool32::True,
- "universal queue does not support presenting this surface"
- );
+ cmd_buffer: &mut CmdBuffer,
+ global_barrier: Option<&GlobalBarrier>,
+ image_barriers: &[ImageBarrier],
+ ) {
+ let arena = HybridArena::<4096>::new();
- let formats = vk_vec(|count, ptr| unsafe {
- self.surface_fn.get_physical_device_surface_formats(
- self.physical_device,
- surface,
- count,
- ptr,
- )
- })
- .into_boxed_slice();
+ let memory_barriers = arena.alloc_slice_fill_iter(
+ global_barrier
+ .iter()
+ .map(|global_barrier| vulkan_memory_barrier(global_barrier)),
+ );
- let present_modes = vk_vec(|count, ptr| unsafe {
- self.surface_fn.get_physical_device_surface_present_modes(
- self.physical_device,
- surface,
- count,
- ptr,
- )
- })
- .into_boxed_slice();
+ let image_memory_barriers =
+ arena.alloc_slice_fill_iter(image_barriers.iter().map(|image_barrier| {
+ let image = self
+ .image_pool
+ .lock()
+ .get(image_barrier.image.0)
+ .expect("invalid image handle")
+ .image();
+ let subresource_range = vulkan_subresource_range(&image_barrier.subresource_range);
+ vulkan_image_memory_barrier(image_barrier, image, subresource_range)
+ }));
- let mut capabilities = vk::SurfaceCapabilitiesKHR::default();
- vk_check!(self.surface_fn.get_physical_device_surface_capabilities(
- self.physical_device,
- surface,
- &mut capabilities
- ));
+ let command_buffer = self.cmd_buffer_mut(cmd_buffer).command_buffer;
+ unsafe {
+ self.device_fn.cmd_pipeline_barrier2(
+ command_buffer,
+ &vk::DependencyInfo {
+ memory_barriers: memory_barriers.into(),
+ image_memory_barriers: image_memory_barriers.into(),
+ ..default()
+ },
+ )
+ }
+ }
- let surface_format = formats
- .iter()
- .copied()
- .find(|&x| x.format == format)
- .expect("failed to find matching surface format");
+ fn cmd_copy_buffer_to_image(
+ &self,
+ cmd_buffer: &mut CmdBuffer,
+ src_buffer: Buffer,
+ dst_image: Image,
+ dst_image_layout: ImageLayout,
+ copies: &[BufferImageCopy],
+ ) {
+ let arena = HybridArena::<4096>::new();
- VulkanSwapchain {
- window,
- surface,
- surface_format,
- state: VulkanSwapchainState::Vacant,
- _formats: formats,
- _present_modes: present_modes,
- capabilities,
- }
- });
+ let regions = arena.alloc_slice_fill_iter(copies.iter().map(|copy| vk::BufferImageCopy {
+ buffer_offset: copy.buffer_offset,
+ buffer_row_length: copy.buffer_row_length,
+ buffer_image_height: copy.buffer_image_height,
+ image_subresource: vulkan_subresource_layers(©.image_subresource_layers),
+ image_offset: copy.image_offset.into(),
+ image_extent: copy.image_extent.into(),
+ }));
- assert_eq!(format, vulkan_swapchain.surface_format.format);
+ let src_buffer = self
+ .buffer_pool
+ .lock()
+ .get(src_buffer.0)
+ .expect("invalid buffer handle")
+ .buffer;
- let frame = self.frame(frame);
- let mut image_pool = self.image_pool.lock();
+ let dst_image = self
+ .image_pool
+ .lock()
+ .get(dst_image.0)
+ .expect("invalid image handle")
+ .image();
- let mut present_swapchains = frame.present_swapchains.lock();
- let present_info = match present_swapchains.entry(window) {
- hash_map::Entry::Occupied(_) => {
- panic!("attempting to acquire the same swapchain multiple times in a frame")
- }
- hash_map::Entry::Vacant(entry) => entry.insert(default()),
+ let dst_image_layout = match dst_image_layout {
+ ImageLayout::Optimal => vk::ImageLayout::TransferDstOptimal,
+ ImageLayout::General => vk::ImageLayout::General,
};
- let mut old_swapchain = vk::SwapchainKHR::null();
- let mut iters = 0;
-
- loop {
- iters += 1;
- if iters > 10 {
- panic!("acquiring swapchain image took more than 10 tries");
- }
-
- let (desired_width, desired_height) =
- self.app.vk_get_surface_extent(vulkan_swapchain.window);
-
- vk_check!(self.surface_fn.get_physical_device_surface_capabilities(
- self.physical_device,
- vulkan_swapchain.surface,
- &mut vulkan_swapchain.capabilities
- ));
-
- let desired_width = desired_width.clamp(
- vulkan_swapchain.capabilities.min_image_extent.width,
- vulkan_swapchain.capabilities.max_image_extent.width,
- );
- let desired_height = desired_height.clamp(
- vulkan_swapchain.capabilities.min_image_extent.height,
- vulkan_swapchain.capabilities.max_image_extent.height,
- );
-
- match &mut vulkan_swapchain.state {
- VulkanSwapchainState::Vacant => {
- let image_extent = vk::Extent2d {
- width: desired_width,
- height: desired_height,
- };
- let mut new_swapchain = vk::SwapchainKHR::null();
- let create_info = vk::SwapchainCreateInfoKHR {
- surface: vulkan_swapchain.surface,
- min_image_count: vulkan_swapchain.capabilities.min_image_count,
- image_format: vulkan_swapchain.surface_format.format,
- image_color_space: vulkan_swapchain.surface_format.color_space,
- image_extent,
- image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT,
- image_array_layers: 1,
- image_sharing_mode: vk::SharingMode::Exclusive,
- pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
- composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
- present_mode: vk::PresentModeKHR::Fifo,
- clipped: vk::Bool32::True,
- old_swapchain,
- ..default()
- };
- vk_check!(self.swapchain_fn.create_swapchain(
- self.device,
- &create_info,
- None,
- &mut new_swapchain
- ));
- assert!(!new_swapchain.is_null());
-
- let images = vk_vec(|count, ptr| unsafe {
- self.swapchain_fn.get_swapchain_images(
- self.device,
- new_swapchain,
- count,
- ptr,
- )
- });
-
- let image_views = images
- .iter()
- .map(|&image| {
- let create_info = vk::ImageViewCreateInfo {
- image,
- view_type: vk::ImageViewType::Type2d,
- format: vulkan_swapchain.surface_format.format,
- subresource_range: vk::ImageSubresourceRange {
- aspect_mask: vk::ImageAspectFlags::COLOR,
- base_mip_level: 0,
- level_count: 1,
- base_array_layer: 0,
- layer_count: 1,
- },
- ..default()
- };
- let mut view = vk::ImageView::null();
- vk_check!(self.device_fn.create_image_view(
- self.device,
- &create_info,
- None,
- &mut view,
- ));
-
- let handle = image_pool.insert(VulkanImageHolder::Swapchain(
- VulkanImageSwapchain {
- window,
- image,
- view,
- },
- ));
- Image(handle)
- })
- .collect::<Box<_>>();
-
- vulkan_swapchain.state = VulkanSwapchainState::Occupied {
- width: image_extent.width,
- height: image_extent.height,
- suboptimal: false,
- swapchain: new_swapchain,
- image_views,
- };
-
- continue;
- }
- VulkanSwapchainState::Occupied {
- width,
- height,
- suboptimal,
- swapchain,
- image_views,
- } => {
- let destroy_image_views =
- |images: &mut Pool<VulkanImageHolder>| -> Box<[vk::ImageView]> {
- let mut vulkan_image_views = Vec::new();
- for &image_view in image_views.iter() {
- match images.remove(image_view.0) {
- Some(VulkanImageHolder::Swapchain(VulkanImageSwapchain {
- window: _,
- image: _,
- view,
- })) => vulkan_image_views.push(view),
- _ => panic!("swapchain image in wrong state"),
- }
- }
- vulkan_image_views.into_boxed_slice()
- };
-
- if *width != desired_width || *height != desired_height || *suboptimal {
- let image_views = destroy_image_views(&mut image_pool);
- old_swapchain = *swapchain;
- if !old_swapchain.is_null() {
- self.destroyed_swapchains.lock().push((
- Window::default(),
- old_swapchain,
- vk::SurfaceKHR::null(),
- image_views,
- ));
- }
- vulkan_swapchain.state = VulkanSwapchainState::Vacant;
- continue;
- }
-
- let acquire = self.request_transient_semaphore(frame);
- let mut image_index = 0;
- match unsafe {
- self.swapchain_fn.acquire_next_image2(
- self.device,
- &vk::AcquireNextImageInfoKHR {
- swapchain: *swapchain,
- timeout: !0,
- semaphore: acquire,
- fence: vk::Fence::null(),
- device_mask: 1,
- ..default()
- },
- &mut image_index,
- )
- } {
- vk::Result::Success => {}
- vk::Result::SuboptimalKHR => {
- *suboptimal = true;
- }
- vk::Result::ErrorOutOfDateKHR => {
- old_swapchain = *swapchain;
- let image_views = destroy_image_views(&mut image_pool);
- if !old_swapchain.is_null() {
- self.destroyed_swapchains.lock().push((
- Window::default(),
- old_swapchain,
- vk::SurfaceKHR::null(),
- image_views,
- ));
- }
- vulkan_swapchain.state = VulkanSwapchainState::Vacant;
- continue;
- }
- result => vk_check!(result),
- }
-
- present_info.acquire = acquire;
- present_info.image_index = image_index;
- present_info.swapchain = *swapchain;
- let view = image_views[image_index as usize];
-
- return (*width, *height, view);
- }
- }
- }
- }
-
- fn create_cmd_buffer(&self, frame: &Frame, thread_token: &mut ThreadToken) -> CmdBuffer {
- let frame = self.frame(frame);
- let per_thread = frame.per_thread.get_mut(thread_token);
- let cmd_buffer_pool = &mut per_thread.cmd_buffer_pool;
-
- // We have consumed all available command buffers, need to allocate a new one.
- if cmd_buffer_pool.next_free_index >= cmd_buffer_pool.command_buffers.len() {
- let mut cmd_buffers = [vk::CommandBuffer::null(); 4];
- let allocate_info = vk::CommandBufferAllocateInfo {
- command_pool: cmd_buffer_pool.command_pool,
- level: vk::CommandBufferLevel::Primary,
- command_buffer_count: cmd_buffers.len() as u32,
- ..default()
- };
- vk_check!(self.device_fn.allocate_command_buffers(
- self.device,
- &allocate_info,
- cmd_buffers.as_mut_ptr()
- ));
- cmd_buffer_pool.command_buffers.extend(cmd_buffers.iter());
- }
-
- let index = cmd_buffer_pool.next_free_index;
- cmd_buffer_pool.next_free_index += 1;
- let command_buffer = cmd_buffer_pool.command_buffers[index];
-
- vk_check!(self.device_fn.begin_command_buffer(
- command_buffer,
- &vk::CommandBufferBeginInfo {
- flags: vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT,
- ..default()
- }
- ));
-
- let vulkan_cmd_buffer = per_thread.arena.alloc(VulkanCmdBuffer {
- command_buffer,
- bound_pipeline: None,
- swapchains_touched: HashMap::new(),
- });
-
- CmdBuffer {
- cmd_buffer_addr: vulkan_cmd_buffer as *mut _ as usize,
- _phantom: &PhantomData,
- phantom_unsend: PhantomUnsend {},
- }
- }
-
- fn cmd_barrier(
- &self,
- cmd_buffer: &mut CmdBuffer,
- global_barrier: Option<&GlobalBarrier>,
- image_barriers: &[ImageBarrier],
- ) {
- let arena = HybridArena::<4096>::new();
-
- let memory_barriers = arena.alloc_slice_fill_iter(
- global_barrier
- .iter()
- .map(|global_barrier| vulkan_memory_barrier(global_barrier)),
- );
-
- let image_memory_barriers =
- arena.alloc_slice_fill_iter(image_barriers.iter().map(|image_barrier| {
- let image = self
- .image_pool
- .lock()
- .get(image_barrier.image.0)
- .expect("invalid image handle")
- .image();
- let subresource_range = vulkan_subresource_range(&image_barrier.subresource_range);
- vulkan_image_memory_barrier(image_barrier, image, subresource_range)
- }));
-
- let command_buffer = self.cmd_buffer_mut(cmd_buffer).command_buffer;
- unsafe {
- self.device_fn.cmd_pipeline_barrier2(
- command_buffer,
- &vk::DependencyInfo {
- memory_barriers: memory_barriers.into(),
- image_memory_barriers: image_memory_barriers.into(),
- ..default()
- },
- )
- }
- }
-
- fn cmd_copy_buffer_to_image(
- &self,
- cmd_buffer: &mut CmdBuffer,
- src_buffer: Buffer,
- dst_image: Image,
- dst_image_layout: ImageLayout,
- copies: &[BufferImageCopy],
- ) {
- let arena = HybridArena::<4096>::new();
-
- let regions = arena.alloc_slice_fill_iter(copies.iter().map(|copy| vk::BufferImageCopy {
- buffer_offset: copy.buffer_offset,
- buffer_row_length: copy.buffer_row_length,
- buffer_image_height: copy.buffer_image_height,
- image_subresource: vulkan_subresource_layers(©.image_subresource_layers),
- image_offset: copy.image_offset.into(),
- image_extent: copy.image_extent.into(),
- }));
-
- let src_buffer = self
- .buffer_pool
- .lock()
- .get(src_buffer.0)
- .expect("invalid buffer handle")
- .buffer;
-
- let dst_image = self
- .image_pool
- .lock()
- .get(dst_image.0)
- .expect("invalid image handle")
- .image();
-
- let dst_image_layout = match dst_image_layout {
- ImageLayout::Optimal => vk::ImageLayout::TransferDstOptimal,
- ImageLayout::General => vk::ImageLayout::General,
- };
-
- let command_buffer = self.cmd_buffer_mut(cmd_buffer).command_buffer;
- unsafe {
- self.device_fn.cmd_copy_buffer_to_image(
- command_buffer,
- src_buffer,
- dst_image,
- dst_image_layout,
- regions,
- )
- }
- }
+ let command_buffer = self.cmd_buffer_mut(cmd_buffer).command_buffer;
+ unsafe {
+ self.device_fn.cmd_copy_buffer_to_image(
+ command_buffer,
+ src_buffer,
+ dst_image,
+ dst_image_layout,
+ regions,
+ )
+ }
+ }
fn cmd_set_bind_group(
&self,
VulkanImageHolder::Shared(image) => image.view,
VulkanImageHolder::Swapchain(image) => {
assert!(
- !cmd_buffer.swapchains_touched.contains_key(&image.window),
+ !cmd_buffer.swapchains_touched.contains_key(&image.surface),
"swapchain attached multiple times in a command buffer"
);
cmd_buffer.swapchains_touched.insert(
- image.window,
+ image.surface,
(
image.image,
vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT,
per_thread.arena.reset()
}
- self.recycled_semaphores
- .lock()
- .extend(frame.recycled_semaphores.get_mut().drain(..));
+ self.recycled_semaphores
+ .lock()
+ .extend(frame.recycled_semaphores.get_mut().drain(..));
+
+ for descriptor_pool in frame.recycled_descriptor_pools.get_mut() {
+ vk_check!(device_fn.reset_descriptor_pool(
+ device,
+ *descriptor_pool,
+ vk::DescriptorPoolResetFlags::default()
+ ))
+ }
+
+ self.recycled_descriptor_pools
+ .lock()
+ .extend(frame.recycled_descriptor_pools.get_mut().drain(..));
+
+ Self::destroy_deferred(device_fn, device, frame);
+
+ self.destroyed_swapchains
+ .lock()
+ .expire(|(swapchain, surface, image_views)| {
+ self.destroy_swapchain_deferred(surface, swapchain, &image_views);
+ });
+ }
+
+ frame
+ }
+
+ fn end_frame(&self, mut frame: Frame) {
+ let arena = HybridArena::<512>::new();
+
+ {
+ let frame = self.frame_mut(&mut frame);
+
+ let present_swapchains = frame.present_swapchains.get_mut();
+ if !present_swapchains.is_empty() {
+ let windows = arena.alloc_slice_fill_iter(present_swapchains.keys().copied());
+ let wait_semaphores =
+ arena.alloc_slice_fill_iter(present_swapchains.values().map(|x| x.release));
+ let swapchains =
+ arena.alloc_slice_fill_iter(present_swapchains.values().map(|x| x.swapchain));
+ let swapchain_image_indices =
+ arena.alloc_slice_fill_iter(present_swapchains.values().map(|x| x.image_index));
+
+ present_swapchains.clear();
+
+ let results = arena.alloc_slice_fill_copy(swapchains.len(), vk::Result::Success);
+
+ let present_info = vk::PresentInfoKHR {
+ wait_semaphores: wait_semaphores.into(),
+ swapchains: (swapchains, swapchain_image_indices).into(),
+ results: results.as_mut_ptr(),
+ ..default()
+ };
+
+ unsafe {
+ // check results below, so ignore this return value.
+ let _ = self
+ .swapchain_fn
+ .queue_present(self.universal_queue, &present_info);
+ };
+
+ for (i, &result) in results.iter().enumerate() {
+ match result {
+ vk::Result::Success => {}
+ vk::Result::SuboptimalKHR => {
+ // Yikes
+ if let VulkanSwapchainState::Occupied {
+ width: _,
+ height: _,
+ suboptimal,
+ swapchain: _,
+ image_views: _,
+ } = &mut self.swapchains.lock().get_mut(&windows[i]).unwrap().state
+ {
+ *suboptimal = true;
+ }
+ }
+ _ => vk_check!(result),
+ }
+ }
+ }
+ }
+
+ self.frame_counter.release(frame);
+ }
+
+ unsafe fn map_buffer(&self, buffer: Buffer) -> *mut u8 {
+ let mut ptr = std::ptr::null_mut();
+ if let Some(buffer) = self.buffer_pool.lock().get(buffer.0) {
+ vk_check!(self.device_fn.map_memory(
+ self.device,
+ buffer.memory.memory,
+ buffer.memory.offset,
+ buffer.memory.size,
+ vk::MemoryMapFlags::default(),
+ &mut ptr
+ ))
+ }
+ std::mem::transmute::<*mut c_void, *mut u8>(ptr)
+ }
+
+ unsafe fn unmap_buffer(&self, buffer: Buffer) {
+ if let Some(buffer) = self.buffer_pool.lock().get(buffer.0) {
+ self.device_fn
+ .unmap_memory(self.device, buffer.memory.memory)
+ }
+ }
+
+ fn acquire_swapchain(
+ &self,
+ frame: &Frame,
+ window: &dyn AsRawWindow,
+ width: u32,
+ height: u32,
+ format: ImageFormat,
+ ) -> Result<(u32, u32, Image), SwapchainOutOfDateError> {
+ let raw_window = window.as_raw_window();
+ let mut surfaces = self.surfaces.lock();
+ let surface = surfaces
+ .entry(raw_window)
+ .or_insert_with(|| match raw_window {
+ RawWindow::Xcb(xcb) => {
+ let create_info = vk::XcbSurfaceCreateInfoKHR {
+ connection: xcb.connection,
+ window: xcb.window,
+ ..default()
+ };
+ let mut surface = vk::SurfaceKHR::null();
+ vk_check!(self.xcb_surface_fn.as_ref().unwrap().create_xcb_surface(
+ self.instance,
+ &create_info,
+ None,
+ &mut surface,
+ ));
+ surface
+ }
+ RawWindow::Xlib(xlib) => {
+ let create_info = vk::XlibSurfaceCreateInfoKHR {
+ display: xlib.display,
+ window: xlib.window,
+ ..default()
+ };
+ let mut surface = vk::SurfaceKHR::null();
+ vk_check!(self.xlib_surface_fn.as_ref().unwrap().create_xlib_surface(
+ self.instance,
+ &create_info,
+ None,
+ &mut surface,
+ ));
+ surface
+ }
+ RawWindow::Wayland(wayland) => {
+ let create_info = vk::WaylandSurfaceCreateInfoKHR {
+ display: wayland.display,
+ surface: wayland.surface,
+ ..default()
+ };
+ let mut surface = vk::SurfaceKHR::null();
+ vk_check!(self
+ .wayland_surface_fn
+ .as_ref()
+ .unwrap()
+ .create_wayland_surface(self.instance, &create_info, None, &mut surface,));
+ surface
+ }
+ });
+ self.acquire_swapchain(frame, *surface, width, height, format)
+ }
+
+ fn destroy_swapchain(&self, window: &dyn AsRawWindow) {
+ let raw_window = window.as_raw_window();
+ if let Some(surface) = self.surfaces.lock().remove(&raw_window) {
+ self.destroy_swapchain(surface)
+ }
+ }
+}
+
+impl VulkanDevice {
+ fn acquire_swapchain(
+ &self,
+ frame: &Frame,
+ surface: vk::SurfaceKHR,
+ width: u32,
+ height: u32,
+ format: ImageFormat,
+ ) -> Result<(u32, u32, Image), SwapchainOutOfDateError> {
+ let format = vulkan_format(format);
+
+ let mut swapchains = self.swapchains.lock();
+ let mut vulkan_swapchain = swapchains.entry(surface).or_insert_with(|| {
+ let mut supported = vk::Bool32::False;
+ vk_check!(self.surface_fn.get_physical_device_surface_support(
+ self.physical_device,
+ self.universal_queue_family_index,
+ surface,
+ &mut supported
+ ));
+
+ assert_eq!(
+ supported,
+ vk::Bool32::True,
+ "universal queue does not support presenting this surface"
+ );
+
+ let formats = vk_vec(|count, ptr| unsafe {
+ self.surface_fn.get_physical_device_surface_formats(
+ self.physical_device,
+ surface,
+ count,
+ ptr,
+ )
+ })
+ .into_boxed_slice();
+
+ let present_modes = vk_vec(|count, ptr| unsafe {
+ self.surface_fn.get_physical_device_surface_present_modes(
+ self.physical_device,
+ surface,
+ count,
+ ptr,
+ )
+ })
+ .into_boxed_slice();
+
+ let mut capabilities = vk::SurfaceCapabilitiesKHR::default();
+ vk_check!(self.surface_fn.get_physical_device_surface_capabilities(
+ self.physical_device,
+ surface,
+ &mut capabilities
+ ));
+
+ let surface_format = formats
+ .iter()
+ .copied()
+ .find(|&x| x.format == format)
+ .expect("failed to find matching surface format");
+
+ VulkanSwapchain {
+ surface_format,
+ state: VulkanSwapchainState::Vacant,
+ _formats: formats,
+ _present_modes: present_modes,
+ capabilities,
+ }
+ });
+
+ assert_eq!(format, vulkan_swapchain.surface_format.format);
- for descriptor_pool in frame.recycled_descriptor_pools.get_mut() {
- vk_check!(device_fn.reset_descriptor_pool(
- device,
- *descriptor_pool,
- vk::DescriptorPoolResetFlags::default()
- ))
- }
+ let frame = self.frame(frame);
+ let mut image_pool = self.image_pool.lock();
- self.recycled_descriptor_pools
- .lock()
- .extend(frame.recycled_descriptor_pools.get_mut().drain(..));
+ let mut present_swapchains = frame.present_swapchains.lock();
+ let present_info = match present_swapchains.entry(surface) {
+ Entry::Occupied(_) => panic!("acquiring swapchain multiple times in a frame"),
+ Entry::Vacant(entry) => entry.insert(default()),
+ };
- Self::destroy_deferred(device_fn, device, frame);
+ vk_check!(self.surface_fn.get_physical_device_surface_capabilities(
+ self.physical_device,
+ surface,
+ &mut vulkan_swapchain.capabilities
+ ));
- self.destroyed_swapchains
- .lock()
- .expire(|(window, swapchain, surface, image_views)| {
- self.destroy_swapchain(window, surface, swapchain, &image_views);
- });
- }
+ let width = width.clamp(
+ vulkan_swapchain.capabilities.min_image_extent.width,
+ vulkan_swapchain.capabilities.max_image_extent.width,
+ );
+ let height = height.clamp(
+ vulkan_swapchain.capabilities.min_image_extent.height,
+ vulkan_swapchain.capabilities.max_image_extent.height,
+ );
- frame
- }
+ let mut old_swapchain = vk::SwapchainKHR::null();
+ loop {
+ match &mut vulkan_swapchain.state {
+ VulkanSwapchainState::Vacant => {
+ let mut new_swapchain = vk::SwapchainKHR::null();
+ let create_info = vk::SwapchainCreateInfoKHR {
+ surface,
+ min_image_count: vulkan_swapchain.capabilities.min_image_count,
+ image_format: vulkan_swapchain.surface_format.format,
+ image_color_space: vulkan_swapchain.surface_format.color_space,
+ image_extent: vk::Extent2d { width, height },
+ image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT,
+ image_array_layers: 1,
+ image_sharing_mode: vk::SharingMode::Exclusive,
+ pre_transform: vk::SurfaceTransformFlagsKHR::IDENTITY,
+ composite_alpha: vk::CompositeAlphaFlagsKHR::OPAQUE,
+ present_mode: vk::PresentModeKHR::Fifo,
+ clipped: vk::Bool32::True,
+ old_swapchain,
+ ..default()
+ };
+ vk_check!(self.swapchain_fn.create_swapchain(
+ self.device,
+ &create_info,
+ None,
+ &mut new_swapchain
+ ));
+ assert!(!new_swapchain.is_null());
- fn end_frame(&self, mut frame: Frame) {
- let arena = HybridArena::<512>::new();
+ let images = vk_vec(|count, ptr| unsafe {
+ self.swapchain_fn.get_swapchain_images(
+ self.device,
+ new_swapchain,
+ count,
+ ptr,
+ )
+ });
- {
- let frame = self.frame_mut(&mut frame);
+ let image_views = images
+ .iter()
+ .map(|&image| {
+ let create_info = vk::ImageViewCreateInfo {
+ image,
+ view_type: vk::ImageViewType::Type2d,
+ format: vulkan_swapchain.surface_format.format,
+ subresource_range: vk::ImageSubresourceRange {
+ aspect_mask: vk::ImageAspectFlags::COLOR,
+ base_mip_level: 0,
+ level_count: 1,
+ base_array_layer: 0,
+ layer_count: 1,
+ },
+ ..default()
+ };
+ let mut view = vk::ImageView::null();
+ vk_check!(self.device_fn.create_image_view(
+ self.device,
+ &create_info,
+ None,
+ &mut view,
+ ));
- let present_swapchains = frame.present_swapchains.get_mut();
- if !present_swapchains.is_empty() {
- let windows = arena.alloc_slice_fill_iter(present_swapchains.keys().copied());
- let wait_semaphores =
- arena.alloc_slice_fill_iter(present_swapchains.values().map(|x| x.release));
- let swapchains =
- arena.alloc_slice_fill_iter(present_swapchains.values().map(|x| x.swapchain));
- let swapchain_image_indices =
- arena.alloc_slice_fill_iter(present_swapchains.values().map(|x| x.image_index));
+ let handle = image_pool.insert(VulkanImageHolder::Swapchain(
+ VulkanImageSwapchain {
+ surface,
+ image,
+ view,
+ },
+ ));
+ Image(handle)
+ })
+ .collect::<Box<_>>();
- present_swapchains.clear();
+ vulkan_swapchain.state = VulkanSwapchainState::Occupied {
+ width,
+ height,
+ suboptimal: false,
+ swapchain: new_swapchain,
+ image_views,
+ };
+ }
+ VulkanSwapchainState::Occupied {
+ width: current_width,
+ height: current_height,
+ suboptimal,
+ swapchain,
+ image_views,
+ } => {
+ let destroy_image_views =
+ |images: &mut Pool<VulkanImageHolder>| -> Box<[vk::ImageView]> {
+ let mut vulkan_image_views = Vec::new();
+ for &image_view in image_views.iter() {
+ match images.remove(image_view.0) {
+ Some(VulkanImageHolder::Swapchain(VulkanImageSwapchain {
+ surface: _,
+ image: _,
+ view,
+ })) => vulkan_image_views.push(view),
+ _ => panic!("swapchain image in wrong state"),
+ }
+ }
+ vulkan_image_views.into_boxed_slice()
+ };
- let results = arena.alloc_slice_fill_copy(swapchains.len(), vk::Result::Success);
+ let swapchain = *swapchain;
- let present_info = vk::PresentInfoKHR {
- wait_semaphores: wait_semaphores.into(),
- swapchains: (swapchains, swapchain_image_indices).into(),
- results: results.as_mut_ptr(),
- ..default()
- };
+ if width != *current_width || height != *current_height || *suboptimal {
+ let image_views = destroy_image_views(&mut image_pool);
+ old_swapchain = swapchain;
+ self.destroyed_swapchains.lock().push((
+ old_swapchain,
+ vk::SurfaceKHR::null(),
+ image_views,
+ ));
- unsafe {
- // check results below, so ignore this return value.
- let _ = self
- .swapchain_fn
- .queue_present(self.universal_queue, &present_info);
- };
+ vulkan_swapchain.state = VulkanSwapchainState::Vacant;
+ continue;
+ }
- for (i, &result) in results.iter().enumerate() {
- match result {
+ let acquire = self.request_transient_semaphore(frame);
+ let mut image_index = 0;
+ match unsafe {
+ self.swapchain_fn.acquire_next_image2(
+ self.device,
+ &vk::AcquireNextImageInfoKHR {
+ swapchain,
+ timeout: !0,
+ semaphore: acquire,
+ fence: vk::Fence::null(),
+ device_mask: 1,
+ ..default()
+ },
+ &mut image_index,
+ )
+ } {
vk::Result::Success => {}
vk::Result::SuboptimalKHR => {
- // Yikes
- if let VulkanSwapchainState::Occupied {
- width: _,
- height: _,
- suboptimal,
- swapchain: _,
- image_views: _,
- } = &mut self.swapchains.lock().get_mut(&windows[i]).unwrap().state
- {
- *suboptimal = true;
- }
+ *suboptimal = true;
}
- _ => vk_check!(result),
+ vk::Result::ErrorOutOfDateKHR => {
+ let image_views = destroy_image_views(&mut image_pool);
+
+ old_swapchain = swapchain;
+ self.destroyed_swapchains.lock().push((
+ old_swapchain,
+ vk::SurfaceKHR::null(),
+ image_views,
+ ));
+
+ vulkan_swapchain.state = VulkanSwapchainState::Vacant;
+ return Err(SwapchainOutOfDateError(()));
+ }
+ result => vk_check!(result),
}
+
+ present_info.acquire = acquire;
+ present_info.image_index = image_index;
+ present_info.swapchain = swapchain;
+ let view = image_views[image_index as usize];
+
+ return Ok((width, height, view));
}
}
}
-
- self.frame_counter.release(frame);
}
- unsafe fn map_buffer(&self, buffer: Buffer) -> *mut u8 {
- let mut ptr = std::ptr::null_mut();
- if let Some(buffer) = self.buffer_pool.lock().get(buffer.0) {
- vk_check!(self.device_fn.map_memory(
- self.device,
- buffer.memory.memory,
- buffer.memory.offset,
- buffer.memory.size,
- vk::MemoryMapFlags::default(),
- &mut ptr
- ))
- }
- std::mem::transmute::<*mut c_void, *mut u8>(ptr)
- }
+ fn destroy_swapchain(&self, surface: vk::SurfaceKHR) {
+ if let Some(VulkanSwapchain {
+ surface_format: _,
+ state,
+ _formats: _,
+ _present_modes: _,
+ capabilities: _,
+ }) = self.swapchains.lock().remove(&surface)
+ {
+ let mut image_pool = self.image_pool.lock();
- unsafe fn unmap_buffer(&self, buffer: Buffer) {
- if let Some(buffer) = self.buffer_pool.lock().get(buffer.0) {
- self.device_fn
- .unmap_memory(self.device, buffer.memory.memory)
+ if let VulkanSwapchainState::Occupied {
+ width: _,
+ height: _,
+ suboptimal: _,
+ swapchain,
+ image_views,
+ } = state
+ {
+ let mut vulkan_image_views = Vec::new();
+ for &image_view in image_views.iter() {
+ match image_pool.remove(image_view.0) {
+ Some(VulkanImageHolder::Swapchain(VulkanImageSwapchain {
+ surface: _,
+ image: _,
+ view,
+ })) => vulkan_image_views.push(view),
+ _ => panic!("swapchain image in wrong state"),
+ }
+ }
+
+ self.destroyed_swapchains.lock().push((
+ swapchain,
+ surface,
+ vulkan_image_views.into_boxed_slice(),
+ ));
+ }
}
}
}
-impl<'app> Drop for VulkanDevice<'app> {
+impl Drop for VulkanDevice {
fn drop(&mut self) {
vk_check!(self.device_fn.device_wait_idle(self.device));
.get_mut()
.drain(..)
.collect::<Vec<_>>();
- for (_, (window, swapchain, surface, image_views)) in destroyed_swapchains {
- self.destroy_swapchain(window, surface, swapchain, &image_views);
+ for (_, (swapchain, surface, image_views)) in destroyed_swapchains {
+ self.destroy_swapchain_deferred(surface, swapchain, &image_views);
}
}
- for (_, swapchain) in self.swapchains.get_mut().iter() {
+ for (&surface, swapchain) in self.swapchains.get_mut().iter() {
if let VulkanSwapchainState::Occupied {
width: _,
height: _,
{
unsafe { self.swapchain_fn.destroy_swapchain(device, swapchain, None) }
}
- unsafe {
- self.surface_fn
- .destroy_surface(instance, swapchain.surface, None)
- }
+ unsafe { self.surface_fn.destroy_surface(instance, surface, None) }
}
unsafe { device_fn.destroy_device(device, None) }