async acquire image
This commit is contained in:
parent
29258aed7b
commit
37aaa07edc
|
@ -14,7 +14,6 @@ use winit::{
|
||||||
struct WindowState {
|
struct WindowState {
|
||||||
last_resize_events: BTreeMap<WindowId, PhysicalSize<u32>>,
|
last_resize_events: BTreeMap<WindowId, PhysicalSize<u32>>,
|
||||||
window_attrs: WindowAttributes,
|
window_attrs: WindowAttributes,
|
||||||
window: Option<Window>,
|
|
||||||
windows: BTreeMap<WindowId, Window>,
|
windows: BTreeMap<WindowId, Window>,
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
}
|
}
|
||||||
|
@ -22,7 +21,6 @@ struct WindowState {
|
||||||
impl WindowState {
|
impl WindowState {
|
||||||
fn new(window_title: String, display: DisplayHandle) -> WindowState {
|
fn new(window_title: String, display: DisplayHandle) -> WindowState {
|
||||||
Self {
|
Self {
|
||||||
window: None,
|
|
||||||
windows: BTreeMap::new(),
|
windows: BTreeMap::new(),
|
||||||
last_resize_events: BTreeMap::new(),
|
last_resize_events: BTreeMap::new(),
|
||||||
window_attrs: WindowAttributes::default()
|
window_attrs: WindowAttributes::default()
|
||||||
|
@ -95,7 +93,7 @@ impl WindowState {
|
||||||
|
|
||||||
impl ApplicationHandler for WindowState {
|
impl ApplicationHandler for WindowState {
|
||||||
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||||
tracing::debug!("winit::resumed");
|
tracing::info!("winit::resumed");
|
||||||
|
|
||||||
self.create_window(event_loop);
|
self.create_window(event_loop);
|
||||||
}
|
}
|
||||||
|
@ -104,6 +102,7 @@ impl ApplicationHandler for WindowState {
|
||||||
&mut self,
|
&mut self,
|
||||||
_event_loop: &winit::event_loop::ActiveEventLoop,
|
_event_loop: &winit::event_loop::ActiveEventLoop,
|
||||||
) {
|
) {
|
||||||
|
tracing::info!("winit::about_to_wait");
|
||||||
for (&window, &resize) in self.last_resize_events.clone().iter() {
|
for (&window, &resize) in self.last_resize_events.clone().iter() {
|
||||||
self.handle_final_resize(window, resize);
|
self.handle_final_resize(window, resize);
|
||||||
}
|
}
|
||||||
|
@ -121,6 +120,7 @@ impl ApplicationHandler for WindowState {
|
||||||
self.handle_final_resize(window_id, resize);
|
self.handle_final_resize(window_id, resize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
winit::event::WindowEvent::Resized(physical_size) => {
|
winit::event::WindowEvent::Resized(physical_size) => {
|
||||||
_ = self.last_resize_events.insert(window_id, physical_size);
|
_ = self.last_resize_events.insert(window_id, physical_size);
|
||||||
|
@ -153,6 +153,30 @@ impl ApplicationHandler for WindowState {
|
||||||
} => {
|
} => {
|
||||||
self.create_window(event_loop);
|
self.create_window(event_loop);
|
||||||
}
|
}
|
||||||
|
winit::event::WindowEvent::KeyboardInput {
|
||||||
|
event:
|
||||||
|
winit::event::KeyEvent {
|
||||||
|
physical_key:
|
||||||
|
winit::keyboard::PhysicalKey::Code(
|
||||||
|
winit::keyboard::KeyCode::KeyQ,
|
||||||
|
),
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
repeat: false,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
tracing::info!(
|
||||||
|
window = u64::from(window_id),
|
||||||
|
"window close requested"
|
||||||
|
);
|
||||||
|
|
||||||
|
self.windows.remove(&window_id);
|
||||||
|
self.renderer.window_contexts.remove(&window_id);
|
||||||
|
if self.windows.is_empty() {
|
||||||
|
event_loop.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
winit::event::WindowEvent::KeyboardInput {
|
winit::event::WindowEvent::KeyboardInput {
|
||||||
device_id,
|
device_id,
|
||||||
event,
|
event,
|
||||||
|
@ -187,6 +211,7 @@ impl ApplicationHandler for WindowState {
|
||||||
fn main() {
|
fn main() {
|
||||||
tracing_subscriber::fmt().init();
|
tracing_subscriber::fmt().init();
|
||||||
let ev = EventLoop::new().unwrap();
|
let ev = EventLoop::new().unwrap();
|
||||||
|
ev.set_control_flow(winit::event_loop::ControlFlow::Poll);
|
||||||
|
|
||||||
let display = ev.display_handle().expect("display handle");
|
let display = ev.display_handle().expect("display handle");
|
||||||
let mut game = WindowState::new("Vidya".to_owned(), display);
|
let mut game = WindowState::new("Vidya".to_owned(), display);
|
||||||
|
|
|
@ -18,3 +18,6 @@ tracing-subscriber = "0.3.18"
|
||||||
vk-mem = "0.4.0"
|
vk-mem = "0.4.0"
|
||||||
vk-sync = "0.1.6"
|
vk-sync = "0.1.6"
|
||||||
winit = "0.30.5"
|
winit = "0.30.5"
|
||||||
|
crossbeam = "0.8.4"
|
||||||
|
parking_lot = "0.12.3"
|
||||||
|
smol.workspace = true
|
||||||
|
|
|
@ -1,61 +1,10 @@
|
||||||
use std::{future::Future, marker::PhantomData};
|
use std::{future::Future, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
use super::{Device2 as Device, Queue};
|
use crate::sync::{self, FenceFuture};
|
||||||
|
|
||||||
|
use super::{Device, Queue};
|
||||||
use ash::{prelude::*, vk};
|
use ash::{prelude::*, vk};
|
||||||
|
|
||||||
pub struct FenceFuture<'a> {
|
|
||||||
device: Device,
|
|
||||||
fence: vk::Fence,
|
|
||||||
_pd: PhantomData<&'a ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for FenceFuture<'_> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { self.device.dev().destroy_fence(self.fence, None) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FenceFuture<'_> {
|
|
||||||
/// Unsafe because `fence` must not be destroyed while this future is live.
|
|
||||||
pub unsafe fn from_fence(device: Device, fence: vk::Fence) -> Self {
|
|
||||||
Self {
|
|
||||||
device,
|
|
||||||
fence,
|
|
||||||
_pd: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn block_until(&self) -> VkResult<()> {
|
|
||||||
unsafe {
|
|
||||||
self.device
|
|
||||||
.dev()
|
|
||||||
.wait_for_fences(&[self.fence], true, u64::MAX)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Future for FenceFuture<'_> {
|
|
||||||
type Output = ();
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
self: std::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut std::task::Context<'_>,
|
|
||||||
) -> std::task::Poll<Self::Output> {
|
|
||||||
let signaled = unsafe {
|
|
||||||
self.device
|
|
||||||
.dev()
|
|
||||||
.get_fence_status(self.fence)
|
|
||||||
.unwrap_or(false)
|
|
||||||
};
|
|
||||||
|
|
||||||
if signaled {
|
|
||||||
std::task::Poll::Ready(())
|
|
||||||
} else {
|
|
||||||
tokio::task::spawn_blocking(move || {});
|
|
||||||
std::task::Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SingleUseCommandPool {
|
pub struct SingleUseCommandPool {
|
||||||
device: Device,
|
device: Device,
|
||||||
pool: vk::CommandPool,
|
pool: vk::CommandPool,
|
||||||
|
@ -128,12 +77,13 @@ impl SingleUseCommand {
|
||||||
self.buffer
|
self.buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_fence(
|
pub fn submit_fence(
|
||||||
&self,
|
&self,
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
||||||
signal: Option<vk::Semaphore>,
|
signal: Option<vk::Semaphore>,
|
||||||
) -> VkResult<vk::Fence> {
|
fence: Option<vk::Fence>,
|
||||||
|
) -> VkResult<()> {
|
||||||
unsafe { self.device.dev().end_command_buffer(self.buffer)? };
|
unsafe { self.device.dev().end_command_buffer(self.buffer)? };
|
||||||
|
|
||||||
let buffers = [self.buffer];
|
let buffers = [self.buffer];
|
||||||
|
@ -154,16 +104,13 @@ impl SingleUseCommand {
|
||||||
.wait_dst_stage_mask(core::slice::from_ref(stage));
|
.wait_dst_stage_mask(core::slice::from_ref(stage));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fence = {
|
let fence = fence.unwrap_or(vk::Fence::null());
|
||||||
let create_info = vk::FenceCreateInfo::default();
|
|
||||||
unsafe { self.device.dev().create_fence(&create_info, None)? }
|
|
||||||
};
|
|
||||||
|
|
||||||
queue.with_locked(|queue| unsafe {
|
queue.with_locked(|queue| unsafe {
|
||||||
self.device.dev().queue_submit(queue, &[submit_info], fence)
|
self.device.dev().queue_submit(queue, &[submit_info], fence)
|
||||||
})?;
|
})?;
|
||||||
|
tracing::info!("submitted queue {:?} and fence {:?}", queue, fence);
|
||||||
|
|
||||||
Ok(fence)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit_async<'a>(
|
pub fn submit_async<'a>(
|
||||||
|
@ -171,11 +118,12 @@ impl SingleUseCommand {
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
||||||
signal: Option<vk::Semaphore>,
|
signal: Option<vk::Semaphore>,
|
||||||
|
fence: Arc<sync::Fence>,
|
||||||
) -> VkResult<FenceFuture<'a>> {
|
) -> VkResult<FenceFuture<'a>> {
|
||||||
let device = self.device.clone();
|
let device = self.device.clone();
|
||||||
let fence = self.submit_fence(queue, wait, signal)?;
|
self.submit_fence(queue, wait, signal, Some(fence.fence()))?;
|
||||||
|
|
||||||
Ok(unsafe { FenceFuture::from_fence(device, fence) })
|
Ok(unsafe { FenceFuture::new(fence) })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn submit_blocking(
|
pub fn submit_blocking(
|
||||||
|
@ -184,13 +132,9 @@ impl SingleUseCommand {
|
||||||
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
|
||||||
signal: Option<vk::Semaphore>,
|
signal: Option<vk::Semaphore>,
|
||||||
) -> VkResult<()> {
|
) -> VkResult<()> {
|
||||||
let device = self.device.clone();
|
let fence = Arc::new(sync::Fence::create(self.device.clone())?);
|
||||||
let fence = self.submit_fence(queue, wait, signal)?;
|
let future = self.submit_async(queue, wait, signal, fence)?;
|
||||||
|
future.block();
|
||||||
unsafe {
|
|
||||||
device.dev().wait_for_fences(&[fence], true, u64::MAX)?;
|
|
||||||
device.dev().destroy_fence(fence, None);
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,6 +144,13 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
async fn async_submit(cmd: SingleUseCommand, queue: Queue) {
|
async fn async_submit(cmd: SingleUseCommand, queue: Queue) {
|
||||||
cmd.submit_async(queue, None, None).unwrap().await;
|
cmd.submit_async(
|
||||||
|
queue,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Arc::new(sync::Fence::create(cmd.device.clone()).unwrap()),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{Device2 as Device, Queue, VkAllocator};
|
use super::{Device, Queue, VkAllocator};
|
||||||
use ash::{prelude::*, vk};
|
use ash::{prelude::*, vk};
|
||||||
use vk_mem::Alloc;
|
use vk_mem::Alloc;
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
#![feature(c_str_module, closure_lifetime_binder, let_chains)]
|
#![feature(c_str_module, closure_lifetime_binder, let_chains, negative_impls)]
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, BTreeSet, HashMap},
|
collections::{BTreeMap, BTreeSet, HashMap},
|
||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
sync::{atomic::AtomicU64, Arc, Mutex},
|
ops::Deref,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicU32, AtomicU64},
|
||||||
|
Arc, Mutex,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use ash::{
|
use ash::{
|
||||||
khr,
|
khr,
|
||||||
|
prelude::VkResult,
|
||||||
vk::{self, Handle},
|
vk::{self, Handle},
|
||||||
Entry,
|
Entry,
|
||||||
};
|
};
|
||||||
|
@ -27,6 +32,8 @@ type VkAllocator = Arc<vk_mem::Allocator>;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("Swapchain suboptimal.")]
|
||||||
|
SuboptimalSwapchain,
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
LoadingError(#[from] ash::LoadingError),
|
LoadingError(#[from] ash::LoadingError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
@ -118,7 +125,7 @@ trait ExtendsDeviceFeatures2Debug:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
trait ExtendsDeviceProperties2Debug:
|
trait ExtendsDeviceProperties2Debug:
|
||||||
vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone
|
vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,8 +133,9 @@ impl<T: vk::ExtendsPhysicalDeviceFeatures2 + Debug> ExtendsDeviceFeatures2Debug
|
||||||
for T
|
for T
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
impl<T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone>
|
impl<
|
||||||
ExtendsDeviceProperties2Debug for T
|
T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync,
|
||||||
|
> ExtendsDeviceProperties2Debug for T
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,20 +397,6 @@ struct PhysicalDevice {
|
||||||
properties: PhysicalDeviceProperties,
|
properties: PhysicalDeviceProperties,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhysicalDevice {
|
|
||||||
fn swapchain_family_indices(&self) -> ArrayVec<[u32; 2]> {
|
|
||||||
let mut indices = array_vec!([u32; 2] => self.queue_families.graphics);
|
|
||||||
|
|
||||||
if let Some(present) = self.queue_families.present
|
|
||||||
&& present != self.queue_families.graphics
|
|
||||||
{
|
|
||||||
indices.push(present);
|
|
||||||
}
|
|
||||||
|
|
||||||
indices
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Instance {
|
struct Instance {
|
||||||
entry: Entry,
|
entry: Entry,
|
||||||
instance: ash::Instance,
|
instance: ash::Instance,
|
||||||
|
@ -441,7 +435,22 @@ struct DeviceQueueFamilies {
|
||||||
transfer: Option<u32>,
|
transfer: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Device {
|
impl DeviceQueueFamilies {
|
||||||
|
fn swapchain_family_indices(&self) -> ArrayVec<[u32; 2]> {
|
||||||
|
let mut indices = array_vec!([u32; 2] => self.graphics);
|
||||||
|
|
||||||
|
if let Some(present) = self.present
|
||||||
|
&& present != self.graphics
|
||||||
|
{
|
||||||
|
indices.push(present);
|
||||||
|
}
|
||||||
|
|
||||||
|
indices
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DeviceInner {
|
||||||
|
instance: Arc<Instance>,
|
||||||
physical: PhysicalDevice,
|
physical: PhysicalDevice,
|
||||||
device: ash::Device,
|
device: ash::Device,
|
||||||
swapchain: khr::swapchain::Device,
|
swapchain: khr::swapchain::Device,
|
||||||
|
@ -449,12 +458,23 @@ struct Device {
|
||||||
compute_queue: Queue,
|
compute_queue: Queue,
|
||||||
transfer_queue: Queue,
|
transfer_queue: Queue,
|
||||||
present_queue: Queue,
|
present_queue: Queue,
|
||||||
|
sync_threadpool: sync::SyncThreadpool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Device2(Arc<Device>);
|
pub struct Device(Arc<DeviceInner>);
|
||||||
|
pub type WeakDevice = std::sync::Weak<DeviceInner>;
|
||||||
|
|
||||||
impl Device2 {
|
impl Device {
|
||||||
|
fn new(inner: DeviceInner) -> Self {
|
||||||
|
Self(Arc::new(inner))
|
||||||
|
}
|
||||||
|
fn sync_threadpool(&self) -> &sync::SyncThreadpool {
|
||||||
|
&self.0.sync_threadpool
|
||||||
|
}
|
||||||
|
fn weak(&self) -> WeakDevice {
|
||||||
|
Arc::downgrade(&self.0)
|
||||||
|
}
|
||||||
fn dev(&self) -> &ash::Device {
|
fn dev(&self) -> &ash::Device {
|
||||||
&self.0.device
|
&self.0.device
|
||||||
}
|
}
|
||||||
|
@ -464,8 +484,8 @@ impl Device2 {
|
||||||
fn queue_families(&self) -> &DeviceQueueFamilies {
|
fn queue_families(&self) -> &DeviceQueueFamilies {
|
||||||
&self.0.physical.queue_families
|
&self.0.physical.queue_families
|
||||||
}
|
}
|
||||||
fn phy(&self) -> &vk::PhysicalDevice {
|
fn phy(&self) -> vk::PhysicalDevice {
|
||||||
&self.0.physical.pdev
|
self.0.physical.pdev
|
||||||
}
|
}
|
||||||
fn graphics_queue(&self) -> &Queue {
|
fn graphics_queue(&self) -> &Queue {
|
||||||
&self.0.main_queue
|
&self.0.main_queue
|
||||||
|
@ -475,19 +495,19 @@ impl Device2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<khr::swapchain::Device> for Device2 {
|
impl AsRef<khr::swapchain::Device> for Device {
|
||||||
fn as_ref(&self) -> &khr::swapchain::Device {
|
fn as_ref(&self) -> &khr::swapchain::Device {
|
||||||
&self.0.swapchain
|
&self.0.swapchain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<ash::Device> for Device2 {
|
impl AsRef<ash::Device> for Device {
|
||||||
fn as_ref(&self) -> &ash::Device {
|
fn as_ref(&self) -> &ash::Device {
|
||||||
&self.0.device
|
&self.0.device
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Device {
|
impl Drop for DeviceInner {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
_ = self.device.device_wait_idle();
|
_ = self.device.device_wait_idle();
|
||||||
|
@ -497,7 +517,7 @@ impl Drop for Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DeviceAndQueues {
|
struct DeviceAndQueues {
|
||||||
device: Arc<Device>,
|
device: Device,
|
||||||
main_queue: Queue,
|
main_queue: Queue,
|
||||||
compute_queue: Queue,
|
compute_queue: Queue,
|
||||||
transfer_queue: Queue,
|
transfer_queue: Queue,
|
||||||
|
@ -506,27 +526,67 @@ struct DeviceAndQueues {
|
||||||
|
|
||||||
impl AsRef<ash::Device> for DeviceAndQueues {
|
impl AsRef<ash::Device> for DeviceAndQueues {
|
||||||
fn as_ref(&self) -> &ash::Device {
|
fn as_ref(&self) -> &ash::Device {
|
||||||
&self.device.device
|
&self.device.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl AsRef<ash::khr::swapchain::Device> for DeviceAndQueues {
|
impl AsRef<ash::khr::swapchain::Device> for DeviceAndQueues {
|
||||||
fn as_ref(&self) -> &ash::khr::swapchain::Device {
|
fn as_ref(&self) -> &ash::khr::swapchain::Device {
|
||||||
&self.device.swapchain
|
&self.device.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Swapchain {
|
struct RawSwapchain(vk::SwapchainKHR);
|
||||||
|
|
||||||
|
impl !Sync for RawSwapchain {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SwapchainHandle(parking_lot::Mutex<vk::SwapchainKHR>);
|
||||||
|
|
||||||
|
impl SwapchainHandle {
|
||||||
|
unsafe fn from_handle(swapchain: vk::SwapchainKHR) -> SwapchainHandle {
|
||||||
|
Self(parking_lot::Mutex::new(swapchain))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lock(
|
||||||
|
&self,
|
||||||
|
) -> parking_lot::lock_api::MutexGuard<
|
||||||
|
'_,
|
||||||
|
parking_lot::RawMutex,
|
||||||
|
vk::SwapchainKHR,
|
||||||
|
> {
|
||||||
|
self.0.lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_locked<T, F: FnOnce(vk::SwapchainKHR) -> T>(&self, f: F) -> T {
|
||||||
|
let lock = self.0.lock();
|
||||||
|
f(*lock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Swapchain {
|
||||||
|
device: Device,
|
||||||
instance: Arc<Instance>,
|
instance: Arc<Instance>,
|
||||||
device: Arc<Device>,
|
|
||||||
// has a strong ref to the surface because the surface may not outlive the swapchain
|
// has a strong ref to the surface because the surface may not outlive the swapchain
|
||||||
surface: Arc<Surface>,
|
surface: Arc<Surface>,
|
||||||
swapchain: vk::SwapchainKHR,
|
swapchain: SwapchainHandle,
|
||||||
present_mode: vk::PresentModeKHR,
|
present_mode: vk::PresentModeKHR,
|
||||||
color_space: vk::ColorSpaceKHR,
|
color_space: vk::ColorSpaceKHR,
|
||||||
format: vk::Format,
|
format: vk::Format,
|
||||||
images: Vec<vk::Image>,
|
images: Vec<vk::Image>,
|
||||||
image_views: Vec<vk::ImageView>,
|
image_views: Vec<vk::ImageView>,
|
||||||
extent: vk::Extent2D,
|
extent: vk::Extent2D,
|
||||||
|
min_image_count: u32,
|
||||||
|
|
||||||
|
// sync objects:
|
||||||
|
// we need two semaphores per each image, one acquire-semaphore and one release-semaphore.
|
||||||
|
// semaphores must be unique to each frame and cannot be reused per swapchain.
|
||||||
|
acquire_semaphores: Vec<vk::Semaphore>,
|
||||||
|
release_semaphores: Vec<vk::Semaphore>,
|
||||||
|
|
||||||
|
// one fence per in-flight frame, to synchronize image acquisition
|
||||||
|
fences: Vec<Arc<sync::Fence>>,
|
||||||
|
|
||||||
|
current_frame: AtomicU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Swapchain {
|
impl Drop for Swapchain {
|
||||||
|
@ -534,11 +594,22 @@ impl Drop for Swapchain {
|
||||||
unsafe {
|
unsafe {
|
||||||
info!("dropping swapchain {:?}", self.swapchain);
|
info!("dropping swapchain {:?}", self.swapchain);
|
||||||
for view in &self.image_views {
|
for view in &self.image_views {
|
||||||
self.device.device.destroy_image_view(*view, None);
|
self.device.dev().destroy_image_view(*view, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.swapchain.with_locked(|swapchain| {
|
||||||
|
self.device.swapchain().destroy_swapchain(swapchain, None)
|
||||||
|
});
|
||||||
|
|
||||||
|
for &semaphore in self
|
||||||
|
.acquire_semaphores
|
||||||
|
.iter()
|
||||||
|
.chain(&self.release_semaphores)
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
self.device.dev().destroy_semaphore(semaphore, None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.device
|
|
||||||
.swapchain
|
|
||||||
.destroy_swapchain(self.swapchain, None);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,19 +634,35 @@ fn current_extent_or_clamped(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SwapchainParams {
|
||||||
|
present_mode: vk::PresentModeKHR,
|
||||||
|
format: vk::Format,
|
||||||
|
color_space: vk::ColorSpaceKHR,
|
||||||
|
/// the number of images to request from the device
|
||||||
|
image_count: u32,
|
||||||
|
/// the minimum number of images the surface permits
|
||||||
|
min_image_count: u32,
|
||||||
|
extent: vk::Extent2D,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SwapchainFrame {
|
||||||
|
pub swapchain: Arc<Swapchain>,
|
||||||
|
pub index: u32,
|
||||||
|
pub image: vk::Image,
|
||||||
|
pub view: vk::ImageView,
|
||||||
|
pub acquire: vk::Semaphore,
|
||||||
|
pub release: vk::Semaphore,
|
||||||
|
}
|
||||||
|
|
||||||
impl Swapchain {
|
impl Swapchain {
|
||||||
|
const PREFERRED_IMAGES_IN_FLIGHT: u32 = 3;
|
||||||
|
|
||||||
fn get_swapchain_params_from_surface(
|
fn get_swapchain_params_from_surface(
|
||||||
instance: &Arc<Instance>,
|
instance: &Arc<Instance>,
|
||||||
surface: vk::SurfaceKHR,
|
surface: vk::SurfaceKHR,
|
||||||
pdev: vk::PhysicalDevice,
|
pdev: vk::PhysicalDevice,
|
||||||
requested_extent: vk::Extent2D,
|
requested_extent: vk::Extent2D,
|
||||||
) -> Result<(
|
) -> Result<SwapchainParams> {
|
||||||
vk::PresentModeKHR,
|
|
||||||
vk::Format,
|
|
||||||
vk::ColorSpaceKHR,
|
|
||||||
u32,
|
|
||||||
vk::Extent2D,
|
|
||||||
)> {
|
|
||||||
let caps = unsafe {
|
let caps = unsafe {
|
||||||
instance
|
instance
|
||||||
.surface
|
.surface
|
||||||
|
@ -611,52 +698,76 @@ impl Swapchain {
|
||||||
.cloned()
|
.cloned()
|
||||||
.expect("no surface format available!");
|
.expect("no surface format available!");
|
||||||
|
|
||||||
let image_count = 3u32.clamp(
|
// 0 here means no limit
|
||||||
caps.min_image_count,
|
let max_image_count = core::num::NonZero::new(caps.max_image_count)
|
||||||
if caps.max_image_count == 0 {
|
.map(|n| n.get())
|
||||||
u32::MAX
|
.unwrap_or(u32::MAX);
|
||||||
} else {
|
|
||||||
caps.max_image_count
|
// we want PREFERRED_IMAGES_IN_FLIGHT images acquired at the same time,
|
||||||
},
|
let image_count = (caps.min_image_count
|
||||||
);
|
+ Self::PREFERRED_IMAGES_IN_FLIGHT)
|
||||||
|
.min(max_image_count);
|
||||||
|
|
||||||
let extent = current_extent_or_clamped(&caps, requested_extent);
|
let extent = current_extent_or_clamped(&caps, requested_extent);
|
||||||
|
|
||||||
Ok((
|
Ok(SwapchainParams {
|
||||||
present_mode,
|
present_mode,
|
||||||
format.format,
|
format: format.format,
|
||||||
format.color_space,
|
color_space: format.color_space,
|
||||||
image_count,
|
image_count,
|
||||||
extent,
|
extent,
|
||||||
))
|
min_image_count: caps.min_image_count,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
fn create_new(
|
|
||||||
|
pub fn new(
|
||||||
instance: Arc<Instance>,
|
instance: Arc<Instance>,
|
||||||
device: Arc<Device>,
|
device: Device,
|
||||||
surface: Arc<Surface>,
|
surface: Arc<Surface>,
|
||||||
pdev: vk::PhysicalDevice,
|
pdev: vk::PhysicalDevice,
|
||||||
extent: vk::Extent2D,
|
extent: vk::Extent2D,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let (present_mode, format, color_space, image_count, extent) =
|
Self::create(instance, device, surface, pdev, extent, None)
|
||||||
Self::get_swapchain_params_from_surface(
|
}
|
||||||
&instance,
|
|
||||||
surface.surface,
|
|
||||||
pdev,
|
|
||||||
extent,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (swapchain, images) = Self::create_vkswapchainkhr(
|
fn create(
|
||||||
&device,
|
instance: Arc<Instance>,
|
||||||
surface.surface,
|
device: Device,
|
||||||
&device.physical.swapchain_family_indices(),
|
surface: Arc<Surface>,
|
||||||
extent,
|
pdev: vk::PhysicalDevice,
|
||||||
None,
|
extent: vk::Extent2D,
|
||||||
|
old_swapchain: Option<&SwapchainHandle>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let SwapchainParams {
|
||||||
present_mode,
|
present_mode,
|
||||||
format,
|
format,
|
||||||
color_space,
|
color_space,
|
||||||
image_count,
|
image_count,
|
||||||
|
min_image_count,
|
||||||
|
extent,
|
||||||
|
} = Self::get_swapchain_params_from_surface(
|
||||||
|
&instance,
|
||||||
|
surface.surface,
|
||||||
|
pdev,
|
||||||
|
extent,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let (swapchain, images) = {
|
||||||
|
let lock = old_swapchain.as_ref().map(|handle| handle.lock());
|
||||||
|
|
||||||
|
Self::create_vkswapchainkhr(
|
||||||
|
&device,
|
||||||
|
surface.surface,
|
||||||
|
&device.queue_families().swapchain_family_indices(),
|
||||||
|
extent,
|
||||||
|
lock.as_ref().map(|lock| **lock),
|
||||||
|
present_mode,
|
||||||
|
format,
|
||||||
|
color_space,
|
||||||
|
image_count,
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
let image_views = images
|
let image_views = images
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&image| {
|
.map(|&image| {
|
||||||
|
@ -674,10 +785,42 @@ impl Swapchain {
|
||||||
.layer_count(1),
|
.layer_count(1),
|
||||||
);
|
);
|
||||||
|
|
||||||
unsafe { device.device.create_image_view(&info, None) }
|
unsafe { device.dev().create_image_view(&info, None) }
|
||||||
})
|
})
|
||||||
.collect::<core::result::Result<Vec<vk::ImageView>, _>>()?;
|
.collect::<core::result::Result<Vec<vk::ImageView>, _>>()?;
|
||||||
|
|
||||||
|
let num_images = images.len() as u32;
|
||||||
|
let inflight_frames = num_images - min_image_count;
|
||||||
|
|
||||||
|
let acquire_semaphores = {
|
||||||
|
(0..inflight_frames)
|
||||||
|
.map(|_| unsafe {
|
||||||
|
device.dev().create_semaphore(
|
||||||
|
&vk::SemaphoreCreateInfo::default(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<VkResult<Vec<_>>>()?
|
||||||
|
};
|
||||||
|
let release_semaphores = {
|
||||||
|
(0..inflight_frames)
|
||||||
|
.map(|_| unsafe {
|
||||||
|
device.dev().create_semaphore(
|
||||||
|
&vk::SemaphoreCreateInfo::default(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<VkResult<Vec<_>>>()?
|
||||||
|
};
|
||||||
|
|
||||||
|
let fences = {
|
||||||
|
(0..inflight_frames)
|
||||||
|
.map(|_| Ok(Arc::new(sync::Fence::create(device.clone())?)))
|
||||||
|
.collect::<VkResult<Vec<_>>>()?
|
||||||
|
};
|
||||||
|
|
||||||
|
tracing::info!("fences: {fences:?}");
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
instance,
|
instance,
|
||||||
device,
|
device,
|
||||||
|
@ -688,68 +831,96 @@ impl Swapchain {
|
||||||
format,
|
format,
|
||||||
images,
|
images,
|
||||||
image_views,
|
image_views,
|
||||||
|
min_image_count,
|
||||||
extent,
|
extent,
|
||||||
|
acquire_semaphores,
|
||||||
|
release_semaphores,
|
||||||
|
fences,
|
||||||
|
current_frame: AtomicU32::new(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn max_in_flight_images(&self) -> u32 {
|
||||||
|
self.num_images() - self.min_image_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_images(&self) -> u32 {
|
||||||
|
self.images.len() as u32
|
||||||
|
}
|
||||||
|
|
||||||
fn recreate(&self, extent: vk::Extent2D) -> Result<Self> {
|
fn recreate(&self, extent: vk::Extent2D) -> Result<Self> {
|
||||||
let (present_mode, format, color_space, image_count, extent) =
|
Self::create(
|
||||||
Self::get_swapchain_params_from_surface(
|
self.instance.clone(),
|
||||||
&self.instance,
|
self.device.clone(),
|
||||||
self.surface.surface,
|
self.surface.clone(),
|
||||||
self.device.physical.pdev,
|
self.device.phy(),
|
||||||
extent,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (swapchain, images) = Self::create_vkswapchainkhr(
|
|
||||||
&self.device,
|
|
||||||
self.surface.surface,
|
|
||||||
&self.device.physical.swapchain_family_indices(),
|
|
||||||
extent,
|
extent,
|
||||||
Some(self.swapchain),
|
Some(&self.swapchain),
|
||||||
present_mode,
|
)
|
||||||
format,
|
}
|
||||||
color_space,
|
|
||||||
image_count,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let image_views = images
|
/// returns a future yielding the frame, and true if the swapchain is
|
||||||
.iter()
|
/// suboptimal and should be recreated.
|
||||||
.map(|&image| {
|
fn acquire_image(
|
||||||
let info = vk::ImageViewCreateInfo::default()
|
self: Arc<Self>,
|
||||||
.image(image)
|
) -> impl std::future::Future<Output = VkResult<(SwapchainFrame, bool)>>
|
||||||
.view_type(vk::ImageViewType::TYPE_2D)
|
{
|
||||||
.format(format)
|
let frame = self
|
||||||
.components(vk::ComponentMapping::default())
|
.current_frame
|
||||||
.subresource_range(
|
.fetch_update(
|
||||||
vk::ImageSubresourceRange::default()
|
std::sync::atomic::Ordering::Release,
|
||||||
.aspect_mask(vk::ImageAspectFlags::COLOR)
|
std::sync::atomic::Ordering::Relaxed,
|
||||||
.base_mip_level(0)
|
|i| Some((i + 1) % self.max_in_flight_images()),
|
||||||
.level_count(1)
|
)
|
||||||
.base_array_layer(0)
|
.unwrap() as usize;
|
||||||
.layer_count(1),
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe { self.device.device.create_image_view(&info, None) }
|
tracing::info!(frame, "acquiring image for frame {frame}");
|
||||||
|
|
||||||
|
async move {
|
||||||
|
let fence = self.fences[frame].clone();
|
||||||
|
let acquire = self.acquire_semaphores[frame];
|
||||||
|
let release = self.release_semaphores[frame];
|
||||||
|
|
||||||
|
// spawn on threadpool because it might block.
|
||||||
|
let (idx, suboptimal) = smol::unblock({
|
||||||
|
let this = self.clone();
|
||||||
|
let fence = fence.clone();
|
||||||
|
move || unsafe {
|
||||||
|
this.swapchain.with_locked(|swapchain| {
|
||||||
|
this.device.swapchain().acquire_next_image(
|
||||||
|
swapchain,
|
||||||
|
u64::MAX,
|
||||||
|
acquire,
|
||||||
|
fence.fence(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<core::result::Result<Vec<vk::ImageView>, _>>()?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self {
|
// wait for image to become available.
|
||||||
instance: self.instance.clone(),
|
sync::FenceFuture::new(fence.clone()).await;
|
||||||
device: self.device.clone(),
|
|
||||||
swapchain,
|
let idx = idx as usize;
|
||||||
surface: self.surface.clone(),
|
let image = self.images[idx];
|
||||||
present_mode,
|
let view = self.image_views[idx];
|
||||||
color_space,
|
|
||||||
format,
|
Ok((
|
||||||
images,
|
SwapchainFrame {
|
||||||
image_views,
|
index: idx as u32,
|
||||||
extent,
|
swapchain: self.clone(),
|
||||||
})
|
image,
|
||||||
|
view,
|
||||||
|
acquire,
|
||||||
|
release,
|
||||||
|
},
|
||||||
|
suboptimal,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_vkswapchainkhr(
|
fn create_vkswapchainkhr(
|
||||||
device: &Arc<Device>,
|
device: &Device,
|
||||||
surface: vk::SurfaceKHR,
|
surface: vk::SurfaceKHR,
|
||||||
queue_families: &[u32],
|
queue_families: &[u32],
|
||||||
image_extent: vk::Extent2D,
|
image_extent: vk::Extent2D,
|
||||||
|
@ -758,7 +929,7 @@ impl Swapchain {
|
||||||
image_format: vk::Format,
|
image_format: vk::Format,
|
||||||
image_color_space: vk::ColorSpaceKHR,
|
image_color_space: vk::ColorSpaceKHR,
|
||||||
image_count: u32,
|
image_count: u32,
|
||||||
) -> Result<(vk::SwapchainKHR, Vec<vk::Image>)> {
|
) -> Result<(SwapchainHandle, Vec<vk::Image>)> {
|
||||||
let create_info = vk::SwapchainCreateInfoKHR::default()
|
let create_info = vk::SwapchainCreateInfoKHR::default()
|
||||||
.surface(surface)
|
.surface(surface)
|
||||||
.present_mode(present_mode)
|
.present_mode(present_mode)
|
||||||
|
@ -784,10 +955,10 @@ 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)?;
|
||||||
let images = device.swapchain.get_swapchain_images(swapchain)?;
|
let images = device.swapchain().get_swapchain_images(swapchain)?;
|
||||||
|
|
||||||
(swapchain, images)
|
(SwapchainHandle::from_handle(swapchain), images)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((swapchain, images))
|
Ok((swapchain, images))
|
||||||
|
@ -829,7 +1000,7 @@ impl Drop for Surface {
|
||||||
|
|
||||||
pub struct Vulkan {
|
pub struct Vulkan {
|
||||||
instance: Arc<Instance>,
|
instance: Arc<Instance>,
|
||||||
device: Arc<Device>,
|
device: Device,
|
||||||
alloc: VkAllocator,
|
alloc: VkAllocator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1017,12 +1188,12 @@ impl Vulkan {
|
||||||
|
|
||||||
tracing::debug!("pdev: {pdev:?}");
|
tracing::debug!("pdev: {pdev:?}");
|
||||||
let device =
|
let device =
|
||||||
Arc::new(Self::create_device(&instance, pdev, &mut features)?);
|
Self::create_device(instance.clone(), pdev, &mut features)?;
|
||||||
|
|
||||||
let alloc_info = vk_mem::AllocatorCreateInfo::new(
|
let alloc_info = vk_mem::AllocatorCreateInfo::new(
|
||||||
&instance.instance,
|
&instance.instance,
|
||||||
&device.device,
|
device.dev(),
|
||||||
device.physical.pdev,
|
device.phy(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let alloc = Arc::new(unsafe { vk_mem::Allocator::new(alloc_info)? });
|
let alloc = Arc::new(unsafe { vk_mem::Allocator::new(alloc_info)? });
|
||||||
|
@ -1221,7 +1392,7 @@ impl Vulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_device(
|
fn create_device(
|
||||||
instance: &Instance,
|
instance: Arc<Instance>,
|
||||||
pdev: PhysicalDevice,
|
pdev: PhysicalDevice,
|
||||||
features: &mut PhysicalDeviceFeatures,
|
features: &mut PhysicalDeviceFeatures,
|
||||||
) -> Result<Device> {
|
) -> Result<Device> {
|
||||||
|
@ -1305,18 +1476,20 @@ impl Vulkan {
|
||||||
.map(|(f, i)| Queue::new(&device, f, i))
|
.map(|(f, i)| Queue::new(&device, f, i))
|
||||||
.unwrap_or(compute_queue.clone());
|
.unwrap_or(compute_queue.clone());
|
||||||
|
|
||||||
Device {
|
Device::new(DeviceInner {
|
||||||
device: device.clone(),
|
device: device.clone(),
|
||||||
physical: pdev,
|
physical: pdev,
|
||||||
swapchain: khr::swapchain::Device::new(
|
swapchain: khr::swapchain::Device::new(
|
||||||
&instance.instance,
|
&instance.instance,
|
||||||
&device,
|
&device,
|
||||||
),
|
),
|
||||||
|
instance,
|
||||||
main_queue,
|
main_queue,
|
||||||
present_queue,
|
present_queue,
|
||||||
compute_queue,
|
compute_queue,
|
||||||
transfer_queue,
|
transfer_queue,
|
||||||
}
|
sync_threadpool: sync::SyncThreadpool::new(),
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(device)
|
Ok(device)
|
||||||
|
@ -1500,7 +1673,7 @@ pub struct WindowContext {
|
||||||
impl WindowContext {
|
impl WindowContext {
|
||||||
fn new(
|
fn new(
|
||||||
instance: Arc<Instance>,
|
instance: Arc<Instance>,
|
||||||
device: Arc<Device>,
|
device: Device,
|
||||||
extent: vk::Extent2D,
|
extent: vk::Extent2D,
|
||||||
window: winit::raw_window_handle::RawWindowHandle,
|
window: winit::raw_window_handle::RawWindowHandle,
|
||||||
display: winit::raw_window_handle::RawDisplayHandle,
|
display: winit::raw_window_handle::RawDisplayHandle,
|
||||||
|
@ -1508,11 +1681,11 @@ impl WindowContext {
|
||||||
let surface =
|
let surface =
|
||||||
Arc::new(Surface::create(instance.clone(), display, window)?);
|
Arc::new(Surface::create(instance.clone(), display, window)?);
|
||||||
|
|
||||||
let swapchain = Arc::new(Swapchain::create_new(
|
let swapchain = Arc::new(Swapchain::new(
|
||||||
instance,
|
instance,
|
||||||
device.clone(),
|
device.clone(),
|
||||||
surface.clone(),
|
surface.clone(),
|
||||||
device.physical.pdev,
|
device.phy(),
|
||||||
extent,
|
extent,
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
|
@ -1549,7 +1722,7 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_draw(&mut self) -> Result<()> {
|
pub fn debug_draw(&mut self) -> Result<()> {
|
||||||
let dev = Device2(self.vulkan.device.clone());
|
let dev = self.vulkan.device.clone();
|
||||||
|
|
||||||
unsafe { dev.dev().device_wait_idle()? };
|
unsafe { dev.dev().device_wait_idle()? };
|
||||||
|
|
||||||
|
@ -1565,20 +1738,15 @@ impl Renderer {
|
||||||
|
|
||||||
let extent = ctx.current_swapchain.extent;
|
let extent = ctx.current_swapchain.extent;
|
||||||
|
|
||||||
let ready_semaphore = sync::Semaphore::new(dev.clone())?;
|
let (frame, suboptimal) =
|
||||||
let (swapchain_index, suboptimal) = unsafe {
|
smol::block_on(ctx.current_swapchain.clone().acquire_image())?;
|
||||||
dev.swapchain().acquire_next_image(
|
|
||||||
ctx.current_swapchain.swapchain,
|
|
||||||
u64::MAX,
|
|
||||||
ready_semaphore.semaphore(),
|
|
||||||
vk::Fence::null(),
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
if suboptimal {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let image = ctx.current_swapchain.images[swapchain_index as usize];
|
if suboptimal {
|
||||||
|
tracing::warn!(
|
||||||
|
"swapchain ({:?}) is suboptimal!",
|
||||||
|
ctx.current_swapchain.swapchain
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// let image = images::Image2D::new_exclusive(
|
// let image = images::Image2D::new_exclusive(
|
||||||
// self.vulkan.alloc.clone(),
|
// self.vulkan.alloc.clone(),
|
||||||
|
@ -1604,7 +1772,7 @@ impl Renderer {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let barriers = [images::image_barrier(
|
let barriers = [images::image_barrier(
|
||||||
image,
|
frame.image,
|
||||||
vk::ImageAspectFlags::COLOR,
|
vk::ImageAspectFlags::COLOR,
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
vk::PipelineStageFlags2::TRANSFER,
|
||||||
vk::AccessFlags2::empty(),
|
vk::AccessFlags2::empty(),
|
||||||
|
@ -1622,7 +1790,7 @@ impl Renderer {
|
||||||
dev.dev().cmd_pipeline_barrier2(buffer, &dependency_info);
|
dev.dev().cmd_pipeline_barrier2(buffer, &dependency_info);
|
||||||
dev.dev().cmd_clear_color_image(
|
dev.dev().cmd_clear_color_image(
|
||||||
buffer,
|
buffer,
|
||||||
image,
|
frame.image,
|
||||||
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
|
||||||
&clear_values,
|
&clear_values,
|
||||||
&[vk::ImageSubresourceRange::default()
|
&[vk::ImageSubresourceRange::default()
|
||||||
|
@ -1634,7 +1802,7 @@ impl Renderer {
|
||||||
);
|
);
|
||||||
|
|
||||||
let barriers = [images::image_barrier(
|
let barriers = [images::image_barrier(
|
||||||
image,
|
frame.image,
|
||||||
vk::ImageAspectFlags::COLOR,
|
vk::ImageAspectFlags::COLOR,
|
||||||
vk::PipelineStageFlags2::TRANSFER,
|
vk::PipelineStageFlags2::TRANSFER,
|
||||||
vk::AccessFlags2::TRANSFER_WRITE,
|
vk::AccessFlags2::TRANSFER_WRITE,
|
||||||
|
@ -1651,52 +1819,52 @@ impl Renderer {
|
||||||
|
|
||||||
dev.dev().cmd_pipeline_barrier2(buffer, &dependency_info);
|
dev.dev().cmd_pipeline_barrier2(buffer, &dependency_info);
|
||||||
|
|
||||||
let signal_semaphore = sync::Semaphore::new(dev.clone())?;
|
|
||||||
|
|
||||||
let future = cmd.submit_async(
|
let future = cmd.submit_async(
|
||||||
dev.graphics_queue().clone(),
|
dev.graphics_queue().clone(),
|
||||||
Some((
|
Some((frame.acquire, vk::PipelineStageFlags::ALL_COMMANDS)),
|
||||||
ready_semaphore.semaphore(),
|
Some(frame.release),
|
||||||
vk::PipelineStageFlags::ALL_COMMANDS,
|
Arc::new(sync::Fence::create(dev.clone())?),
|
||||||
)),
|
|
||||||
Some(signal_semaphore.semaphore()),
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let wait_semaphores = [signal_semaphore.semaphore()];
|
let present_id = {
|
||||||
let swapchains = [ctx.current_swapchain.swapchain];
|
let swapchain = ctx.current_swapchain.swapchain.lock();
|
||||||
let indices = [swapchain_index];
|
let queue = dev.present_queue().lock();
|
||||||
static PRESENT_ID: AtomicU64 = AtomicU64::new(1);
|
|
||||||
let mut present_ids = [PRESENT_ID
|
let wait_semaphores = [frame.release];
|
||||||
.fetch_add(1, std::sync::atomic::Ordering::Release)];
|
let swapchains = [*swapchain];
|
||||||
let mut present_id =
|
let indices = [frame.index];
|
||||||
vk::PresentIdKHR::default().present_ids(&present_ids);
|
static PRESENT_ID: AtomicU64 = AtomicU64::new(1);
|
||||||
let present_info = vk::PresentInfoKHR::default()
|
let mut present_ids = [PRESENT_ID
|
||||||
.image_indices(&indices)
|
.fetch_add(1, std::sync::atomic::Ordering::Release)];
|
||||||
.swapchains(&swapchains)
|
let mut present_id =
|
||||||
.wait_semaphores(&wait_semaphores)
|
vk::PresentIdKHR::default().present_ids(&present_ids);
|
||||||
.push_next(&mut present_id);
|
let present_info = vk::PresentInfoKHR::default()
|
||||||
dev.present_queue().with_locked(|queue| {
|
.image_indices(core::slice::from_ref(&frame.index))
|
||||||
dev.swapchain().queue_present(queue, &present_info)
|
.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()?;
|
||||||
|
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,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
future.block_until()?;
|
dev.dev().device_wait_idle();
|
||||||
khr::present_wait::Device::new(
|
|
||||||
&self.vulkan.instance.instance,
|
|
||||||
&self.vulkan.device.device,
|
|
||||||
)
|
|
||||||
.wait_for_present(
|
|
||||||
ctx.current_swapchain.swapchain,
|
|
||||||
present_ids[0],
|
|
||||||
u64::MAX,
|
|
||||||
)?;
|
|
||||||
// dev.dev().device_wait_idle();
|
|
||||||
drop(ready_semaphore);
|
|
||||||
drop(signal_semaphore);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//unsafe {dev.dev().}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,220 @@
|
||||||
use super::Device2 as Device;
|
use std::{
|
||||||
|
future::Future,
|
||||||
|
marker::PhantomData,
|
||||||
|
sync::{atomic::AtomicU32, Arc},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Device;
|
||||||
use ash::{prelude::*, vk};
|
use ash::{prelude::*, vk};
|
||||||
|
use crossbeam::channel::{Receiver, Sender};
|
||||||
|
|
||||||
|
type Message = (Arc<Fence>, std::task::Waker);
|
||||||
|
|
||||||
|
pub struct SyncThreadpool {
|
||||||
|
channel: (Sender<Message>, Receiver<Message>),
|
||||||
|
timeout: u64,
|
||||||
|
thread_dies_after: Duration,
|
||||||
|
max_threads: u32,
|
||||||
|
num_threads: Arc<AtomicU32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyncThreadpool {
|
||||||
|
pub fn new() -> SyncThreadpool {
|
||||||
|
Self::with_max_threads(512)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_max_threads(max_threads: u32) -> SyncThreadpool {
|
||||||
|
Self {
|
||||||
|
// 0-capacity channel to ensure immediate consumption of fences
|
||||||
|
channel: crossbeam::channel::bounded(0),
|
||||||
|
max_threads,
|
||||||
|
num_threads: Arc::new(AtomicU32::new(0)),
|
||||||
|
timeout: u64::MAX,
|
||||||
|
thread_dies_after: Duration::from_secs(5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_spawn_thread(&self) -> Option<()> {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
match self.num_threads.fetch_update(
|
||||||
|
Ordering::Release,
|
||||||
|
Ordering::Acquire,
|
||||||
|
|i| {
|
||||||
|
if i < self.max_threads {
|
||||||
|
Some(i + 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Ok(tid) => {
|
||||||
|
struct SyncThread {
|
||||||
|
timeout: u64,
|
||||||
|
thread_dies_after: Duration,
|
||||||
|
num_threads: Arc<AtomicU32>,
|
||||||
|
rx: Receiver<Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SyncThread {
|
||||||
|
fn run(self, barrier: Arc<std::sync::Barrier>) {
|
||||||
|
tracing::info!("spawned new sync thread");
|
||||||
|
barrier.wait();
|
||||||
|
while let Ok((fence, waker)) =
|
||||||
|
self.rx.recv_timeout(self.thread_dies_after)
|
||||||
|
{
|
||||||
|
tracing::info!(
|
||||||
|
"received ({:?}, {:?})",
|
||||||
|
fence,
|
||||||
|
waker
|
||||||
|
);
|
||||||
|
loop {
|
||||||
|
match fence.wait_on(Some(self.timeout)) {
|
||||||
|
Ok(_) => {
|
||||||
|
waker.wake();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(vk::Result::TIMEOUT) => {}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::error!(
|
||||||
|
"failed to wait on fence in waiter thread: {err}"
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.num_threads.fetch_sub(1, Ordering::AcqRel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let thread = SyncThread {
|
||||||
|
timeout: self.timeout,
|
||||||
|
thread_dies_after: self.thread_dies_after,
|
||||||
|
num_threads: self.num_threads.clone(),
|
||||||
|
rx: self.channel.1.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let barrier = Arc::new(std::sync::Barrier::new(2));
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name(format!("fence-waiter-{tid}"))
|
||||||
|
.spawn({
|
||||||
|
let barrier = barrier.clone();
|
||||||
|
move || {
|
||||||
|
thread.run(barrier);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
barrier.wait();
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
tracing::error!(
|
||||||
|
"sync-threadpool exceeded local thread limit ({})",
|
||||||
|
self.max_threads
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_waiter(&self, fence: Arc<Fence>, waker: std::task::Waker) {
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
let mut msg = (fence, waker);
|
||||||
|
while let Err(err) = self.channel.0.try_send(msg) {
|
||||||
|
match err {
|
||||||
|
crossbeam::channel::TrySendError::Full(msg2) => {
|
||||||
|
msg = msg2;
|
||||||
|
self.try_spawn_thread();
|
||||||
|
}
|
||||||
|
crossbeam::channel::TrySendError::Disconnected(_) => {
|
||||||
|
//tracing::error!("sync-threadpool channel disconnected?");
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Semaphore {
|
pub struct Semaphore {
|
||||||
device: Device,
|
device: Device,
|
||||||
inner: vk::Semaphore,
|
inner: vk::Semaphore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Fence {
|
||||||
|
dev: Device,
|
||||||
|
fence: vk::Fence,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Fence {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Fence").field("fence", &self.fence).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Fence {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
self.dev.dev().destroy_fence(self.fence, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fence {
|
||||||
|
unsafe fn new(dev: Device, fence: vk::Fence) -> Fence {
|
||||||
|
Self { dev, fence }
|
||||||
|
}
|
||||||
|
pub fn create(dev: Device) -> VkResult<Fence> {
|
||||||
|
unsafe {
|
||||||
|
Ok(Self::new(
|
||||||
|
dev.clone(),
|
||||||
|
dev.dev()
|
||||||
|
.create_fence(&vk::FenceCreateInfo::default(), None)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn create_signaled(dev: Device) -> VkResult<Fence> {
|
||||||
|
unsafe {
|
||||||
|
Ok(Self::new(
|
||||||
|
dev.clone(),
|
||||||
|
dev.dev().create_fence(
|
||||||
|
&vk::FenceCreateInfo::default()
|
||||||
|
.flags(vk::FenceCreateFlags::SIGNALED),
|
||||||
|
None,
|
||||||
|
)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn wait_on(&self, timeout: Option<u64>) -> Result<(), vk::Result> {
|
||||||
|
use core::slice::from_ref;
|
||||||
|
unsafe {
|
||||||
|
self.dev.dev().wait_for_fences(
|
||||||
|
from_ref(&self.fence),
|
||||||
|
true,
|
||||||
|
timeout.unwrap_or(u64::MAX),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn fence(&self) -> vk::Fence {
|
||||||
|
self.fence
|
||||||
|
}
|
||||||
|
pub fn is_signaled(&self) -> bool {
|
||||||
|
unsafe { self.dev.dev().get_fence_status(self.fence).unwrap_or(false) }
|
||||||
|
}
|
||||||
|
pub fn reset(&self) -> Result<(), vk::Result> {
|
||||||
|
unsafe {
|
||||||
|
self.dev
|
||||||
|
.dev()
|
||||||
|
.reset_fences(core::slice::from_ref(&self.fence))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsRef<vk::Fence> for Fence {
|
||||||
|
fn as_ref(&self) -> &vk::Fence {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Semaphore {
|
impl Semaphore {
|
||||||
pub fn new(device: Device) -> VkResult<Self> {
|
pub fn new(device: Device) -> VkResult<Self> {
|
||||||
let mut type_info = vk::SemaphoreTypeCreateInfo::default()
|
let mut type_info = vk::SemaphoreTypeCreateInfo::default()
|
||||||
|
@ -40,3 +249,50 @@ impl Drop for Semaphore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FenceFuture<'a> {
|
||||||
|
fence: Arc<Fence>,
|
||||||
|
// lifetime parameter to prevent release of resources while future is pendign
|
||||||
|
_pd: PhantomData<&'a ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FenceFuture<'_> {
|
||||||
|
/// Unsafe because `fence` must not be destroyed while this future is live.
|
||||||
|
pub unsafe fn from_fence(device: Device, fence: vk::Fence) -> Self {
|
||||||
|
Self {
|
||||||
|
fence: Arc::new(Fence::new(device, fence)),
|
||||||
|
_pd: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new(fence: Arc<Fence>) -> Self {
|
||||||
|
Self {
|
||||||
|
fence,
|
||||||
|
_pd: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn block(&self) -> VkResult<()> {
|
||||||
|
self.fence.wait_on(None)?;
|
||||||
|
self.fence.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for FenceFuture<'_> {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Self::Output> {
|
||||||
|
if self.fence.is_signaled() {
|
||||||
|
tracing::info!("fence ({:?}) is signaled", self.fence);
|
||||||
|
_ = self.fence.reset();
|
||||||
|
std::task::Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
self.fence
|
||||||
|
.dev
|
||||||
|
.sync_threadpool()
|
||||||
|
.spawn_waiter(self.fence.clone(), cx.waker().clone());
|
||||||
|
std::task::Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue