From 562074c579e8be2906bc2db23fad9078a4495195 Mon Sep 17 00:00:00 2001 From: Joshua Simmons Date: Thu, 3 Nov 2022 22:52:35 +0100 Subject: [PATCH] Add descriptor APIs and move the triangle --- narcissus-gpu/src/lib.rs | 102 +++-- narcissus-gpu/src/vulkan.rs | 463 +++++++++++++++++++---- narcissus/src/main.rs | 239 ++++++------ narcissus/src/shaders/triangle.vert.glsl | 8 +- narcissus/src/shaders/triangle.vert.spv | Bin 1504 -> 1924 bytes 5 files changed, 583 insertions(+), 229 deletions(-) diff --git a/narcissus-gpu/src/lib.rs b/narcissus-gpu/src/lib.rs index d1dd1f1..b256b0f 100644 --- a/narcissus-gpu/src/lib.rs +++ b/narcissus-gpu/src/lib.rs @@ -17,9 +17,6 @@ pub struct Sampler(Handle); #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct BindGroupLayout(Handle); -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct BindGroup(Handle); - #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Pipeline(Handle); @@ -157,27 +154,6 @@ pub struct SamplerDesc { pub max_lod: f32, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum BindingType { - Sampler, - Texture, - UniformBuffer, - StorageBuffer, - DynamicUniformBuffer, - DynamicStorageBuffer, -} - -pub struct BindGroupLayoutEntryDesc { - pub slot: u32, - pub stages: ShaderStageFlags, - pub binding_type: BindingType, - pub count: u32, -} - -pub struct BindGroupLayoutDesc<'a> { - pub entries: &'a [BindGroupLayoutEntryDesc], -} - pub struct GraphicsPipelineLayout<'a> { pub color_attachment_formats: &'a [TextureFormat], pub depth_attachment_format: Option, @@ -232,6 +208,39 @@ pub struct RenderingDesc<'a> { pub stencil_attachment: Option, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum BindingType { + Sampler, + Texture, + UniformBuffer, + StorageBuffer, + DynamicUniformBuffer, + DynamicStorageBuffer, +} + +pub struct BindGroupLayoutEntryDesc { + pub slot: u32, + pub stages: ShaderStageFlags, + pub binding_type: BindingType, + pub count: u32, +} + +pub struct BindGroupLayoutDesc<'a> { + pub entries: &'a [BindGroupLayoutEntryDesc], +} + +pub struct Bind<'a> { + pub binding: u32, + pub array_element: u32, + pub typed: TypedBind<'a>, +} + +pub enum TypedBind<'a> { + Sampler(&'a [Sampler]), + Texture(&'a [Texture]), + Buffer(&'a [Buffer]), +} + thread_token_def!(ThreadToken, GpuConcurrent, 8); pub struct FrameToken<'device> { @@ -240,12 +249,10 @@ pub struct FrameToken<'device> { phantom: PhantomData<&'device dyn Device>, } -pub struct CommandBufferToken<'frame, 'thread> { - frame_token: &'frame FrameToken<'frame>, - thread_token: &'thread mut ThreadToken, +pub struct CommandBufferToken { index: usize, raw: u64, - phantom: PhantomUnsend, + phantom_unsend: PhantomUnsend, } pub trait Device { @@ -267,6 +274,9 @@ pub trait Device { ); fn destroy_pipeline(&self, frame_token: &FrameToken, pipeline: Pipeline); + unsafe fn map_buffer(&self, buffer: Buffer) -> *mut u8; + unsafe fn unmap_buffer(&self, buffer: Buffer); + fn acquire_swapchain( &self, frame_token: &FrameToken, @@ -275,25 +285,43 @@ pub trait Device { ) -> (u32, u32, Texture); fn destroy_window(&self, window: Window); - fn create_command_buffer<'frame>( - &'frame self, - frame_token: &'frame FrameToken, - thread_token: &'frame mut ThreadToken, + fn create_command_buffer( + &self, + frame_token: &FrameToken, + thread_token: &mut ThreadToken, ) -> CommandBufferToken; - fn cmd_bind_pipeline(&self, command_buffer_token: &mut CommandBufferToken, pipeline: Pipeline); + fn cmd_set_bind_group( + &self, + frame_token: &FrameToken, + thread_token: &mut ThreadToken, + command_buffer_token: &mut CommandBufferToken, + pipeline: Pipeline, + layout: BindGroupLayout, + bind_group_index: u32, + bindings: &[Bind], + ); + + fn cmd_set_pipeline(&self, command_buffer_token: &mut CommandBufferToken, pipeline: Pipeline); + fn cmd_begin_rendering( &self, + frame_token: &FrameToken, + thread_token: &mut ThreadToken, command_buffer_token: &mut CommandBufferToken, desc: &RenderingDesc, ); + fn cmd_end_rendering(&self, command_buffer_token: &mut CommandBufferToken); + fn cmd_set_viewports( &self, command_buffer_token: &mut CommandBufferToken, viewports: &[Viewport], ); + fn cmd_set_scissors(&self, command_buffer_token: &mut CommandBufferToken, scissors: &[Scissor]); + fn cmd_draw( &self, command_buffer_token: &mut CommandBufferToken, @@ -303,9 +331,15 @@ pub trait Device { first_instance: u32, ); - fn submit(&self, command_buffer_token: CommandBufferToken); + fn submit( + &self, + frame_token: &FrameToken, + thread_token: &mut ThreadToken, + command_buffer_token: CommandBufferToken, + ); fn begin_frame(&self) -> FrameToken; + fn end_frame<'device>(&'device self, frame_token: FrameToken<'device>); } diff --git a/narcissus-gpu/src/vulkan.rs b/narcissus-gpu/src/vulkan.rs index c504f52..d0d29bb 100644 --- a/narcissus-gpu/src/vulkan.rs +++ b/narcissus-gpu/src/vulkan.rs @@ -9,18 +9,17 @@ use std::{ use narcissus_app::{App, Window}; use narcissus_core::{ - cstr, default, manual_arc, manual_arc::ManualArc, Mutex, PhantomUnsend, Pool, + cstr, default, manual_arc, manual_arc::ManualArc, HybridArena, Mutex, PhantomUnsend, Pool, }; -use vk::DeviceFunctions; use vulkan_sys as vk; use crate::{ - BindGroupLayout, BindGroupLayoutDesc, BindingType, Buffer, BufferDesc, BufferUsageFlags, + Bind, BindGroupLayout, BindGroupLayoutDesc, BindingType, Buffer, BufferDesc, BufferUsageFlags, ClearValue, CommandBufferToken, ComputePipelineDesc, Device, FrameToken, GpuConcurrent, GraphicsPipelineDesc, LoadOp, MemoryLocation, Pipeline, Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc, SamplerFilter, ShaderStageFlags, Texture, TextureDesc, - TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken, + TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken, TypedBind, }; const NUM_FRAMES: usize = 2; @@ -191,12 +190,23 @@ enum VulkanTextureHolder { Swapchain(VulkanTextureSwapchain), } +impl VulkanTextureHolder { + fn image_view(&self) -> vk::ImageView { + match self { + VulkanTextureHolder::Unique(x) => x.view, + VulkanTextureHolder::Shared(x) => x.view, + VulkanTextureHolder::Swapchain(x) => x.view, + } + } +} + struct VulkanSampler(vk::Sampler); struct VulkanBindGroupLayout(vk::DescriptorSetLayout); struct VulkanPipeline { pipeline: vk::Pipeline, + pipeline_layout: vk::PipelineLayout, pipeline_bind_point: vk::PipelineBindPoint, } @@ -241,9 +251,10 @@ struct VulkanMemoryDesc { struct VulkanMemory { memory: vk::DeviceMemory, offset: u64, - _size: u64, + size: u64, } +#[derive(Default)] struct VulkanPools { textures: Pool, buffers: Pool, @@ -325,6 +336,7 @@ struct VulkanFrame { universal_queue_fence: AtomicU64, command_buffer_pools: GpuConcurrent, + descriptor_pool_pools: GpuConcurrent, present_swapchains: Mutex>, @@ -335,9 +347,32 @@ struct VulkanFrame { destroyed_image_views: Mutex>, destroyed_samplers: Mutex>, destroyed_descriptor_set_layouts: Mutex>, + destroyed_pipeline_layouts: Mutex>, destroyed_pipelines: Mutex>, recycled_semaphores: Mutex>, + recycled_descriptor_pools: Mutex>, +} + +impl VulkanFrame { + fn command_buffer_mut<'token>( + &self, + thread_token: &'token mut ThreadToken, + command_buffer_token: &CommandBufferToken, + ) -> &'token mut VulkanCommandBuffer { + let command_buffer_pool = self.command_buffer_pools.get_mut(thread_token); + &mut command_buffer_pool.command_buffers[command_buffer_token.index] + } + + fn recycle_semaphore(&self, semaphore: vk::Semaphore) { + self.recycled_semaphores.lock().push_back(semaphore); + } + + fn recycle_descriptor_pool(&self, descriptor_pool: vk::DescriptorPool) { + self.recycled_descriptor_pools + .lock() + .push_back(descriptor_pool) + } } type SwapchainDestroyQueue = DelayQueue<( @@ -368,6 +403,7 @@ pub(crate) struct VulkanDevice<'app> { pools: Mutex, semaphores: Mutex>, + descriptor_pools: Mutex>, _global_fn: vk::GlobalFunctions, instance_fn: vk::InstanceFunctions, @@ -593,7 +629,7 @@ impl<'app> VulkanDevice<'app> { }; let frames = Box::new(std::array::from_fn(|_| { - let cmd_buffer_pools = GpuConcurrent::new(|| { + let command_buffer_pools = GpuConcurrent::new(|| { let pool = { let create_info = vk::CommandPoolCreateInfo { flags: vk::CommandPoolCreateFlags::TRANSIENT, @@ -610,8 +646,12 @@ impl<'app> VulkanDevice<'app> { next_free_index: 0, } }); + + let descriptor_pool_pools = GpuConcurrent::new(|| vk::DescriptorPool::null()); + UnsafeCell::new(VulkanFrame { - command_buffer_pools: cmd_buffer_pools, + command_buffer_pools, + descriptor_pool_pools, universal_queue_fence: AtomicU64::new(universal_queue_fence), present_swapchains: Default::default(), destroyed_allocations: Default::default(), @@ -621,8 +661,10 @@ impl<'app> VulkanDevice<'app> { destroyed_image_views: Default::default(), destroyed_samplers: Default::default(), destroyed_descriptor_set_layouts: Default::default(), + destroyed_pipeline_layouts: Default::default(), destroyed_pipelines: Default::default(), recycled_semaphores: Default::default(), + recycled_descriptor_pools: Default::default(), }) })); @@ -645,15 +687,10 @@ impl<'app> VulkanDevice<'app> { swapchains: Mutex::new(HashMap::new()), destroyed_swapchains: Mutex::new(DelayQueue::new(8)), - pools: Mutex::new(VulkanPools { - textures: Pool::new(), - buffers: Pool::new(), - samplers: Pool::new(), - bind_group_layouts: Pool::new(), - pipelines: Pool::new(), - }), + pools: Default::default(), - semaphores: Mutex::new(VecDeque::new()), + semaphores: Default::default(), + descriptor_pools: Default::default(), _global_fn: global_fn, instance_fn, @@ -671,10 +708,7 @@ impl<'app> VulkanDevice<'app> { unsafe { &*self.frames[frame_token.frame_index % NUM_FRAMES].get() } } - fn frame_mut<'token>( - &'token self, - frame_token: &'token mut FrameToken, - ) -> &'token mut VulkanFrame { + fn frame_mut<'token>(&self, frame_token: &'token mut FrameToken) -> &'token mut VulkanFrame { frame_token.check_device(self); frame_token.check_frame_counter(self.frame_counter.load()); // SAFETY: mutable reference is bound to the frame token exposed by the API. only one frame token can be valid at a time. @@ -682,17 +716,6 @@ impl<'app> VulkanDevice<'app> { unsafe { &mut *self.frames[frame_token.frame_index % NUM_FRAMES].get() } } - fn command_buffer_mut<'token>( - &'token self, - command_buffer_token: &'token mut CommandBufferToken, - ) -> &'token mut VulkanCommandBuffer { - let frame = self.frame(command_buffer_token.frame_token); - let command_buffer_pool = frame - .command_buffer_pools - .get_mut(command_buffer_token.thread_token); - &mut command_buffer_pool.command_buffers[command_buffer_token.index] - } - fn find_memory_type_index(&self, filter: u32, flags: vk::MemoryPropertyFlags) -> u32 { (0..self.physical_device_memory_properties.memory_type_count) .map(|memory_type_index| { @@ -730,7 +753,7 @@ impl<'app> VulkanDevice<'app> { VulkanMemory { memory, offset: 0, - _size: desc.requirements.size, + size: desc.requirements.size, } } @@ -774,6 +797,54 @@ impl<'app> VulkanDevice<'app> { }) } + fn request_descriptor_pool(&self) -> vk::DescriptorPool { + if let Some(descriptor_pool) = self.descriptor_pools.lock().pop_front() { + descriptor_pool + } else { + let descriptor_count = 500; + let pool_sizes = &[ + vk::DescriptorPoolSize { + descriptor_type: vk::DescriptorType::Sampler, + descriptor_count, + }, + vk::DescriptorPoolSize { + descriptor_type: vk::DescriptorType::UniformBuffer, + descriptor_count, + }, + vk::DescriptorPoolSize { + descriptor_type: vk::DescriptorType::UniformBufferDynamic, + descriptor_count, + }, + vk::DescriptorPoolSize { + descriptor_type: vk::DescriptorType::StorageBuffer, + descriptor_count, + }, + vk::DescriptorPoolSize { + descriptor_type: vk::DescriptorType::StorageBufferDynamic, + descriptor_count, + }, + vk::DescriptorPoolSize { + descriptor_type: vk::DescriptorType::SampledImage, + descriptor_count: 500, + }, + ]; + + let mut descriptor_pool = vk::DescriptorPool::null(); + let create_info = vk::DescriptorPoolCreateInfo { + max_sets: 500, + pool_sizes: pool_sizes.into(), + ..default() + }; + vk_check!(self.device_fn.create_descriptor_pool( + self.device, + &create_info, + None, + &mut descriptor_pool + )); + descriptor_pool + } + } + fn request_semaphore(&self) -> vk::Semaphore { if let Some(semaphore) = self.semaphores.lock().pop_front() { semaphore @@ -790,17 +861,20 @@ impl<'app> VulkanDevice<'app> { } } - fn recycle_semaphore(&self, frame: &VulkanFrame, semaphore: vk::Semaphore) { - frame.recycled_semaphores.lock().push_back(semaphore) - } - fn request_transient_semaphore(&self, frame: &VulkanFrame) -> vk::Semaphore { let semaphore = self.request_semaphore(); - self.recycle_semaphore(frame, semaphore); + frame.recycle_semaphore(semaphore); semaphore } - fn destroy_deferred(device_fn: &DeviceFunctions, device: vk::Device, frame: &mut VulkanFrame) { + fn destroy_deferred( + device_fn: &vk::DeviceFunctions, + device: vk::Device, + frame: &mut VulkanFrame, + ) { + for pipeline_layout in frame.destroyed_pipeline_layouts.get_mut().drain(..) { + unsafe { device_fn.destroy_pipeline_layout(device, pipeline_layout, None) } + } for pipeline in frame.destroyed_pipelines.get_mut().drain(..) { unsafe { device_fn.destroy_pipeline(device, pipeline, None) } } @@ -1211,9 +1285,20 @@ impl<'driver> Device for VulkanDevice<'driver> { } fn create_graphics_pipeline(&self, desc: &GraphicsPipelineDesc) -> Pipeline { + let arena = HybridArena::<1024>::new(); + let set_layouts_iter = desc.bind_group_layouts.iter().map(|bind_group_layout| { + self.pools + .lock() + .bind_group_layouts + .get(bind_group_layout.0) + .unwrap() + .0 + }); + let set_layouts = arena.alloc_slice_fill_iter(set_layouts_iter); + let layout = { let create_info = vk::PipelineLayoutCreateInfo { - //set_layouts: set_layouts.as_slice().into(), + set_layouts: set_layouts.into(), ..default() }; let mut pipeline_layout = vk::PipelineLayout::null(); @@ -1347,13 +1432,10 @@ impl<'driver> Device for VulkanDevice<'driver> { self.device_fn .destroy_shader_module(self.device, fragment_module, None) }; - unsafe { - self.device_fn - .destroy_pipeline_layout(self.device, layout, None) - }; let handle = self.pools.lock().pipelines.insert(VulkanPipeline { pipeline: pipelines[0], + pipeline_layout: layout, pipeline_bind_point: vk::PipelineBindPoint::Graphics, }); @@ -1434,10 +1516,15 @@ impl<'driver> Device for VulkanDevice<'driver> { fn destroy_pipeline(&self, frame_token: &FrameToken, pipeline: Pipeline) { if let Some(pipeline) = self.pools.lock().pipelines.remove(pipeline.0) { - self.frame(frame_token) + let frame = self.frame(frame_token); + frame + .destroyed_pipeline_layouts + .lock() + .push_back(pipeline.pipeline_layout); + frame .destroyed_pipelines .lock() - .push_back(pipeline.pipeline) + .push_back(pipeline.pipeline); } } @@ -1782,10 +1869,10 @@ impl<'driver> Device for VulkanDevice<'driver> { } } - fn create_command_buffer<'frame>( - &'frame self, - frame_token: &'frame FrameToken, - thread_token: &'frame mut ThreadToken, + fn create_command_buffer( + &self, + frame_token: &FrameToken, + thread_token: &mut ThreadToken, ) -> CommandBufferToken { let command_buffer_pool = self .frame(frame_token) @@ -1830,18 +1917,151 @@ impl<'driver> Device for VulkanDevice<'driver> { )); CommandBufferToken { - frame_token, - thread_token, index, raw: command_buffer.as_raw(), - phantom: PhantomUnsend {}, + phantom_unsend: PhantomUnsend {}, } } - fn cmd_bind_pipeline(&self, command_buffer_token: &mut CommandBufferToken, pipeline: Pipeline) { + fn cmd_set_bind_group( + &self, + frame_token: &FrameToken, + thread_token: &mut ThreadToken, + command_buffer_token: &mut CommandBufferToken, + pipeline: Pipeline, + layout: BindGroupLayout, + bind_group_index: u32, + bindings: &[Bind], + ) { + let arena = HybridArena::<4096>::new(); + + let frame = self.frame(frame_token); + let pools = self.pools.lock(); + + let descriptor_set_layout = pools.bind_group_layouts.get(layout.0).unwrap().0; + + let mut descriptor_pool = *frame.descriptor_pool_pools.get(thread_token); + let mut allocated_pool = false; + let descriptor_set = loop { + if descriptor_pool.is_null() { + // Need to fetch a new descriptor pool + descriptor_pool = self.request_descriptor_pool(); + frame.recycle_descriptor_pool(descriptor_pool); + *frame.descriptor_pool_pools.get_mut(thread_token) = descriptor_pool; + allocated_pool = true; + } + let allocate_info = vk::DescriptorSetAllocateInfo { + descriptor_pool, + set_layouts: std::slice::from_ref(&descriptor_set_layout).into(), + ..default() + }; + let mut descriptor_set = vk::DescriptorSet::null(); + match unsafe { + self.device_fn.allocate_descriptor_sets( + self.device, + &allocate_info, + &mut descriptor_set, + ) + } { + vk::Result::Success => break descriptor_set, + _ => { + // If we fail to allocate after just creating a new descriptor set, then we'll + // never be able to allocate one. :'( + if allocated_pool { + panic!("failed to allocate descriptor set") + } + } + } + }; + + let write_descriptors_iter = bindings.iter().map(|bind| match bind.typed { + TypedBind::Sampler(samplers) => { + let sampler_infos_iter = samplers.iter().map(|sampler| { + let sampler = pools.samplers.get(sampler.0).unwrap(); + vk::DescriptorImageInfo { + image_layout: vk::ImageLayout::Undefined, + image_view: vk::ImageView::null(), + sampler: sampler.0, + } + }); + let image_infos = arena.alloc_slice_fill_iter(sampler_infos_iter); + vk::WriteDescriptorSet { + dst_set: descriptor_set, + dst_binding: bind.binding, + dst_array_element: bind.array_element, + descriptor_count: image_infos.len() as u32, + descriptor_type: vk::DescriptorType::Sampler, + image_info: image_infos.as_ptr(), + ..default() + } + } + TypedBind::Texture(textures) => { + let image_infos_iter = textures.iter().map(|texture| { + let texture = pools.textures.get(texture.0).unwrap(); + vk::DescriptorImageInfo { + image_layout: vk::ImageLayout::ColorAttachmentOptimal, + image_view: texture.image_view(), + sampler: vk::Sampler::null(), + } + }); + let image_infos = arena.alloc_slice_fill_iter(image_infos_iter); + vk::WriteDescriptorSet { + dst_set: descriptor_set, + dst_binding: bind.binding, + dst_array_element: bind.array_element, + descriptor_count: image_infos.len() as u32, + descriptor_type: vk::DescriptorType::SampledImage, + image_info: image_infos.as_ptr(), + ..default() + } + } + TypedBind::Buffer(buffers) => { + let buffer_infos_iter = buffers.iter().map(|buffer| { + let buffer = pools.buffers.get(buffer.0).unwrap(); + vk::DescriptorBufferInfo { + buffer: buffer.buffer, + offset: 0, + range: !0, + } + }); + let buffer_infos = arena.alloc_slice_fill_iter(buffer_infos_iter); + vk::WriteDescriptorSet { + dst_set: descriptor_set, + dst_binding: bind.binding, + dst_array_element: bind.array_element, + descriptor_count: buffer_infos.len() as u32, + descriptor_type: vk::DescriptorType::UniformBuffer, + buffer_info: buffer_infos.as_ptr(), + ..default() + } + } + }); + let write_descriptors = arena.alloc_slice_fill_iter(write_descriptors_iter); + + unsafe { + self.device_fn + .update_descriptor_sets(self.device, write_descriptors, &[]) + }; + + let pipeline = pools.pipelines.get(pipeline.0).unwrap(); + let command_buffer = vk::CommandBuffer::from_raw(command_buffer_token.raw); + unsafe { + self.device_fn.cmd_bind_descriptor_sets( + command_buffer, + pipeline.pipeline_bind_point, + pipeline.pipeline_layout, + bind_group_index, + &[descriptor_set], + &[], + ) + } + } + + fn cmd_set_pipeline(&self, command_buffer_token: &mut CommandBufferToken, pipeline: Pipeline) { let command_buffer = vk::CommandBuffer::from_raw(command_buffer_token.raw); let VulkanPipeline { pipeline, + pipeline_layout: _, pipeline_bind_point, } = *self.pools.lock().pipelines.get(pipeline.0).unwrap(); unsafe { @@ -1852,10 +2072,13 @@ impl<'driver> Device for VulkanDevice<'driver> { fn cmd_begin_rendering( &self, + frame_token: &FrameToken, + thread_token: &mut ThreadToken, command_buffer_token: &mut CommandBufferToken, desc: &crate::RenderingDesc, ) { - let command_buffer = self.command_buffer_mut(command_buffer_token); + let frame = self.frame(frame_token); + let command_buffer = frame.command_buffer_mut(thread_token, command_buffer_token); let pools = self.pools.lock(); let color_attachments = desc @@ -2019,13 +2242,18 @@ impl<'driver> Device for VulkanDevice<'driver> { } } - fn submit(&self, mut command_buffer_token: CommandBufferToken) { + fn submit( + &self, + frame_token: &FrameToken, + thread_token: &mut ThreadToken, + mut command_buffer_token: CommandBufferToken, + ) { let fence = self.universal_queue_fence.fetch_add(1, Ordering::SeqCst) + 1; - let frame = self.frame(command_buffer_token.frame_token); + let frame = self.frame(frame_token); frame.universal_queue_fence.store(fence, Ordering::Relaxed); - let command_buffer = self.command_buffer_mut(&mut command_buffer_token); + let command_buffer = frame.command_buffer_mut(thread_token, &mut command_buffer_token); for &(image, _) in command_buffer.swapchains_touched.values() { // transition swapchain image from attachment optimal to present src @@ -2132,6 +2360,10 @@ impl<'driver> Device for VulkanDevice<'driver> { vk_check!(device_fn.wait_semaphores(device, &wait_info, !0)); } + for pool in frame.descriptor_pool_pools.slots_mut() { + *pool = vk::DescriptorPool::null() + } + for pool in frame.command_buffer_pools.slots_mut() { if pool.next_free_index == 0 { continue; @@ -2150,6 +2382,18 @@ impl<'driver> Device for VulkanDevice<'driver> { .lock() .extend(frame.recycled_semaphores.get_mut().drain(..)); + for descriptor_pool in frame.recycled_descriptor_pools.get_mut() { + vk_check!(device_fn.reset_descriptor_pool( + device, + *descriptor_pool, + vk::DescriptorPoolResetFlags::default() + )) + } + + self.descriptor_pools + .lock() + .extend(frame.recycled_descriptor_pools.get_mut().drain(..)); + Self::destroy_deferred(device_fn, device, frame); self.destroyed_swapchains @@ -2218,6 +2462,28 @@ impl<'driver> Device for VulkanDevice<'driver> { self.frame_counter.release(frame_token); } + + unsafe fn map_buffer(&self, buffer: Buffer) -> *mut u8 { + let mut ptr = std::ptr::null_mut(); + if let Some(buffer) = self.pools.lock().buffers.get(buffer.0) { + vk_check!(self.device_fn.map_memory( + self.device, + buffer.memory.memory, + buffer.memory.offset, + buffer.memory.size, + vk::MemoryMapFlags::default(), + &mut ptr + )) + } + std::mem::transmute::<*mut c_void, *mut u8>(ptr) + } + + unsafe fn unmap_buffer(&self, buffer: Buffer) { + if let Some(buffer) = self.pools.lock().buffers.get(buffer.0) { + self.device_fn + .unmap_memory(self.device, buffer.memory.memory) + } + } } impl<'app> Drop for VulkanDevice<'app> { @@ -2235,6 +2501,10 @@ impl<'app> Drop for VulkanDevice<'app> { unsafe { device_fn.destroy_semaphore(device, *semaphore, None) } } + for descriptor_pool in frame.recycled_descriptor_pools.get_mut() { + unsafe { device_fn.destroy_descriptor_pool(device, *descriptor_pool, None) } + } + Self::destroy_deferred(device_fn, device, frame); for pool in frame.command_buffer_pools.slots_mut() { @@ -2257,29 +2527,62 @@ impl<'app> Drop for VulkanDevice<'app> { } } - let mut image_views = Vec::new(); - let mut images = Vec::new(); - for texture in self.pools.get_mut().textures.values() { - match texture { - VulkanTextureHolder::Unique(texture) => { - image_views.push(texture.view); - images.push(texture.texture.image) - } - VulkanTextureHolder::Shared(texture) => { - image_views.push(texture.view); - } - VulkanTextureHolder::Swapchain(texture) => { - image_views.push(texture.view); + let VulkanPools { + textures, + buffers, + samplers, + bind_group_layouts, + pipelines, + } = self.pools.get_mut(); + + for buffer in buffers.values() { + unsafe { device_fn.destroy_buffer(device, buffer.buffer, None) } + unsafe { device_fn.free_memory(device, buffer.memory.memory, None) } + } + + { + let mut image_views = Vec::new(); + let mut images = Vec::new(); + for texture in textures.values() { + match texture { + VulkanTextureHolder::Unique(texture) => { + image_views.push(texture.view); + images.push(texture.texture.image) + } + VulkanTextureHolder::Shared(texture) => { + image_views.push(texture.view); + } + VulkanTextureHolder::Swapchain(texture) => { + image_views.push(texture.view); + } } } + + for image_view in image_views { + unsafe { device_fn.destroy_image_view(device, image_view, None) } + } + + for image in images { + unsafe { device_fn.destroy_image(device, image, None) } + } } - for image_view in image_views { - unsafe { device_fn.destroy_image_view(device, image_view, None) } + for sampler in samplers.values() { + unsafe { device_fn.destroy_sampler(device, sampler.0, None) } } - for image in images { - unsafe { device_fn.destroy_image(device, image, None) } + for pipeline in pipelines.values() { + unsafe { + self.device_fn + .destroy_pipeline_layout(self.device, pipeline.pipeline_layout, None) + }; + unsafe { device_fn.destroy_pipeline(device, pipeline.pipeline, None) } + } + + for descriptor_set_layout in bind_group_layouts.values() { + unsafe { + device_fn.destroy_descriptor_set_layout(device, descriptor_set_layout.0, None) + } } for semaphore in self @@ -2291,6 +2594,10 @@ impl<'app> Drop for VulkanDevice<'app> { unsafe { device_fn.destroy_semaphore(device, *semaphore, None) } } + for descriptor_pool in self.descriptor_pools.get_mut() { + unsafe { device_fn.destroy_descriptor_pool(device, *descriptor_pool, None) } + } + { let destroyed_swapchains = self .destroyed_swapchains @@ -2319,16 +2626,6 @@ impl<'app> Drop for VulkanDevice<'app> { } } - for pipeline in self.pools.get_mut().pipelines.values() { - unsafe { device_fn.destroy_pipeline(device, pipeline.pipeline, None) } - } - - for descriptor_set_layout in self.pools.get_mut().bind_group_layouts.values() { - unsafe { - device_fn.destroy_descriptor_set_layout(device, descriptor_set_layout.0, None) - } - } - unsafe { device_fn.destroy_device(device, None) } unsafe { self.instance_fn.destroy_instance(self.instance, None) }; } diff --git a/narcissus/src/main.rs b/narcissus/src/main.rs index 1a8175c..518ae55 100644 --- a/narcissus/src/main.rs +++ b/narcissus/src/main.rs @@ -1,11 +1,13 @@ -use narcissus_app::{create_app, Event, Window, WindowDesc}; +use std::time::Instant; + +use narcissus_app::{create_app, Event, WindowDesc}; use narcissus_core::{cstr, obj, slice, Image}; use narcissus_gpu::{ - create_vulkan_device, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, ClearValue, - Device, FrameToken, GraphicsPipelineDesc, GraphicsPipelineLayout, LoadOp, MemoryLocation, - Pipeline, RenderingAttachment, RenderingDesc, Scissor, ShaderDesc, ShaderStageFlags, StoreOp, - TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken, - Viewport, + create_vulkan_device, Bind, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, Buffer, + BufferDesc, BufferUsageFlags, ClearValue, Device, GraphicsPipelineDesc, GraphicsPipelineLayout, + LoadOp, MemoryLocation, RenderingAttachment, RenderingDesc, Scissor, ShaderDesc, + ShaderStageFlags, StoreOp, TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, + TextureViewDesc, ThreadToken, TypedBind, Viewport, }; use narcissus_maths::{Vec2, Vec3}; @@ -84,47 +86,13 @@ pub fn main() { let vert_shader_spv = Spirv(*include_bytes!("shaders/triangle.vert.spv")); let frag_shader_spv = Spirv(*include_bytes!("shaders/triangle.frag.spv")); - let global_layout = device.create_bind_group_layout(&BindGroupLayoutDesc { - entries: &[ - // Global uniforms. - BindGroupLayoutEntryDesc { - slot: 0, - stages: ShaderStageFlags::ALL, - binding_type: BindingType::UniformBuffer, - count: 1, - }, - ], - }); - - let per_material_layout = device.create_bind_group_layout(&BindGroupLayoutDesc { - entries: &[ - // Per-material uniforms. - BindGroupLayoutEntryDesc { - slot: 0, - stages: ShaderStageFlags::ALL, - binding_type: BindingType::UniformBuffer, - count: 1, - }, - // Per-material textures. - BindGroupLayoutEntryDesc { - slot: 1, - stages: ShaderStageFlags::ALL, - binding_type: BindingType::Texture, - count: 1, - }, - ], - }); - - let per_draw_layout = device.create_bind_group_layout(&BindGroupLayoutDesc { - entries: &[ - // Per-draw Uniforms - BindGroupLayoutEntryDesc { - slot: 0, - stages: ShaderStageFlags::ALL, - binding_type: BindingType::DynamicUniformBuffer, - count: 1, - }, - ], + let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDesc { + entries: &[BindGroupLayoutEntryDesc { + slot: 0, + stages: ShaderStageFlags::ALL, + binding_type: BindingType::UniformBuffer, + count: 1, + }], }); let pipeline = device.create_graphics_pipeline(&GraphicsPipelineDesc { @@ -136,14 +104,7 @@ pub fn main() { entrypoint_name: cstr!("main"), code: &frag_shader_spv.0, }, - bind_group_layouts: &[ - // Set 0 - global_layout, - // Set 1 - per_material_layout, - // Set 2 - per_draw_layout, - ], + bind_group_layouts: &[bind_group_layout], layout: GraphicsPipelineLayout { color_attachment_formats: &[TextureFormat::BGRA8_SRGB], depth_attachment_format: None, @@ -184,9 +145,59 @@ pub fn main() { device.destroy_texture(&frame_token, texture2); device.end_frame(frame_token); + struct UniformBufferMap<'a> { + device: &'a dyn Device, + buffer: Buffer, + slice: &'a mut [u8], + } + + impl<'a> UniformBufferMap<'a> { + pub fn new(device: &'a dyn Device, len: usize) -> Self { + let buffer = device.create_buffer(&BufferDesc { + memory_location: MemoryLocation::PreferDevice, + usage: BufferUsageFlags::UNIFORM, + size: len, + }); + unsafe { + let ptr = device.map_buffer(buffer); + let slice = std::slice::from_raw_parts_mut(ptr, len); + Self { + device, + buffer, + slice, + } + } + } + + pub fn buffer(&self) -> Buffer { + self.buffer + } + + pub fn write_f32(&mut self, value: f32) { + self.slice.copy_from_slice(&value.to_le_bytes()); + } + } + + impl<'a> Drop for UniformBufferMap<'a> { + fn drop(&mut self) { + // Safety: Make sure we don't have the slice outlive the mapping. + unsafe { + self.device.unmap_buffer(self.buffer); + } + } + } + + let mut uniforms = UniformBufferMap::new(device.as_ref(), 4); + + let start_time = Instant::now(); 'main: loop { let frame_token = device.begin_frame(); + let frame_start = Instant::now() - start_time; + let frame_start = frame_start.as_secs_f32(); + + uniforms.write_f32(frame_start); + while let Some(event) = app.poll_event() { use Event::*; match event { @@ -202,67 +213,73 @@ pub fn main() { } } - render_window( - device.as_ref(), + let (width, height, swapchain_image) = + device.acquire_swapchain(&frame_token, window, TextureFormat::BGRA8_SRGB); + + let mut command_buffer_token = + device.create_command_buffer(&frame_token, &mut thread_token); + + device.cmd_begin_rendering( &frame_token, &mut thread_token, - pipeline, - window, + &mut command_buffer_token, + &RenderingDesc { + x: 0, + y: 0, + width, + height, + color_attachments: &[RenderingAttachment { + texture: swapchain_image, + load_op: LoadOp::Clear(ClearValue::ColorF32([ + 0.392157, 0.584314, 0.929412, 1.0, + ])), + store_op: StoreOp::Store, + }], + depth_attachment: None, + stencil_attachment: None, + }, ); - device.end_frame(frame_token); - } -} + device.cmd_set_pipeline(&mut command_buffer_token, pipeline); + device.cmd_set_bind_group( + &frame_token, + &mut thread_token, + &mut command_buffer_token, + pipeline, + bind_group_layout, + 0, + &[Bind { + binding: 0, + array_element: 0, + typed: TypedBind::Buffer(&[uniforms.buffer()]), + }], + ); -fn render_window( - device: &dyn Device, - frame_token: &FrameToken, - thread_token: &mut ThreadToken, - pipeline: Pipeline, - window: Window, -) { - let (width, height, swapchain_image) = - device.acquire_swapchain(frame_token, window, TextureFormat::BGRA8_SRGB); - let mut command_buffer_token = device.create_command_buffer(frame_token, thread_token); - device.cmd_begin_rendering( - &mut command_buffer_token, - &RenderingDesc { - x: 0, - y: 0, - width, - height, - color_attachments: &[RenderingAttachment { - texture: swapchain_image, - load_op: LoadOp::Clear(ClearValue::ColorF32([0.392157, 0.584314, 0.929412, 1.0])), - store_op: StoreOp::Store, + device.cmd_set_scissors( + &mut command_buffer_token, + &[Scissor { + x: 0, + y: 0, + width, + height, }], - depth_attachment: None, - stencil_attachment: None, - }, - ); - device.cmd_bind_pipeline(&mut command_buffer_token, pipeline); - device.cmd_set_scissors( - &mut command_buffer_token, - &[Scissor { - x: 0, - y: 0, - width, - height, - }], - ); - device.cmd_set_viewports( - &mut command_buffer_token, - &[Viewport { - x: 0.0, - y: 0.0, - width: width as f32, - height: height as f32, - min_depth: 0.0, - max_depth: 1.0, - }], - ); - device.cmd_draw(&mut command_buffer_token, 3, 1, 0, 0); - device.cmd_end_rendering(&mut command_buffer_token); + ); + device.cmd_set_viewports( + &mut command_buffer_token, + &[Viewport { + x: 0.0, + y: 0.0, + width: width as f32, + height: height as f32, + min_depth: 0.0, + max_depth: 1.0, + }], + ); + device.cmd_draw(&mut command_buffer_token, 3, 1, 0, 0); + device.cmd_end_rendering(&mut command_buffer_token); - device.submit(command_buffer_token); + device.submit(&frame_token, &mut thread_token, command_buffer_token); + + device.end_frame(frame_token); + } } diff --git a/narcissus/src/shaders/triangle.vert.glsl b/narcissus/src/shaders/triangle.vert.glsl index 66d6766..062e2a3 100644 --- a/narcissus/src/shaders/triangle.vert.glsl +++ b/narcissus/src/shaders/triangle.vert.glsl @@ -1,5 +1,9 @@ #version 450 +layout(set = 0, binding = 0) uniform uniformBuffer { + float someValue; +}; + layout(location = 0) out vec3 fragColor; vec2 positions[3] = vec2[]( @@ -15,6 +19,8 @@ vec3 colors[3] = vec3[]( ); void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + vec2 pos = positions[gl_VertexIndex]; + pos.y += sin(someValue); + gl_Position = vec4(pos, 0.0, 1.0); fragColor = colors[gl_VertexIndex]; } \ No newline at end of file diff --git a/narcissus/src/shaders/triangle.vert.spv b/narcissus/src/shaders/triangle.vert.spv index ef8a465564ab16188c4eeac839c971575a24f795..2ed69c3ebc82e21d010990c539ed3583da7ffe5b 100644 GIT binary patch literal 1924 zcmY+FSx*yT6o%h+IxVt_2)H1ExP!O>qKL?rYC-~rgxiL+Q!>fWk}d|l(qCrcukyyk zJZH{8{gSqG-t(UQeA6I5SCVq-*N}FVo>AqMlQf{B{g(F*_ZHio+Tzmk3mC%+j0k5$ zefC_Cbb1^&5N0K+9jgjcU@Z^%6oA{)-nX)S#rZW;^c0%|=@?&ZMMjGikQk$bqI)jzSeJ$CIRT z6tz0h`R7J0I)_u#LrL96TyM5cx4QLu)RI(moYQWeMn}g)Z(0Pg~s$@7lG(2u}4&(2*z0lFOlcchp#AiEkyK~&AMi)9JfUcV)7dmRB?;LtU z|Ld*elWlq+sjPz4Js|0+q)!U1Ue26-!wC!{Hhk*4ccDvV=F7o8s|}yMSh_!K)+G<0 z@W?&0@o*J)vKJ<+m#wEkD8YWy1P7TQ1gWei$SUx{Ch=aeIl3~=wC&O>K48Iq@ z?J^iT_{5uXU)aH+r+2)^%!ZlV^_j32=$ReQ;N*+Uer0<0lH9SM_T5Cr_L0w;JBuwc z`^x^%dwf3edBJDr(Ph2h&9&GQ>$%54Pp_Ap%Wl%^WtXv;_IsUi(D5P88{aW@nY@s> zm&^?Px<6x!yx}slfRCKMJ5&9Z$3?%Q&}twj4nF@Vuh4DjH+P19eNbTg%O3H}9x$VI z?+y2wUT-S2KHry&4-N+(45QR^&*`%hnwbr&?YHh7|W4*OS5G_(A3V~J%eqBPchJ|$#lC7wp{AYr{ z$}fUEv$Jh{A<52}Gw+j(3X;yXnZFi14+mpP%y|eoS#=HU} z!dcLaJ=Y|C=_VP*O-XkoJ(UzFP*u|Jid0im?M~D_JZyK~MZM7|n&jWpI7*W&j{3x& z9_R5lwALNx;PWd~SHt+!Xn2z4$H|Ylh$(#3HAtg_G#R~3CV4mO#iwy}2;WJXp87%s z)*B9b=+PjJ3NatDK8mP?zI`5d2M1`E^j@K9;mq3i8CJ}R37%o-f`^7OmS-^L)K*~k z!E38#I`BPkoBZpZXGvA#ZQ3Md&npU4m+X#J)zq*)7QSVhUg6_Bbo-p^S+gyW#ei+7 zX1TeqDe0x#%oB`we&<|IY0nq-ua-i)Yr#3-CFerH*)KVZl40N_cG32^z+0-Dh9%o; z86Q72UUEJ3ks6jI!^#>~+z(C-*zDhQ4VO*N&wcW6-xbL)YU5MlSKWp`jbC#c3?F>* zt@&NV!9nkJ&&|Bc88B1!1pPPM54P^_KpBIN53||yx$*8$7dG!VePGsGK5OnJ5B4pe z74f${Cphug>^*$^_gn`%guU%|qXnBk9CFbw;<1bWmBRnrZ8v6f@hccg~Z@yNOO%Gr^J nS|58q=^47P;g`CeP3fZ69p5W(IMluCIPvrb`>!f`p^E+iuHtNl -- 2.49.0