yeah........

This commit is contained in:
janis 2026-04-02 17:10:37 +02:00
parent d91ba8c1b7
commit b0f6c7a1b3
12 changed files with 316 additions and 213 deletions

View file

@ -1,14 +1,11 @@
use std::{ use std::borrow::Cow;
borrow::Cow,
ops::{Deref, DerefMut},
sync::Arc,
};
use ash::{prelude::VkResult, vk}; use ash::vk;
use gpu_allocator::vulkan::AllocationScheme;
use crate::{ use crate::{
Device, Device,
device::{Allocation, DeviceObject, QueueFlags}, device::{Allocation, AllocationStrategy, DeviceObject, QueueFlags},
}; };
#[derive(Clone)] #[derive(Clone)]
@ -18,7 +15,9 @@ pub struct BufferDesc {
pub size: u64, pub size: u64,
pub usage: vk::BufferUsageFlags, pub usage: vk::BufferUsageFlags,
pub queue_families: QueueFlags, pub queue_families: QueueFlags,
pub mem_location: gpu_allocator::MemoryLocation, pub mem_location: gpu_allocator::MemoryLocation,
pub alloc_scheme: AllocationStrategy,
} }
impl std::hash::Hash for BufferDesc { impl std::hash::Hash for BufferDesc {
@ -40,6 +39,7 @@ impl std::fmt::Debug for BufferDesc {
.field("usage", &self.usage) .field("usage", &self.usage)
.field("queue_families", &self.queue_families) .field("queue_families", &self.queue_families)
.field("mem_location", &self.mem_location) .field("mem_location", &self.mem_location)
.field("alloc_scheme", &self.alloc_scheme)
.finish() .finish()
} }
} }
@ -66,6 +66,7 @@ impl Default for BufferDesc {
usage: Default::default(), usage: Default::default(),
queue_families: QueueFlags::empty(), queue_families: QueueFlags::empty(),
mem_location: gpu_allocator::MemoryLocation::Unknown, mem_location: gpu_allocator::MemoryLocation::Unknown,
alloc_scheme: AllocationStrategy::default(),
} }
} }
} }
@ -97,7 +98,12 @@ impl Buffer {
requirements, requirements,
location: desc.mem_location, location: desc.mem_location,
linear: true, linear: true,
allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, allocation_scheme: match desc.alloc_scheme {
AllocationStrategy::AllocatorManaged => {
AllocationScheme::GpuAllocatorManaged
}
AllocationStrategy::Dedicated => AllocationScheme::DedicatedBuffer(buffer),
},
})?; })?;
Ok(Self { Ok(Self {
@ -146,7 +152,7 @@ impl Buffer {
} }
} }
pub fn buffer(&self) -> vk::Buffer { pub fn raw(&self) -> vk::Buffer {
*self.buffer *self.buffer
} }
pub fn len(&self) -> u64 { pub fn len(&self) -> u64 {

View file

@ -33,7 +33,7 @@ impl Drop for SingleUseCommandPool {
impl SingleUseCommandPool { impl SingleUseCommandPool {
pub fn new(device: Device, queue: Queue) -> VkResult<Arc<Self>> { pub fn new(device: Device, queue: Queue) -> VkResult<Arc<Self>> {
let pool_info = vk::CommandPoolCreateInfo::default() let pool_info = vk::CommandPoolCreateInfo::default()
.queue_family_index(queue.family()) .queue_family_index(queue.family_index())
.flags(vk::CommandPoolCreateFlags::TRANSIENT); .flags(vk::CommandPoolCreateFlags::TRANSIENT);
let pool = unsafe { device.dev().create_command_pool(&pool_info, None)? }; let pool = unsafe { device.dev().create_command_pool(&pool_info, None)? };
@ -110,7 +110,7 @@ impl<T: HasQueue> CommandList<T> {
self.0[0] self.0[0]
.device() .device()
.dev() .dev()
.queue_submit(queue, &[info], fence.fence()) .queue_submit(queue.raw(), &[info], fence.raw())
})?; })?;
Ok(FenceFuture::<'a>::new(fence)) Ok(FenceFuture::<'a>::new(fence))
@ -257,7 +257,7 @@ impl SingleUseCommand {
self.pool.queue().with_locked(|queue| unsafe { self.pool.queue().with_locked(|queue| unsafe {
self.device() self.device()
.dev() .dev()
.queue_submit(queue, &[submit_info], fence) .queue_submit(queue.raw(), &[submit_info], fence)
})?; })?;
self.state.set_pending(); self.state.set_pending();
@ -329,7 +329,7 @@ pub mod traits {
queue.with_locked(|queue| unsafe { queue.with_locked(|queue| unsafe {
self.device() self.device()
.dev() .dev()
.queue_submit(queue, &[submit_info], fence.fence()) .queue_submit(queue.raw(), &[submit_info], fence.raw())
})?; })?;
tracing::trace!("submitted queue {:?} and fence {:?}", queue, fence); tracing::trace!("submitted queue {:?} and fence {:?}", queue, fence);
@ -888,7 +888,7 @@ mod command_pools {
) -> Result<CommandBufferFuture, Error> { ) -> Result<CommandBufferFuture, Error> {
let this = ManuallyDrop::new(self); let this = ManuallyDrop::new(self);
if queue.1 != this.pool.family { if queue.family_index() != this.pool.family {
tracing::error!("attempted to submit commandbuffer to incompatible queue."); tracing::error!("attempted to submit commandbuffer to incompatible queue.");
return Err(Error::InvalidQueueSubmission); return Err(Error::InvalidQueueSubmission);
} }
@ -911,7 +911,7 @@ mod command_pools {
queue.with_locked(|queue| unsafe { queue.with_locked(|queue| unsafe {
this.device() this.device()
.dev() .dev()
.queue_submit(queue, &[submit_info], fence.fence()) .queue_submit(queue.raw(), &[submit_info], fence.raw())
})?; })?;
Ok(CommandBufferFuture { Ok(CommandBufferFuture {
@ -931,7 +931,7 @@ mod command_pools {
} }
impl CommandBufferFuture { impl CommandBufferFuture {
pub fn block(&self) -> VkResult<()> { pub fn block(&self) -> crate::Result<()> {
self.inner.block() self.inner.block()
} }
} }

View file

@ -118,6 +118,18 @@ impl DeviceHandle for GpuAllocation {
} }
} }
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AllocationStrategy {
#[default]
/// Let gpu_allocator manage the memory for this allocation, sub-allocating
/// from larger blocks as needed.
AllocatorManaged,
/// Allocate a dedicated block of memory for this allocation. This is
/// recommended for long-lived resources or resources with specific memory
/// requirements.
Dedicated,
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum Allocation { pub(crate) enum Allocation {
Owned(DeviceObject<GpuAllocation>), Owned(DeviceObject<GpuAllocation>),
@ -144,7 +156,6 @@ impl Allocation {
} }
pub struct DeviceInner { pub struct DeviceInner {
pub(crate) alloc: vk_mem::Allocator,
pub(crate) alloc2: Mutex<gpu_allocator::vulkan::Allocator>, pub(crate) alloc2: Mutex<gpu_allocator::vulkan::Allocator>,
pub(crate) raw: ash::Device, pub(crate) raw: ash::Device,
pub(crate) adapter: PhysicalDeviceInfo, pub(crate) adapter: PhysicalDeviceInfo,
@ -439,13 +450,6 @@ impl PhysicalDeviceInfo {
let inner = DeviceInner { let inner = DeviceInner {
raw: device.clone(), raw: device.clone(),
alloc: unsafe {
vk_mem::Allocator::new(vk_mem::AllocatorCreateInfo::new(
&instance.inner.raw,
&device,
self.pdev,
))?
},
alloc2: Mutex::new(alloc2), alloc2: Mutex::new(alloc2),
instance: instance.clone(), instance: instance.clone(),
adapter: self, adapter: self,
@ -531,9 +535,6 @@ impl DeviceInner {
pub fn sync_threadpool(&self) -> &sync::SyncThreadpool { pub fn sync_threadpool(&self) -> &sync::SyncThreadpool {
&self.sync_threadpool &self.sync_threadpool
} }
pub fn alloc(&self) -> &vk_mem::Allocator {
&self.alloc
}
pub fn dev(&self) -> &ash::Device { pub fn dev(&self) -> &ash::Device {
&self.raw &self.raw
} }

View file

@ -1,13 +1,14 @@
use std::{collections::BTreeMap, sync::Arc}; use std::{collections::BTreeMap, sync::Arc};
use ash::{prelude::VkResult, vk}; use ash::vk;
use gpu_allocator::MemoryLocation;
use indexmap::IndexMap; use indexmap::IndexMap;
use crate::{ use crate::{
EguiState, EguiState,
buffers::{Buffer, BufferDesc}, buffers::{Buffer, BufferDesc},
commands::traits::CommandBufferExt, commands::traits::CommandBufferExt,
device::{self, DeviceOwned}, device::{self, AllocationStrategy},
images::{Image, ImageDesc, ImageViewDesc}, images::{Image, ImageDesc, ImageViewDesc},
render_graph::{ render_graph::{
Access, Barrier, GraphResourceDesc, GraphResourceId, PassDesc, RecordFn, RenderContext, Access, Barrier, GraphResourceDesc, GraphResourceId, PassDesc, RecordFn, RenderContext,
@ -23,7 +24,7 @@ pub fn egui_pre_pass(
textures: &mut crate::texture::TextureManager, textures: &mut crate::texture::TextureManager,
egui_state: &mut EguiState, egui_state: &mut EguiState,
output: &egui::FullOutput, output: &egui::FullOutput,
) -> VkResult<()> { ) -> crate::Result<()> {
// allocate resource ids for textures in tessellated list (imported from texture manager) // allocate resource ids for textures in tessellated list (imported from texture manager)
// define accesses for resource ids // define accesses for resource ids
@ -50,7 +51,8 @@ pub fn egui_pre_pass(
depth: 1, depth: 1,
}, },
usage: vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST, usage: vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::GpuOnly,
alloc_scheme: AllocationStrategy::Dedicated,
..Default::default() ..Default::default()
}, },
)?; )?;
@ -94,10 +96,7 @@ pub fn egui_pre_pass(
size: staging_size as u64, size: staging_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_SRC, usage: vk::BufferUsageFlags::TRANSFER_SRC,
queue_families: device::QueueFlags::empty(), queue_families: device::QueueFlags::empty(),
mem_usage: vk_mem::MemoryUsage::AutoPreferHost, mem_location: MemoryLocation::CpuToGpu,
alloc_flags: vk_mem::AllocationCreateFlags::MAPPED
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
..Default::default() ..Default::default()
}, },
)?; )?;
@ -115,14 +114,14 @@ pub fn egui_pre_pass(
}, },
usage: vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST, usage: vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::TRANSFER_DST,
queue_families: device::QueueFlags::empty(), queue_families: device::QueueFlags::empty(),
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::GpuOnly,
..Default::default() ..Default::default()
}, },
)?); )?);
let aliased_images = { let aliased_images = {
tracing::trace!("mmap-ing staging buffer"); tracing::trace!("mmap-ing staging buffer");
let mut staging_map = staging_buffer.map()?; let mut staging_map = staging_buffer.map().unwrap();
let mut offset = 0; let mut offset = 0;
output output
@ -214,7 +213,7 @@ pub fn egui_pre_pass(
let texture = textures.get(&id).and_then(|id| ctx.get_image(*id)).unwrap(); let texture = textures.get(&id).and_then(|id| ctx.get_image(*id)).unwrap();
let image: Barrier = image_barrier( let image: Barrier = image_barrier(
alias.handle(), alias.raw(),
alias.format(), alias.format(),
Access { Access {
stage: vk::PipelineStageFlags2::NONE, stage: vk::PipelineStageFlags2::NONE,
@ -237,8 +236,8 @@ pub fn egui_pre_pass(
} }
ctx.cmd.copy_buffer_to_image( ctx.cmd.copy_buffer_to_image(
staging_buffer.handle(), staging_buffer.raw(),
alias.handle(), alias.raw(),
vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::TRANSFER_DST_OPTIMAL,
&[vk::BufferImageCopy { &[vk::BufferImageCopy {
buffer_offset: offset as u64, buffer_offset: offset as u64,
@ -255,7 +254,7 @@ pub fn egui_pre_pass(
); );
let from_barrier = image_barrier( let from_barrier = image_barrier(
alias.handle(), alias.raw(),
alias.format(), alias.format(),
Access { Access {
stage: vk::PipelineStageFlags2::TRANSFER, stage: vk::PipelineStageFlags2::TRANSFER,
@ -271,7 +270,7 @@ pub fn egui_pre_pass(
); );
let to_barrier = image_barrier( let to_barrier = image_barrier(
texture.handle(), texture.raw(),
texture.format(), texture.format(),
Access { Access {
stage: vk::PipelineStageFlags2::NONE, stage: vk::PipelineStageFlags2::NONE,
@ -304,9 +303,9 @@ pub fn egui_pre_pass(
); );
} }
ctx.cmd.copy_images( ctx.cmd.copy_images(
alias.handle(), alias.raw(),
vk::ImageLayout::TRANSFER_SRC_OPTIMAL, vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
texture.handle(), texture.raw(),
vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::TRANSFER_DST_OPTIMAL,
&[vk::ImageCopy { &[vk::ImageCopy {
src_subresource: vk::ImageSubresourceLayers::default() src_subresource: vk::ImageSubresourceLayers::default()
@ -330,7 +329,7 @@ pub fn egui_pre_pass(
); );
let image: Barrier = image_barrier( let image: Barrier = image_barrier(
texture.handle(), texture.raw(),
texture.format(), texture.format(),
Access { Access {
stage: vk::PipelineStageFlags2::TRANSFER, stage: vk::PipelineStageFlags2::TRANSFER,
@ -397,7 +396,7 @@ pub fn egui_pass(
egui: &egui::Context, egui: &egui::Context,
output: egui::FullOutput, output: egui::FullOutput,
target: GraphResourceId, target: GraphResourceId,
) -> VkResult<Vec<texture::TextureId>> { ) -> crate::Result<Vec<texture::TextureId>> {
let draw_data = egui.tessellate(output.shapes, output.pixels_per_point); let draw_data = egui.tessellate(output.shapes, output.pixels_per_point);
#[repr(C)] #[repr(C)]
@ -461,16 +460,13 @@ pub fn egui_pass(
name: Some("egui-draw-staging".into()), name: Some("egui-draw-staging".into()),
size: staging_size as u64, size: staging_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_SRC, usage: vk::BufferUsageFlags::TRANSFER_SRC,
mem_usage: vk_mem::MemoryUsage::AutoPreferHost, mem_location: MemoryLocation::CpuToGpu,
alloc_flags: vk_mem::AllocationCreateFlags::MAPPED
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
..Default::default() ..Default::default()
}, },
)?; )?;
{ {
let mut map = staging.map()?; let map = staging.map_mut().unwrap();
let (st_vertices, rest) = map.split_at_mut(vertices_size); let (st_vertices, rest) = map.split_at_mut(vertices_size);
let (st_indices, st_drawcalls) = rest.split_at_mut(indices_size); let (st_indices, st_drawcalls) = rest.split_at_mut(indices_size);
@ -484,21 +480,21 @@ pub fn egui_pass(
name: Some("egui-draw-vertices".into()), name: Some("egui-draw-vertices".into()),
size: vertices_size as u64, size: vertices_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::GpuOnly,
..Default::default() ..Default::default()
})); }));
let indices = rg.add_resource(GraphResourceDesc::Buffer(BufferDesc { let indices = rg.add_resource(GraphResourceDesc::Buffer(BufferDesc {
name: Some("egui-draw-indices".into()), name: Some("egui-draw-indices".into()),
size: indices_size as u64, size: indices_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::GpuOnly,
..Default::default() ..Default::default()
})); }));
let draw_calls = rg.add_resource(GraphResourceDesc::Buffer(BufferDesc { let draw_calls = rg.add_resource(GraphResourceDesc::Buffer(BufferDesc {
name: Some("egui-draw-draw_calls".into()), name: Some("egui-draw-draw_calls".into()),
size: draw_calls_size as u64, size: draw_calls_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDIRECT_BUFFER, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDIRECT_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::GpuOnly,
..Default::default() ..Default::default()
})); }));
@ -508,13 +504,14 @@ pub fn egui_pass(
name: Some("egui-draw-texture_ids".into()), name: Some("egui-draw-texture_ids".into()),
size: (textures_indices.len() * size_of::<u32>()) as u64, size: (textures_indices.len() * size_of::<u32>()) as u64,
usage: vk::BufferUsageFlags::STORAGE_BUFFER, usage: vk::BufferUsageFlags::STORAGE_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::CpuToGpu,
alloc_flags: vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE,
..Default::default() ..Default::default()
}, },
)?; )?;
{ {
let mut map = texture_ids.map()?; let map = texture_ids
.map_mut()
.expect("texture id buffer should be device visible");
map.copy_from_slice(bytemuck::cast_slice(&textures_indices)); map.copy_from_slice(bytemuck::cast_slice(&textures_indices));
} }
@ -528,7 +525,7 @@ pub fn egui_pass(
vk::DescriptorImageInfo { vk::DescriptorImageInfo {
sampler: samplers.get_sampler(entry.as_sampler_desc()).unwrap(), sampler: samplers.get_sampler(entry.as_sampler_desc()).unwrap(),
image_view: texture image_view: texture
.get_view(ImageViewDesc { .create_view(ImageViewDesc {
kind: vk::ImageViewType::TYPE_2D, kind: vk::ImageViewType::TYPE_2D,
format: texture.format(), format: texture.format(),
aspect: vk::ImageAspectFlags::COLOR, aspect: vk::ImageAspectFlags::COLOR,
@ -536,14 +533,15 @@ pub fn egui_pass(
layer_range: (0..1).into(), layer_range: (0..1).into(),
..Default::default() ..Default::default()
}) })
.unwrap(), .unwrap()
.raw(),
image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let uniform_info = vk::DescriptorBufferInfo { let uniform_info = vk::DescriptorBufferInfo {
buffer: texture_ids.buffer(), buffer: texture_ids.raw(),
offset: 0, offset: 0,
range: texture_ids.len(), range: texture_ids.len(),
}; };
@ -609,8 +607,8 @@ pub fn egui_pass(
let target = ctx.get_image(target).unwrap(); let target = ctx.get_image(target).unwrap();
cmd.copy_buffers( cmd.copy_buffers(
staging.buffer(), staging.raw(),
vertices.buffer(), vertices.raw(),
&[vk::BufferCopy { &[vk::BufferCopy {
src_offset: 0, src_offset: 0,
dst_offset: 0, dst_offset: 0,
@ -618,8 +616,8 @@ pub fn egui_pass(
}], }],
); );
cmd.copy_buffers( cmd.copy_buffers(
staging.buffer(), staging.raw(),
indices.buffer(), indices.raw(),
&[vk::BufferCopy { &[vk::BufferCopy {
src_offset: vertices_size as u64, src_offset: vertices_size as u64,
dst_offset: 0, dst_offset: 0,
@ -627,8 +625,8 @@ pub fn egui_pass(
}], }],
); );
cmd.copy_buffers( cmd.copy_buffers(
staging.buffer(), staging.raw(),
draw_calls.buffer(), draw_calls.raw(),
&[vk::BufferCopy { &[vk::BufferCopy {
src_offset: (vertices_size + indices_size) as u64, src_offset: (vertices_size + indices_size) as u64,
dst_offset: 0, dst_offset: 0,
@ -638,7 +636,7 @@ pub fn egui_pass(
let barriers = [ let barriers = [
buffer_barrier( buffer_barrier(
vertices.handle(), vertices.raw(),
0, 0,
vertices.len(), vertices.len(),
Access::transfer_write(), Access::transfer_write(),
@ -646,7 +644,7 @@ pub fn egui_pass(
None, None,
), ),
buffer_barrier( buffer_barrier(
indices.handle(), indices.raw(),
0, 0,
indices.len(), indices.len(),
Access::transfer_write(), Access::transfer_write(),
@ -654,7 +652,7 @@ pub fn egui_pass(
None, None,
), ),
buffer_barrier( buffer_barrier(
draw_calls.handle(), draw_calls.raw(),
0, 0,
draw_calls.len(), draw_calls.len(),
Access::transfer_write(), Access::transfer_write(),
@ -671,12 +669,16 @@ pub fn egui_pass(
let color_attachment = &vk::RenderingAttachmentInfo::default() let color_attachment = &vk::RenderingAttachmentInfo::default()
.image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
.image_view(target.get_view(ImageViewDesc { .image_view(
target
.create_view(ImageViewDesc {
kind: vk::ImageViewType::TYPE_2D, kind: vk::ImageViewType::TYPE_2D,
format: target.format(), format: target.format(),
aspect: vk::ImageAspectFlags::COLOR, aspect: vk::ImageAspectFlags::COLOR,
..Default::default() ..Default::default()
})?) })?
.raw(),
)
.load_op(vk::AttachmentLoadOp::LOAD) .load_op(vk::AttachmentLoadOp::LOAD)
.store_op(vk::AttachmentStoreOp::STORE); .store_op(vk::AttachmentStoreOp::STORE);
@ -700,8 +702,8 @@ pub fn egui_pass(
.height(target.height() as f32)]); .height(target.height() as f32)]);
cmd.bind_pipeline(&pipeline); cmd.bind_pipeline(&pipeline);
cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32); cmd.bind_indices(indices.raw(), 0, vk::IndexType::UINT32);
cmd.bind_vertex_buffers(&[vertices.buffer()], &[0]); cmd.bind_vertex_buffers(&[vertices.raw()], &[0]);
cmd.push_constants( cmd.push_constants(
&pipeline_layout, &pipeline_layout,
vk::ShaderStageFlags::VERTEX, vk::ShaderStageFlags::VERTEX,
@ -716,7 +718,7 @@ pub fn egui_pass(
&[descriptor_set], &[descriptor_set],
); );
cmd.draw_indexed_indirect( cmd.draw_indexed_indirect(
draw_calls.buffer(), draw_calls.raw(),
0, 0,
num_draw_calls as u32, num_draw_calls as u32,
size_of::<vk::DrawIndexedIndirectCommand>() as u32, size_of::<vk::DrawIndexedIndirectCommand>() as u32,

View file

@ -1,21 +1,15 @@
use std::{ use std::{borrow::Cow, sync::Arc};
borrow::Cow,
collections::HashMap,
sync::{Arc, Weak},
};
use crate::{ use crate::{
define_device_owned_handle, device::{Allocation, AllocationStrategy, DeviceHandle, DeviceObject, QueueFlags},
device::{Allocation, DeviceHandle, DeviceObject, DeviceOwned, QueueFlags},
swapchain::Swapchain, swapchain::Swapchain,
util::weak_vec::WeakVec, util::weak_vec::WeakVec,
}; };
use super::Device; use super::Device;
use ash::{prelude::*, vk}; use ash::vk;
use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme}; use gpu_allocator::vulkan::{AllocationCreateDesc, AllocationScheme};
use parking_lot::Mutex; use parking_lot::Mutex;
use vk_mem::Alloc;
#[derive(Clone)] #[derive(Clone)]
pub struct ImageDesc { pub struct ImageDesc {
@ -33,6 +27,7 @@ pub struct ImageDesc {
pub layout: vk::ImageLayout, pub layout: vk::ImageLayout,
pub mem_location: gpu_allocator::MemoryLocation, pub mem_location: gpu_allocator::MemoryLocation,
pub alloc_scheme: AllocationStrategy,
} }
impl std::hash::Hash for ImageDesc { impl std::hash::Hash for ImageDesc {
@ -87,6 +82,7 @@ impl<'a> std::fmt::Debug for ImageDesc {
.field("queue_families", &self.queue_families) .field("queue_families", &self.queue_families)
.field("layout", &self.layout) .field("layout", &self.layout)
.field("mem_location", &self.mem_location) .field("mem_location", &self.mem_location)
.field("alloc_scheme", &self.alloc_scheme)
.finish() .finish()
} }
} }
@ -107,6 +103,7 @@ impl Default for ImageDesc {
queue_families: QueueFlags::empty(), queue_families: QueueFlags::empty(),
layout: vk::ImageLayout::UNDEFINED, layout: vk::ImageLayout::UNDEFINED,
mem_location: gpu_allocator::MemoryLocation::Unknown, mem_location: gpu_allocator::MemoryLocation::Unknown,
alloc_scheme: AllocationStrategy::AllocatorManaged,
} }
} }
} }
@ -168,7 +165,10 @@ impl Image {
requirements, requirements,
location: desc.mem_location, location: desc.mem_location,
linear: desc.tiling == vk::ImageTiling::LINEAR, linear: desc.tiling == vk::ImageTiling::LINEAR,
allocation_scheme: AllocationScheme::GpuAllocatorManaged, allocation_scheme: match desc.alloc_scheme {
AllocationStrategy::AllocatorManaged => AllocationScheme::GpuAllocatorManaged,
AllocationStrategy::Dedicated => AllocationScheme::DedicatedImage(image),
},
})?; })?;
Ok(Self { Ok(Self {
@ -316,6 +316,10 @@ impl Image {
Ok((image, requirements)) Ok((image, requirements))
} }
pub fn raw(&self) -> vk::Image {
self.image.image()
}
} }
impl Eq for Image {} impl Eq for Image {}
@ -359,35 +363,35 @@ impl Image {
self.image.allocation_mut() self.image.allocation_mut()
} }
/// technically, this ImageView belongs to the image and is managed by it. // /// technically, this ImageView belongs to the image and is managed by it.
pub fn get_view(&self, desc: ImageViewDesc) -> VkResult<vk::ImageView> { // pub fn get_view(&self, desc: ImageViewDesc) -> VkResult<vk::ImageView> {
use std::collections::hash_map::Entry::*; // use std::collections::hash_map::Entry::*;
match self.views.lock().entry(desc.hash_eq_copy()) { // match self.views.lock().entry(desc.hash_eq_copy()) {
Occupied(occupied) => Ok(*occupied.get()), // Occupied(occupied) => Ok(*occupied.get()),
Vacant(vacant) => { // Vacant(vacant) => {
let view = unsafe { // let view = unsafe {
let create_info = vk::ImageViewCreateInfo::default() // let create_info = vk::ImageViewCreateInfo::default()
.flags(desc.flags) // .flags(desc.flags)
.image(self.image()) // .image(self.image())
.view_type(vk::ImageViewType::TYPE_2D) // .view_type(vk::ImageViewType::TYPE_2D)
.format(desc.format) // .format(desc.format)
.components(desc.components) // .components(desc.components)
.subresource_range( // .subresource_range(
vk::ImageSubresourceRange::default() // vk::ImageSubresourceRange::default()
.aspect_mask(desc.aspect) // .aspect_mask(desc.aspect)
.base_mip_level(desc.mip_range.0) // .base_mip_level(desc.mip_range.0)
.level_count(desc.mip_range.count()) // .level_count(desc.mip_range.count())
.base_array_layer(desc.layer_range.0) // .base_array_layer(desc.layer_range.0)
.layer_count(desc.layer_range.count()), // .layer_count(desc.layer_range.count()),
); // );
self.device().dev().create_image_view(&create_info, None)? // self.device().dev().create_image_view(&create_info, None)?
}; // };
Ok(*vacant.insert(view)) // Ok(*vacant.insert(view))
} // }
} // }
} // }
pub fn create_view(&self, desc: ImageViewDesc) -> crate::Result<ImageView> { pub fn create_view(self: &Arc<Self>, mut desc: ImageViewDesc) -> crate::Result<ImageView> {
// validate // validate
if !view_kind_compatible(self.desc.kind, desc.kind) { if !view_kind_compatible(self.desc.kind, desc.kind) {
tracing::error!( tracing::error!(
@ -410,6 +414,10 @@ impl Image {
return Err(crate::Error::Unspecified); return Err(crate::Error::Unspecified);
} }
if desc.format == vk::Format::UNDEFINED {
desc.format = self.desc.format;
}
let create_info = vk::ImageViewCreateInfo::default() let create_info = vk::ImageViewCreateInfo::default()
.flags(desc.flags) .flags(desc.flags)
.image(self.image()) .image(self.image())
@ -428,10 +436,16 @@ impl Image {
let device = self.image.device(); let device = self.image.device();
let view = unsafe { device.raw.create_image_view(&create_info, None)? }; let view = unsafe { device.raw.create_image_view(&create_info, None)? };
ImageView::construct(self.device().clone(), view, desc.name) Ok(ImageView {
view: DeviceObject::new(view, device.clone(), desc.name.clone()),
desc,
image: self.clone(),
})
} }
} }
fn validate_image_view_format(image: &ImageDesc, view_format: vk::Format) -> bool {}
fn view_kind_compatible(image_kind: vk::ImageType, view_kind: vk::ImageViewType) -> bool { fn view_kind_compatible(image_kind: vk::ImageType, view_kind: vk::ImageViewType) -> bool {
use vk::ImageType as IT; use vk::ImageType as IT;
use vk::ImageViewType as VT; use vk::ImageViewType as VT;
@ -456,7 +470,19 @@ pub struct ImageViewDesc {
} }
impl ImageViewDesc { impl ImageViewDesc {
pub fn hash_eq_copy(&self) -> Self { pub fn color_2d() -> Self {
Self {
kind: vk::ImageViewType::TYPE_2D,
aspect: vk::ImageAspectFlags::COLOR,
..Default::default()
}
}
pub fn with_format(self, format: vk::Format) -> Self {
Self { format, ..self }
}
pub(crate) fn hash_eq_copy(&self) -> Self {
let &Self { let &Self {
flags, flags,
kind, kind,
@ -561,6 +587,15 @@ pub struct ImageView {
image: Arc<Image>, image: Arc<Image>,
} }
impl ImageView {
pub fn raw(&self) -> vk::ImageView {
*self.view
}
pub fn image(&self) -> &Arc<Image> {
&self.image
}
}
impl DeviceHandle for vk::ImageView { impl DeviceHandle for vk::ImageView {
unsafe fn destroy(&mut self, device: &Device) { unsafe fn destroy(&mut self, device: &Device) {
unsafe { unsafe {

View file

@ -1,5 +1,4 @@
use std::{ use std::{
cell::OnceCell,
cmp::Ordering, cmp::Ordering,
ffi::{CStr, CString}, ffi::{CStr, CString},
ops::Deref, ops::Deref,

View file

@ -1021,10 +1021,13 @@ impl Renderer2 {
surface: &swapchain::Surface, surface: &swapchain::Surface,
cb: F, cb: F,
) -> Result<T> { ) -> Result<T> {
let frame = surface.acquire_image().await?; let Some(frame) = surface.acquire_image() else {
return Err(Error::SuboptimalSwapchain);
};
let (frame, _suboptimal) = frame.await?;
let mut rg = render_graph::RenderGraph::new(); let mut rg = render_graph::RenderGraph::new();
let _framebuffer = rg.import_framebuffer(frame.image.clone()); let _framebuffer = rg.import_framebuffer(frame.image().clone());
let out = cb(self, &mut rg); let out = cb(self, &mut rg);

View file

@ -47,6 +47,15 @@ pub struct QueueInner {
pub(crate) lock: Mutex<()>, pub(crate) lock: Mutex<()>,
} }
impl QueueInner {
pub fn raw(&self) -> vk::Queue {
self.raw
}
pub fn family_index(&self) -> u32 {
self.family.index
}
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct QueueFamily { pub struct QueueFamily {
pub index: u32, pub index: u32,

View file

@ -62,13 +62,11 @@ impl GraphResource {
discr.hash(&mut state); discr.hash(&mut state);
match self { match self {
GraphResource::Framebuffer(swapchain_frame) => { GraphResource::Framebuffer(swapchain_frame) => (swapchain_frame.raw()).hash(&mut state),
(swapchain_frame.handle()).hash(&mut state) GraphResource::ImportedImage(image) => image.raw().hash(&mut state),
} GraphResource::ImportedBuffer(buffer) => buffer.raw().hash(&mut state),
GraphResource::ImportedImage(image) => image.handle().hash(&mut state), GraphResource::Image(image) => image.raw().hash(&mut state),
GraphResource::ImportedBuffer(buffer) => buffer.handle().hash(&mut state), GraphResource::Buffer(buffer) => buffer.raw().hash(&mut state),
GraphResource::Image(image) => image.handle().hash(&mut state),
GraphResource::Buffer(buffer) => buffer.handle().hash(&mut state),
GraphResource::ImageDesc(image_desc) => image_desc.hash(&mut state), GraphResource::ImageDesc(image_desc) => image_desc.hash(&mut state),
GraphResource::BufferDesc(buffer_desc) => buffer_desc.hash(&mut state), GraphResource::BufferDesc(buffer_desc) => buffer_desc.hash(&mut state),
GraphResource::Default => {} GraphResource::Default => {}
@ -486,7 +484,7 @@ impl RenderGraph {
_ => {} _ => {}
} }
} }
ash::prelude::VkResult::Ok(()) crate::Result::Ok(())
})?; })?;
let pool = let pool =
@ -561,19 +559,19 @@ impl RenderGraph {
) { ) {
let barrier: Barrier = match res { let barrier: Barrier = match res {
GraphResource::Framebuffer(arc) => { GraphResource::Framebuffer(arc) => {
image_barrier(arc.handle(), arc.format(), from, to, None).into() image_barrier(arc.raw(), arc.format(), from, to, None).into()
} }
GraphResource::ImportedImage(arc) => { GraphResource::ImportedImage(arc) => {
image_barrier(arc.handle(), arc.format(), from, to, None).into() image_barrier(arc.raw(), arc.format(), from, to, None).into()
} }
GraphResource::ImportedBuffer(arc) => { GraphResource::ImportedBuffer(arc) => {
buffer_barrier(arc.handle(), 0, arc.len(), from, to, None).into() buffer_barrier(arc.raw(), 0, arc.len(), from, to, None).into()
} }
GraphResource::Image(image) => { GraphResource::Image(image) => {
image_barrier(image.handle(), image.format(), from, to, None).into() image_barrier(image.raw(), image.format(), from, to, None).into()
} }
GraphResource::Buffer(buffer) => { GraphResource::Buffer(buffer) => {
buffer_barrier(buffer.handle(), 0, buffer.len(), from, to, None).into() buffer_barrier(buffer.raw(), 0, buffer.len(), from, to, None).into()
} }
_ => { _ => {
unreachable!() unreachable!()
@ -1297,7 +1295,7 @@ pub fn clear_pass(rg: &mut RenderGraph, color: Rgba, target: GraphResourceId) {
let cmd = &ctx.cmd; let cmd = &ctx.cmd;
cmd.clear_color_image( cmd.clear_color_image(
target.handle(), target.raw(),
target.format(), target.format(),
vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::TRANSFER_DST_OPTIMAL,
color, color,

View file

@ -2,6 +2,7 @@ use std::sync::Arc;
use ash::vk; use ash::vk;
use glam::{f32::Mat4, vec3}; use glam::{f32::Mat4, vec3};
use gpu_allocator::MemoryLocation;
pub use crate::egui_pass::{egui_pass, egui_pre_pass}; pub use crate::egui_pass::{egui_pass, egui_pre_pass};
@ -9,7 +10,7 @@ use crate::{
Result, Result,
buffers::{Buffer, BufferDesc}, buffers::{Buffer, BufferDesc},
commands::{self, traits::CommandBufferExt}, commands::{self, traits::CommandBufferExt},
device::{Device, DeviceOwned}, device::Device,
images::ImageViewDesc, images::ImageViewDesc,
pipeline, pipeline,
render_graph::{ render_graph::{
@ -50,16 +51,15 @@ impl Wireframe {
name: Some("wireframe-staging".into()), name: Some("wireframe-staging".into()),
size: staging_size as u64, size: staging_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_SRC, usage: vk::BufferUsageFlags::TRANSFER_SRC,
mem_usage: vk_mem::MemoryUsage::AutoPreferHost, mem_location: MemoryLocation::CpuToGpu,
alloc_flags: vk_mem::AllocationCreateFlags::MAPPED
| vk_mem::AllocationCreateFlags::HOST_ACCESS_SEQUENTIAL_WRITE
| vk_mem::AllocationCreateFlags::STRATEGY_FIRST_FIT,
..Default::default() ..Default::default()
}, },
)?; )?;
{ {
let mut map = staging.map()?; let map = staging
.map_mut()
.expect("staging buffer should be host visible");
map[..positions_size].copy_from_slice(bytemuck::cast_slice(&positions)); map[..positions_size].copy_from_slice(bytemuck::cast_slice(&positions));
map[indices_offset..][..indices_size].copy_from_slice(bytemuck::cast_slice(&indices)); map[indices_offset..][..indices_size].copy_from_slice(bytemuck::cast_slice(&indices));
@ -72,7 +72,7 @@ impl Wireframe {
name: Some("wireframe-positions".into()), name: Some("wireframe-positions".into()),
size: positions_size as u64, size: positions_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::GpuOnly,
..Default::default() ..Default::default()
}, },
)?; )?;
@ -82,7 +82,7 @@ impl Wireframe {
name: Some("wireframe-indices".into()), name: Some("wireframe-indices".into()),
size: indices_size as u64, size: indices_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::GpuOnly,
..Default::default() ..Default::default()
}, },
)?; )?;
@ -92,7 +92,7 @@ impl Wireframe {
name: Some("wireframe-colors".into()), name: Some("wireframe-colors".into()),
size: colors_size as u64, size: colors_size as u64,
usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, usage: vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER,
mem_usage: vk_mem::MemoryUsage::AutoPreferDevice, mem_location: MemoryLocation::GpuOnly,
..Default::default() ..Default::default()
}, },
)?; )?;
@ -102,8 +102,8 @@ impl Wireframe {
let cmd = pool.alloc()?; let cmd = pool.alloc()?;
cmd.copy_buffers( cmd.copy_buffers(
staging.handle(), staging.raw(),
positions.handle(), positions.raw(),
&[vk::BufferCopy { &[vk::BufferCopy {
src_offset: 0, src_offset: 0,
dst_offset: 0, dst_offset: 0,
@ -111,8 +111,8 @@ impl Wireframe {
}], }],
); );
cmd.copy_buffers( cmd.copy_buffers(
staging.handle(), staging.raw(),
indices.handle(), indices.raw(),
&[vk::BufferCopy { &[vk::BufferCopy {
src_offset: indices_offset as u64, src_offset: indices_offset as u64,
dst_offset: 0, dst_offset: 0,
@ -120,8 +120,8 @@ impl Wireframe {
}], }],
); );
cmd.copy_buffers( cmd.copy_buffers(
staging.handle(), staging.raw(),
colors.handle(), colors.raw(),
&[vk::BufferCopy { &[vk::BufferCopy {
src_offset: colors_offset as u64, src_offset: colors_offset as u64,
dst_offset: 0, dst_offset: 0,
@ -131,7 +131,7 @@ impl Wireframe {
let barriers = [ let barriers = [
buffer_barrier( buffer_barrier(
positions.handle(), positions.raw(),
0, 0,
positions.len(), positions.len(),
Access::transfer_write(), Access::transfer_write(),
@ -139,7 +139,7 @@ impl Wireframe {
None, None,
), ),
buffer_barrier( buffer_barrier(
indices.handle(), indices.raw(),
0, 0,
indices.len(), indices.len(),
Access::transfer_write(), Access::transfer_write(),
@ -147,7 +147,7 @@ impl Wireframe {
None, None,
), ),
buffer_barrier( buffer_barrier(
colors.handle(), colors.raw(),
0, 0,
colors.len(), colors.len(),
Access::transfer_write(), Access::transfer_write(),
@ -363,8 +363,8 @@ impl Wireframe {
.height(target.height() as f32)]); .height(target.height() as f32)]);
cmd.bind_pipeline(&pipeline); cmd.bind_pipeline(&pipeline);
cmd.bind_indices(indices.buffer(), 0, vk::IndexType::UINT32); cmd.bind_indices(indices.raw(), 0, vk::IndexType::UINT32);
cmd.bind_vertex_buffers(&[positions.handle(), colors.handle()], &[0, 0]); cmd.bind_vertex_buffers(&[positions.raw(), colors.raw()], &[0, 0]);
cmd.push_constants( cmd.push_constants(
&layout, &layout,
vk::ShaderStageFlags::VERTEX, vk::ShaderStageFlags::VERTEX,

View file

@ -1,6 +1,6 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
marker::PhantomData, ops::Deref,
sync::{ sync::{
Arc, Arc,
atomic::{AtomicU32, AtomicU64, Ordering}, atomic::{AtomicU32, AtomicU64, Ordering},
@ -12,16 +12,14 @@ use ash::{
prelude::VkResult, prelude::VkResult,
vk::{self, Handle}, vk::{self, Handle},
}; };
use parking_lot::{Mutex, RawMutex, RwLock}; use parking_lot::{Mutex, RwLock};
use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use crate::{ use crate::{
Instance, PhysicalDeviceInfo, Result, SurfaceCapabilities, define_device_owned_handle, Instance, PhysicalDeviceInfo, Result, SurfaceCapabilities,
device::{Device, DeviceObject, DeviceOwned}, device::{Device, DeviceObject},
images, images::{self, ImageViewDesc},
instance::InstanceInner, sync::Fence,
sync::{self, Fence},
util::RawMutexGuard,
}; };
use derive_more::Debug; use derive_more::Debug;
@ -274,6 +272,18 @@ impl Surface {
composite_alpha_mode, composite_alpha_mode,
}) })
} }
pub fn acquire_image(
&self,
) -> Option<impl std::future::Future<Output = crate::Result<(SwapchainImage, bool)>>> {
// ensure lock does not block for the entire duration of the async image acquisition.
let swapchain = self.swapchain.read().as_ref().cloned();
if let Some(swapchain) = swapchain {
Some(swapchain.acquire_image())
} else {
None
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -302,7 +312,7 @@ pub struct Swapchain {
// Some of the swapchain operations require external synchronisation; this mutex allows `Swapchain` to be `Sync`. // Some of the swapchain operations require external synchronisation; this mutex allows `Swapchain` to be `Sync`.
#[debug(skip)] #[debug(skip)]
guard: parking_lot::RawMutex, pub(crate) guard: parking_lot::Mutex<()>,
// for khr_present_id/khr_present_wait // for khr_present_id/khr_present_wait
#[allow(unused)] #[allow(unused)]
@ -365,7 +375,7 @@ impl Swapchain {
.expect("swapchain extension not loaded"); .expect("swapchain extension not loaded");
let (swapchain, images) = { let (swapchain, images) = {
let _lock = old_swapchain.as_ref().map(|old| old.lock()); let _lock = old_swapchain.as_ref().map(|old| old.guard.lock());
let old_swapchain = old_swapchain let old_swapchain = old_swapchain
.map(|swp| *swp.swapchain) .map(|swp| *swp.swapchain)
.unwrap_or(vk::SwapchainKHR::null()); .unwrap_or(vk::SwapchainKHR::null());
@ -467,7 +477,7 @@ impl Swapchain {
), ),
images, images,
config, config,
guard: <RawMutex as parking_lot::lock_api::RawMutex>::INIT, guard: Mutex::new(()),
min_image_count: surface_caps.capabilities.min_image_count, min_image_count: surface_caps.capabilities.min_image_count,
acquire_semaphores, acquire_semaphores,
release_semaphores, release_semaphores,
@ -485,11 +495,15 @@ impl Swapchain {
self.images.len() as u32 self.images.len() as u32
} }
pub fn raw(&self) -> vk::SwapchainKHR {
*self.swapchain
}
/// returns a future yielding the frame, and true if the swapchain is /// returns a future yielding the frame, and true if the swapchain is
/// suboptimal and should be recreated. /// suboptimal and should be recreated.
fn acquire_image( fn acquire_image(
self: Arc<Self>, self: Arc<Self>,
) -> impl std::future::Future<Output = crate::Result<(SwapchainFrame, bool)>> { ) -> impl std::future::Future<Output = crate::Result<(SwapchainImage, bool)>> {
let frame = self let frame = self
.current_frame .current_frame
.try_update(Ordering::Release, Ordering::Relaxed, |i| { .try_update(Ordering::Release, Ordering::Relaxed, |i| {
@ -507,28 +521,34 @@ impl Swapchain {
// spawn on threadpool because it might block. // spawn on threadpool because it might block.
let (idx, suboptimal) = smol::unblock({ let (idx, suboptimal) = smol::unblock({
let this = self.clone(); let this = self.clone();
let fence = fence.raw();
move || unsafe { move || unsafe {
this.with_locked(|swapchain| { this.with_locked(|swapchain| {
this.functor this.functor
.acquire_next_image(swapchain, u64::MAX, acquire, fence.raw()) .acquire_next_image(swapchain.raw(), u64::MAX, acquire, fence)
}) })
} }
}) })
.await?; .await?;
let idx = idx as usize;
let image = self.images[idx].clone();
let image = Arc::new(images::Image::from_swapchain_image(image, &self));
let view = image.create_view(ImageViewDesc {
name: Some(format!("swapchain-{:x}-image-view-{idx}", self.raw().as_raw()).into()),
kind: vk::ImageViewType::TYPE_2D,
format: self.config.format,
aspect: vk::ImageAspectFlags::COLOR,
..Default::default()
})?;
// wait for image to become available. // wait for image to become available.
fence.into_future().await; fence.into_future().await;
let idx = idx as usize;
let image = self.images[idx].clone();
let view = self.image_views[idx];
Ok(( Ok((
SwapchainFrame { SwapchainImage {
index: idx as u32, index: idx as u32,
swapchain: self.clone(), swapchain: self,
format: self.config.format,
image,
view, view,
acquire, acquire,
release, release,
@ -538,12 +558,20 @@ impl Swapchain {
} }
} }
fn present(&self, frame: SwapchainFrame, wait: Option<vk::Semaphore>) -> Result<()> { /// # Safety
let swpchain = self.lock(); /// The caller must ensure that the provided index corresponds to an image
let queue = self.device().present_queue().lock(); /// that is currently acquired and not yet presented.
unsafe fn present(&self, index: u32, wait: Option<vk::Semaphore>) -> Result<()> {
let _lock = self.guard.lock();
let wait_semaphores = wait.as_slice(); let wait_semaphores = wait.as_slice();
let present_info = vk::PresentInfoKHR::default()
.image_indices(core::slice::from_ref(&index))
.swapchains(core::slice::from_ref(&*self.swapchain))
.wait_semaphores(wait_semaphores);
let queue = self.swapchain.device().queues.graphics();
queue.with_locked(|queue| -> crate::Result<()> {
// TODO: make this optional for devices with no support for present_wait/present_id // TODO: make this optional for devices with no support for present_wait/present_id
// let present_id = self // let present_id = self
// .present_id // .present_id
@ -551,18 +579,15 @@ impl Swapchain {
// let mut present_id = // let mut present_id =
// vk::PresentIdKHR::default().present_ids(core::slice::from_ref(&present_id)); // vk::PresentIdKHR::default().present_ids(core::slice::from_ref(&present_id));
let present_info = vk::PresentInfoKHR::default()
.image_indices(core::slice::from_ref(&frame.index))
.swapchains(core::slice::from_ref(&swpchain))
.wait_semaphores(wait_semaphores);
//.push_next(&mut present_id) //.push_next(&mut present_id)
// call winits pre_present_notify here // call winits pre_present_notify here
unsafe { unsafe {
self.functor.queue_present(*queue, &present_info)?; self.functor.queue_present(queue.raw(), &present_info)?;
} }
Ok(()) Ok(())
})
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -615,6 +640,41 @@ impl Swapchain {
} }
static SWAPCHAIN_COUNT: AtomicU64 = AtomicU64::new(0); static SWAPCHAIN_COUNT: AtomicU64 = AtomicU64::new(0);
#[derive(Debug)]
#[must_use = "This struct represents an acquired image from the Swapchain and
must be presented in order to free resources on the device."]
pub struct SwapchainImage {
view: images::ImageView,
// The swapchain must be kept alive while the image is in use, because the
// image is owned by the swapchain and will be freed when the swapchain is
// dropped. Additionally, we need access to the swapchain in order to
// present the image.
swapchain: Arc<Swapchain>,
index: u32,
pub acquire: vk::Semaphore,
pub release: vk::Semaphore,
}
impl Deref for SwapchainImage {
type Target = images::ImageView;
fn deref(&self) -> &Self::Target {
&self.view
}
}
impl SwapchainImage {
pub fn index(&self) -> u32 {
self.index
}
pub fn present(self, wait: Option<vk::Semaphore>) -> crate::Result<()> {
// SAFETY: we know the index is valid because we've aquired the image,
// and we know it isn't presented yet because we still own the image.
unsafe { self.swapchain.present(self.index, wait) }
}
}
#[derive(Debug)] #[derive(Debug)]
#[must_use = "This struct represents an acquired image from the swapchain and #[must_use = "This struct represents an acquired image from the swapchain and
must be presented in order to free resources on the device."] must be presented in order to free resources on the device."]
@ -637,7 +697,8 @@ impl PartialEq for SwapchainFrame {
impl SwapchainFrame { impl SwapchainFrame {
pub fn present(self, wait: Option<vk::Semaphore>) -> crate::Result<()> { pub fn present(self, wait: Option<vk::Semaphore>) -> crate::Result<()> {
self.swapchain.clone().present(self, wait) // SAFETY: we know the index is valid because we've aquired the image, and we know it isn't presented yet because we still own the image.
unsafe { self.swapchain.present(self.index, wait) }
} }
} }
@ -678,19 +739,9 @@ pub struct SwapchainConfiguration {
} }
impl Swapchain { impl Swapchain {
pub fn lock(&self) -> RawMutexGuard<'_, vk::SwapchainKHR> { pub fn with_locked<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T {
use parking_lot::lock_api::RawMutex; let _lock = self.guard.lock();
self.guard.lock(); f(self)
RawMutexGuard {
mutex: &self.guard,
value: &*self.swapchain,
_pd: PhantomData,
}
}
pub fn with_locked<T, F: FnOnce(vk::SwapchainKHR) -> T>(&self, f: F) -> T {
let lock = self.lock();
f(*lock)
} }
} }

View file

@ -3,16 +3,15 @@ use std::borrow::Cow;
use std::{ use std::{
future::Future, future::Future,
marker::PhantomData, marker::PhantomData,
mem::ManuallyDrop,
sync::{Arc, atomic::AtomicU32}, sync::{Arc, atomic::AtomicU32},
time::Duration, time::Duration,
}; };
use crate::device::{DeviceObject, DeviceOwned, Pool, PoolObject, Pooled}; use crate::device::{DeviceObject, Pool, PoolObject, Pooled};
use crate::{Result, device::DeviceInner}; use crate::{Result, device::DeviceInner};
use super::Device; use super::Device;
use ash::{prelude::*, vk}; use ash::{prelude::VkResult, vk};
use crossbeam::channel::{Receiver, Sender}; use crossbeam::channel::{Receiver, Sender};
type Message = (SyncPrimitive, std::task::Waker); type Message = (SyncPrimitive, std::task::Waker);
@ -212,7 +211,7 @@ impl Fence {
} }
} }
pub fn wait_on(&self, timeout: Option<u64>) -> Result<()> { pub fn wait_on(&self, timeout: Option<u64>) -> VkResult<()> {
unsafe { unsafe {
self.device().raw.wait_for_fences( self.device().raw.wait_for_fences(
core::slice::from_ref(&self.raw()), core::slice::from_ref(&self.raw()),