diff --git a/crates/renderer/src/error.rs b/crates/renderer/src/error.rs index 42fcb4d..6fd613f 100644 --- a/crates/renderer/src/error.rs +++ b/crates/renderer/src/error.rs @@ -23,4 +23,10 @@ pub enum RendererError { /// An invalid string was encountered. #[error("Invalid string")] InvalidString, + /// The renderer's synchronization primitives were unavailable. + #[error("Synchronization primitives missing")] + SyncPrimitivesMissing, + /// The renderer's GPU memory allocator was unavailable. + #[error("GPU allocator missing")] + AllocatorMissing, } diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index 5d03e2a..036305b 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -143,18 +143,18 @@ impl Renderer { swapchain_image_views: image_views, command_pool, command_buffers, - allocator, + allocator: Some(allocator), index_buffer, - index_allocation, + index_allocation: Some(index_allocation), index_count, depth_image, - depth_allocation, + depth_allocation: Some(depth_allocation), depth_image_view, pipeline_layout, graphics_pipeline, vertex_buffer, - vertex_allocation, - sync, + vertex_allocation: Some(vertex_allocation), + sync: Some(sync), current_frame: 0, }) } diff --git a/crates/renderer/src/renderer.rs b/crates/renderer/src/renderer.rs index 60dcb17..80e5048 100644 --- a/crates/renderer/src/renderer.rs +++ b/crates/renderer/src/renderer.rs @@ -50,24 +50,24 @@ pub struct Renderer { /// The compiled graphics pipeline state. pub(crate) graphics_pipeline: vk::Pipeline, /// Memory manager for GPU allocations. - pub(crate) allocator: Allocator, + pub(crate) allocator: Option, /// Buffer containing the vertex data for the initial triangle. pub(crate) vertex_buffer: vk::Buffer, /// Memory allocation for the vertex buffer. - pub(crate) vertex_allocation: Allocation, + pub(crate) vertex_allocation: Option, /// Buffer containing the index data for indexed drawing. pub(crate) index_buffer: vk::Buffer, /// Memory allocation for the index buffer. - pub(crate) index_allocation: Allocation, + pub(crate) index_allocation: Option, pub(crate) index_count: u32, /// The depth image used for depth testing. pub(crate) depth_image: vk::Image, /// Image view for the depth buffer. pub(crate) depth_image_view: vk::ImageView, /// Memory allocation for the depth image. - pub(crate) depth_allocation: Allocation, + pub(crate) depth_allocation: Option, /// Synchronization primitives for frame-by-frame execution. - pub(crate) sync: SyncPrimitives, + pub(crate) sync: Option, /// Index of the current frame being processed (0 to `crate::MAX_FRAMES_IN_FLIGHT` - 1). pub(crate) current_frame: usize, } @@ -75,8 +75,12 @@ pub struct Renderer { impl Renderer { /// Renders a single frame. pub fn draw_frame(&mut self) -> Result<(), RendererError> { - let in_flight_fence = self.sync.in_flight[self.current_frame]; - let image_available_semaphore = self.sync.image_available[self.current_frame]; + let sync = self + .sync + .as_ref() + .ok_or(RendererError::SyncPrimitivesMissing)?; + let in_flight_fence = sync.in_flight[self.current_frame]; + let image_available_semaphore = sync.image_available[self.current_frame]; let cmd = self.command_buffers[self.current_frame]; // 1. Wait for the current frame's GPU work to finish @@ -97,7 +101,11 @@ impl Renderer { }; // Use the semaphore tied to this specific swapchain image for rendering completion - let render_finished_semaphore = self.sync.render_finished[image_index as usize]; + let render_finished_semaphore = self + .sync + .as_ref() + .ok_or(RendererError::SyncPrimitivesMissing)? + .render_finished[image_index as usize]; // 3. Reset and begin recording the command buffer unsafe { @@ -352,20 +360,30 @@ impl Renderer { unsafe { let _ = self.device.device_wait_idle(); - let _ = self + let allocator = self .allocator - .free(std::ptr::read(&raw const self.vertex_allocation)); + .as_mut() + .ok_or(RendererError::AllocatorMissing)?; + + if let Some(alloc) = self.vertex_allocation.take() { + let _ = allocator.free(alloc); + } self.device.destroy_buffer(self.vertex_buffer, None); - let _ = self - .allocator - .free(std::ptr::read(&raw const self.index_allocation)); + if let Some(alloc) = self.index_allocation.take() { + let _ = allocator.free(alloc); + } self.device.destroy_buffer(self.index_buffer, None); } + let allocator = self + .allocator + .as_mut() + .ok_or(RendererError::AllocatorMissing)?; + let (v_buf, v_alloc) = create_gpu_buffer( &self.device, - &mut self.allocator, + allocator, bytemuck::cast_slice(vertices), vk::BufferUsageFlags::VERTEX_BUFFER, "Chunk Vertex Buffer", @@ -373,16 +391,16 @@ impl Renderer { let (i_buf, i_alloc) = crate::create_gpu_buffer( &self.device, - &mut self.allocator, + allocator, bytemuck::cast_slice(indices), vk::BufferUsageFlags::INDEX_BUFFER, "Chunk Index Buffer", )?; self.vertex_buffer = v_buf; - self.vertex_allocation = v_alloc; + self.vertex_allocation = Some(v_alloc); self.index_buffer = i_buf; - self.index_allocation = i_alloc; + self.index_allocation = Some(i_alloc); self.index_count = indices.len() as u32; Ok(()) @@ -398,46 +416,46 @@ impl Drop for Renderer { self.device .destroy_pipeline_layout(self.pipeline_layout, None); - if let Err(e) = self - .allocator - .free(std::ptr::read(&raw const self.vertex_allocation)) - { - tracing::error!("Failed to free vertex buffer allocation: {e}"); + if let Some(allocator) = self.allocator.as_mut() { + if let Some(alloc) = self.vertex_allocation.take() + && let Err(e) = allocator.free(alloc) + { + tracing::error!("Failed to free vertex buffer allocation: {e}"); + } + if let Some(alloc) = self.index_allocation.take() + && let Err(e) = allocator.free(alloc) + { + tracing::error!("Failed to free index buffer allocation: {e}"); + } + if let Some(alloc) = self.depth_allocation.take() + && let Err(e) = allocator.free(alloc) + { + tracing::error!("Failed to free depth image allocation: {e}"); + } } self.device.destroy_buffer(self.vertex_buffer, None); - - if let Err(e) = self - .allocator - .free(std::ptr::read(&raw const self.index_allocation)) - { - tracing::error!("Failed to free index buffer allocation: {e}"); - } self.device.destroy_buffer(self.index_buffer, None); - self.device.destroy_image_view(self.depth_image_view, None); - if let Err(e) = self - .allocator - .free(std::ptr::read(&raw const self.depth_allocation)) - { - tracing::error!("Failed to free depth image allocation: {e}"); - } self.device.destroy_image(self.depth_image, None); + // Drop the allocator before destroying the logical device so it can + // release any remaining memory blocks while the device is still valid. + drop(self.allocator.take()); + self.device.destroy_command_pool(self.command_pool, None); - // Use the safe cleanup function from sync module - let sync = std::ptr::read(&raw const self.sync); - crate::sync::destroy_sync_primitives(&self.device, sync); + if let Some(sync) = self.sync.take() { + crate::sync::destroy_sync_primitives(&self.device, sync); + } - // Destroy the swapchain - self.swapchain_loader - .destroy_swapchain(self.swapchain, None); - - // Destroy image views + // Destroy image views before the swapchain that owns the underlying images. for &view in &self.swapchain_image_views { self.device.destroy_image_view(view, None); } + self.swapchain_loader + .destroy_swapchain(self.swapchain, None); + // Destroy the logical device self.device.destroy_device(None);