From 29258aed7b5b496d6a005f764ef1f0916afc8d94 Mon Sep 17 00:00:00 2001
From: Janis <janis@nirgendwo.xyz>
Date: Sun, 15 Dec 2024 22:24:18 +0100
Subject: [PATCH] stuff?

---
 Cargo.toml                      |   1 +
 crates/renderer/Cargo.toml      |   1 +
 crates/renderer/src/commands.rs | 205 ++++++++++++++++++++++++++++++++
 crates/renderer/src/images.rs   | 137 +++++++++++++++++++++
 crates/renderer/src/lib.rs      |  37 +++++-
 crates/renderer/src/sync.rs     |  42 +++++++
 6 files changed, 420 insertions(+), 3 deletions(-)
 create mode 100644 crates/renderer/src/commands.rs
 create mode 100644 crates/renderer/src/images.rs
 create mode 100644 crates/renderer/src/sync.rs

diff --git a/Cargo.toml b/Cargo.toml
index 783b176..e03758d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,6 +20,7 @@ vk-sync = "0.1.6"
 winit = "0.30.5"
 tinyvec = "1.8"
 rand = "0.8.5"
+tokio = "1.42.0"
 
 futures = "0.3"
 smol = "2.0"
diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml
index 6db2b27..6ce57cc 100644
--- a/crates/renderer/Cargo.toml
+++ b/crates/renderer/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 [dependencies]
 tinyvec = {workspace = true}
 rand = {workspace = true}
+tokio = {workspace = true, features = ["rt", "sync"]}
 dyn-clone = "1"
 anyhow = "1.0.89"
 ash = "0.38.0"
