This commit is contained in:
Janis 2024-12-15 22:24:18 +01:00
parent 99c86c0c85
commit 29258aed7b
6 changed files with 420 additions and 3 deletions

View file

@ -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"

View file

@ -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"

View file

@ -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;
}
}

View file

@ -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)
}

View file

@ -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);
}

View file

@ -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);
}
}
}