diff --git a/crates/renderer/src/commands.rs b/crates/renderer/src/commands.rs
new file mode 100644
index 0000000..fa5978a
--- /dev/null
+++ b/crates/renderer/src/commands.rs
@@ -0,0 +1,205 @@
+use std::{future::Future, marker::PhantomData};
+
+use super::{Device2 as Device, Queue};
+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 {
+    device: Device,
+    pool: vk::CommandPool,
+}
+
+impl Drop for SingleUseCommandPool {
+    fn drop(&mut self) {
+        unsafe {
+            self.device.dev().destroy_command_pool(self.pool, None);
+        }
+    }
+}
+
+impl SingleUseCommandPool {
+    pub fn new(device: Device, family_index: u32) -> VkResult<Self> {
+        let pool_info = vk::CommandPoolCreateInfo::default()
+            .queue_family_index(family_index)
+            .flags(vk::CommandPoolCreateFlags::TRANSIENT);
+
+        let pool =
+            unsafe { device.dev().create_command_pool(&pool_info, None)? };
+
+        Ok(Self { device, pool })
+    }
+
+    pub fn pool(&self) -> vk::CommandPool {
+        self.pool
+    }
+}
+
+pub struct SingleUseCommand {
+    device: Device,
+    pool: vk::CommandPool,
+    buffer: vk::CommandBuffer,
+}
+
+impl Drop for SingleUseCommand {
+    fn drop(&mut self) {
+        unsafe {
+            self.device
+                .dev()
+                .free_command_buffers(self.pool, &[self.buffer])
+        };
+    }
+}
+
+impl SingleUseCommand {
+    pub fn new(device: Device, pool: vk::CommandPool) -> VkResult<Self> {
+        let buffer = unsafe {
+            let alloc_info = vk::CommandBufferAllocateInfo::default()
+                .command_buffer_count(1)
+                .command_pool(pool)
+                .level(vk::CommandBufferLevel::PRIMARY);
+            let buffer = device.dev().allocate_command_buffers(&alloc_info)?[0];
+
+            let begin_info = vk::CommandBufferBeginInfo::default()
+                .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT);
+            device.dev().begin_command_buffer(buffer, &begin_info)?;
+
+            buffer
+        };
+        Ok(Self {
+            device,
+            pool,
+            buffer,
+        })
+    }
+
+    pub fn command_buffer(&self) -> vk::CommandBuffer {
+        self.buffer
+    }
+
+    fn submit_fence(
+        &self,
+        queue: Queue,
+        wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
+        signal: Option<vk::Semaphore>,
+    ) -> VkResult<vk::Fence> {
+        unsafe { self.device.dev().end_command_buffer(self.buffer)? };
+
+        let buffers = [self.buffer];
+        let mut submit_info =
+            vk::SubmitInfo::default().command_buffers(&buffers);
+
+        if let Some(semaphore) = signal.as_ref() {
+            // SAFETY: T and [T;1] have the same layout
+            submit_info = submit_info.signal_semaphores(unsafe {
+                core::mem::transmute::<&vk::Semaphore, &[vk::Semaphore; 1]>(
+                    semaphore,
+                )
+            });
+        }
+        if let Some((semaphore, stage)) = wait.as_ref() {
+            submit_info = submit_info
+                .wait_semaphores(core::slice::from_ref(semaphore))
+                .wait_dst_stage_mask(core::slice::from_ref(stage));
+        }
+
+        let fence = {
+            let create_info = vk::FenceCreateInfo::default();
+            unsafe { self.device.dev().create_fence(&create_info, None)? }
+        };
+
+        queue.with_locked(|queue| unsafe {
+            self.device.dev().queue_submit(queue, &[submit_info], fence)
+        })?;
+
+        Ok(fence)
+    }
+
+    pub fn submit_async<'a>(
+        &'a self,
+        queue: Queue,
+        wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
+        signal: Option<vk::Semaphore>,
+    ) -> VkResult<FenceFuture<'a>> {
+        let device = self.device.clone();
+        let fence = self.submit_fence(queue, wait, signal)?;
+
+        Ok(unsafe { FenceFuture::from_fence(device, fence) })
+    }
+
+    pub fn submit_blocking(
+        self,
+        queue: Queue,
+        wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
+        signal: Option<vk::Semaphore>,
+    ) -> VkResult<()> {
+        let device = self.device.clone();
+        let fence = self.submit_fence(queue, wait, signal)?;
+
+        unsafe {
+            device.dev().wait_for_fences(&[fence], true, u64::MAX)?;
+            device.dev().destroy_fence(fence, None);
+        }
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    async fn async_submit(cmd: SingleUseCommand, queue: Queue) {
+        cmd.submit_async(queue, None, None).unwrap().await;
+    }
+}
diff --git a/crates/renderer/src/images.rs b/crates/renderer/src/images.rs
new file mode 100644
index 0000000..d407b77
--- /dev/null
+++ b/crates/renderer/src/images.rs
@@ -0,0 +1,137 @@
+use super::{Device2 as Device, Queue, VkAllocator};
+use ash::{prelude::*, vk};
+use vk_mem::Alloc;
+
+pub struct Image2D {
+    alloc: VkAllocator,
+    image: vk::Image,
+    size: vk::Extent2D,
+    mip_levels: u32,
+    format: vk::Format,
+    allocation: vk_mem::Allocation,
+}
+
+impl Image2D {
+    pub fn new_exclusive(
+        alloc: VkAllocator,
+        extent: vk::Extent2D,
+        mip_levels: u32,
+        array_layers: u32,
+        format: vk::Format,
+        tiling: vk::ImageTiling,
+        usage: vk::ImageUsageFlags,
+        memory_usage: vk_mem::MemoryUsage,
+        alloc_flags: vk_mem::AllocationCreateFlags,
+    ) -> VkResult<Image2D> {
+        let create_info = vk::ImageCreateInfo::default()
+            .array_layers(array_layers)
+            .mip_levels(mip_levels)
+            .extent(vk::Extent3D {
+                width: extent.width,
+                height: extent.height,
+                depth: 1,
+            })
+            .image_type(vk::ImageType::TYPE_2D)
+            .format(format)
+            .tiling(tiling)
+            .initial_layout(vk::ImageLayout::UNDEFINED)
+            .usage(usage)
+            .sharing_mode(vk::SharingMode::EXCLUSIVE)
+            .samples(vk::SampleCountFlags::TYPE_1);
+
+        let alloc_info = vk_mem::AllocationCreateInfo {
+            usage: memory_usage,
+            flags: alloc_flags,
+            ..Default::default()
+        };
+
+        let (image, allocation) =
+            unsafe { alloc.create_image(&create_info, &alloc_info)? };
+
+        Ok(Self {
+            alloc,
+            image,
+            size: extent,
+            mip_levels,
+            format,
+            allocation,
+        })
+    }
+
+    pub fn view(
+        &self,
+        device: &Device,
+        aspect: vk::ImageAspectFlags,
+    ) -> VkResult<vk::ImageView> {
+        let create_info = vk::ImageViewCreateInfo::default()
+            .image(self.image)
+            .view_type(vk::ImageViewType::TYPE_2D)
+            .format(self.format)
+            .components(vk::ComponentMapping::default())
+            .subresource_range(
+                vk::ImageSubresourceRange::default()
+                    .aspect_mask(aspect)
+                    .base_mip_level(0)
+                    .level_count(self.mip_levels)
+                    .base_array_layer(0)
+                    .layer_count(1),
+            );
+
+        let view =
+            unsafe { device.dev().create_image_view(&create_info, None)? };
+
+        Ok(view)
+    }
+
+    pub fn image(&self) -> vk::Image {
+        self.image
+    }
+}
+
+impl Drop for Image2D {
+    fn drop(&mut self) {
+        unsafe {
+            self.alloc.destroy_image(self.image, &mut self.allocation);
+        }
+    }
+}
+
+pub struct QueueOwnership {
+    src: u32,
+    dst: u32,
+}
+
+pub fn image_barrier<'a>(
+    image: vk::Image,
+    aspects: vk::ImageAspectFlags,
+    src_stage: vk::PipelineStageFlags2,
+    src_access: vk::AccessFlags2,
+    dst_stage: vk::PipelineStageFlags2,
+    dst_access: vk::AccessFlags2,
+    old_layout: vk::ImageLayout,
+    new_layout: vk::ImageLayout,
+    queue_ownership_op: Option<QueueOwnership>,
+) -> vk::ImageMemoryBarrier2<'a> {
+    let (src_family, dst_family) = queue_ownership_op
+        .map(|t| (t.src, t.dst))
+        .unwrap_or((vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED));
+
+    vk::ImageMemoryBarrier2::default()
+        .image(image)
+        .subresource_range(
+            vk::ImageSubresourceRange::default()
+                .aspect_mask(aspects)
+                .base_mip_level(0)
+                .base_array_layer(0)
+                .level_count(vk::REMAINING_MIP_LEVELS)
+                .layer_count(vk::REMAINING_ARRAY_LAYERS),
+        )
+        .src_stage_mask(src_stage)
+        .src_access_mask(src_access)
+        .dst_stage_mask(dst_stage)
+        .dst_access_mask(dst_access)
+        .dst_queue_family_index(dst_family)
+        .src_queue_family_index(src_family)
+        .old_layout(old_layout)
+        .new_layout(new_layout)
+}
diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs
index cbac9af..2aebf0f 100644
--- a/crates/renderer/src/lib.rs
+++ b/crates/renderer/src/lib.rs
@@ -5,7 +5,7 @@ use std::{
     ffi::{CStr, CString},
     fmt::Debug,
     marker::PhantomData,
-    sync::{Arc, Mutex},
+    sync::{atomic::AtomicU64, Arc, Mutex},
 };
 
 use ash::{
@@ -967,6 +967,22 @@ impl Vulkan {
                     .mesh_shader(true)
                     .task_shader(true),
             )
+            .with_extension(
+                make_extention_properties(
+                    ash::khr::present_id::NAME,
+                    ash::khr::present_id::SPEC_VERSION,
+                ),
+                vk::PhysicalDevicePresentIdFeaturesKHR::default()
+                    .present_id(true),
+            )
+            .with_extension(
+                make_extention_properties(
+                    ash::khr::present_wait::NAME,
+                    ash::khr::present_wait::SPEC_VERSION,
+                ),
+                vk::PhysicalDevicePresentWaitFeaturesKHR::default()
+                    .present_wait(true),
+            )
             .with_extension(
                 make_extention_properties(
                     ash::ext::index_type_uint8::NAME,
@@ -1649,16 +1665,31 @@ impl Renderer {
                 let wait_semaphores = [signal_semaphore.semaphore()];
                 let swapchains = [ctx.current_swapchain.swapchain];
                 let indices = [swapchain_index];
+                static PRESENT_ID: AtomicU64 = AtomicU64::new(1);
+                let mut present_ids = [PRESENT_ID
+                    .fetch_add(1, std::sync::atomic::Ordering::Release)];
+                let mut present_id =
+                    vk::PresentIdKHR::default().present_ids(&present_ids);
                 let present_info = vk::PresentInfoKHR::default()
                     .image_indices(&indices)
                     .swapchains(&swapchains)
-                    .wait_semaphores(&wait_semaphores);
+                    .wait_semaphores(&wait_semaphores)
+                    .push_next(&mut present_id);
                 dev.present_queue().with_locked(|queue| {
                     dev.swapchain().queue_present(queue, &present_info)
                 })?;
 
                 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);
             }
diff --git a/crates/renderer/src/sync.rs b/crates/renderer/src/sync.rs
new file mode 100644
index 0000000..5fc93aa
--- /dev/null
+++ b/crates/renderer/src/sync.rs
@@ -0,0 +1,42 @@
+use super::Device2 as Device;
+use ash::{prelude::*, vk};
+
+pub struct Semaphore {
+    device: Device,
+    inner: vk::Semaphore,
+}
+
+impl Semaphore {
+    pub fn new(device: Device) -> VkResult<Self> {
+        let mut type_info = vk::SemaphoreTypeCreateInfo::default()
+            .semaphore_type(vk::SemaphoreType::BINARY);
+        let create_info =
+            vk::SemaphoreCreateInfo::default().push_next(&mut type_info);
+        let inner =
+            unsafe { device.dev().create_semaphore(&create_info, None)? };
+
+        Ok(Self { device, inner })
+    }
+    pub fn new_timeline(device: Device, value: u64) -> VkResult<Self> {
+        let mut type_info = vk::SemaphoreTypeCreateInfo::default()
+            .semaphore_type(vk::SemaphoreType::TIMELINE)
+            .initial_value(value);
+        let create_info =
+            vk::SemaphoreCreateInfo::default().push_next(&mut type_info);
+        let inner =
+            unsafe { device.dev().create_semaphore(&create_info, None)? };
+
+        Ok(Self { device, inner })
+    }
+    pub fn semaphore(&self) -> vk::Semaphore {
+        self.inner
+    }
+}
+
+impl Drop for Semaphore {
+    fn drop(&mut self) {
+        unsafe {
+            self.device.dev().destroy_semaphore(self.inner, None);
+        }
+    }
+}