From 38077c980dd0c76fc31eef7a95190bbaf2d3376b Mon Sep 17 00:00:00 2001 From: Serkyo Date: Tue, 12 May 2026 23:00:30 +0200 Subject: [PATCH] feat(renderer): implement depth testing and indexed 3d rendering --- assets/shaders/cube.frag | 4 +- assets/shaders/cube.frag.spv | 4 +- assets/shaders/cube.vert | 6 +- assets/shaders/cube.vert.spv | 4 +- crates/renderer/src/lib.rs | 208 ++++++++++++++++++++----- crates/renderer/src/mesh.rs | 11 +- crates/renderer/src/pipeline.rs | 15 +- crates/renderer/src/renderer.rs | 263 +++++++++++++++++++++++++------- 8 files changed, 402 insertions(+), 113 deletions(-) diff --git a/assets/shaders/cube.frag b/assets/shaders/cube.frag index 5ec8259..91d2c3f 100644 --- a/assets/shaders/cube.frag +++ b/assets/shaders/cube.frag @@ -1,9 +1,9 @@ #version 450 -layout(location = 0) in vec2 in_tex_coord; +layout(location = 0) in vec3 frag_color; layout(location = 0) out vec4 out_color; void main () { - out_color = vec4(in_tex_coord, 1.0, 1.0); + out_color = vec4(frag_color, 1.0); } \ No newline at end of file diff --git a/assets/shaders/cube.frag.spv b/assets/shaders/cube.frag.spv index 23b2642..9a6a893 100644 --- a/assets/shaders/cube.frag.spv +++ b/assets/shaders/cube.frag.spv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3874fa1c7782cba103fa7df1505c900506ab27a19a00f6e15c073c6a96b0f34c -size 556 +oid sha256:f3f5c483dddf88cd90dbb62aa579c7f815b7ff304407d27c19a9a52cc450b4bb +size 572 diff --git a/assets/shaders/cube.vert b/assets/shaders/cube.vert index 55f6a40..6e31124 100644 --- a/assets/shaders/cube.vert +++ b/assets/shaders/cube.vert @@ -2,9 +2,9 @@ #version 450 layout(location = 0) in vec3 in_position; -layout(location = 1) in vec2 in_tex_coord; +layout(location = 1) in vec3 in_color; -layout(location = 0) out vec2 out_tex_coord; +layout(location = 0) out vec3 frag_color; layout(push_constant) uniform PushConstants { mat4 mvp; @@ -13,5 +13,5 @@ layout(push_constant) uniform PushConstants { void main() { gl_Position = push_constants.mvp * vec4(in_position, 1.0); - out_tex_coord = in_tex_coord; + frag_color = in_color; } diff --git a/assets/shaders/cube.vert.spv b/assets/shaders/cube.vert.spv index 6c14b94..befb022 100644 --- a/assets/shaders/cube.vert.spv +++ b/assets/shaders/cube.vert.spv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcac088d9ed53209a22008ab3ad49d88dbb8e8f6b4b2a17fef5a8368856019ea -size 1360 +oid sha256:27d230b5332e98c109e6c8c0da8a316f341d82886c1c0a6d568f512b50e31d86 +size 1320 diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index 648dbc7..b6b90e6 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -19,12 +19,15 @@ mod sync; pub const MAX_FRAMES_IN_FLIGHT: usize = 3; use ash::{Entry, vk}; +use gpu_allocator::vulkan::{Allocation, Allocator}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use std::ffi::c_char; pub use error::RendererError; pub use renderer::Renderer; +use crate::mesh::Vertex; + impl Renderer { /// Initializes the Vulkan renderer. /// @@ -113,8 +116,11 @@ impl Renderer { let graphics_pipeline = pipeline::create_graphics_pipeline(&device, pipeline_layout, swapchain_format)?; - // 13. Vertex Buffer Initialization - let (vertex_buffer, vertex_allocation) = create_vertex_buffer(&device, &mut allocator)?; + let (vertex_buffer, vertex_allocation, index_buffer, index_allocation) = + create_geometry(&device, &mut allocator)?; + + let (depth_image, depth_allocation, depth_image_view) = + create_depth_resources(&device, &mut allocator, swapchain_extent)?; Ok(Self { _entry: entry, @@ -136,6 +142,11 @@ impl Renderer { command_pool, command_buffers, allocator, + index_buffer, + index_allocation, + depth_image, + depth_allocation, + depth_image_view, pipeline_layout, graphics_pipeline, vertex_buffer, @@ -166,36 +177,161 @@ fn create_allocator( Ok(allocator) } -/// Creates a vertex buffer and populates it with initial triangle data. -#[allow(clippy::expect_used)] -fn create_vertex_buffer( +/// Creates the 3D geometry buffers (vertex and index) for a cube. +fn create_geometry( device: &ash::Device, - allocator: &mut gpu_allocator::vulkan::Allocator, -) -> Result<(vk::Buffer, gpu_allocator::vulkan::Allocation), RendererError> { + allocator: &mut Allocator, +) -> Result<(vk::Buffer, Allocation, vk::Buffer, Allocation), RendererError> { let vertices = [ - mesh::Vertex { - position: [0.0, -0.5, 0.0], - tex_coord: [0.5, 0.0], + // Front face + Vertex { + position: [-0.5, -0.5, 0.5], + color: [1.0, 0.0, 0.0], }, - mesh::Vertex { - position: [-0.5, 0.5, 0.0], - tex_coord: [0.0, 1.0], + Vertex { + position: [0.5, -0.5, 0.5], + color: [0.0, 1.0, 0.0], }, - mesh::Vertex { - position: [0.5, 0.5, 0.0], - tex_coord: [1.0, 1.0], + Vertex { + position: [0.5, 0.5, 0.5], + color: [0.0, 0.0, 1.0], + }, + Vertex { + position: [-0.5, 0.5, 0.5], + color: [1.0, 1.0, 1.0], + }, + // Back face + Vertex { + position: [-0.5, -0.5, -0.5], + color: [1.0, 0.0, 0.0], + }, + Vertex { + position: [0.5, -0.5, -0.5], + color: [0.0, 1.0, 0.0], + }, + Vertex { + position: [0.5, 0.5, -0.5], + color: [0.0, 0.0, 1.0], + }, + Vertex { + position: [-0.5, 0.5, -0.5], + color: [1.0, 1.0, 1.0], }, ]; + let indices: [u32; 36] = [ + 0, 1, 2, 2, 3, 0, // front + 1, 5, 6, 6, 2, 1, // right + 7, 6, 5, 5, 4, 7, // back + 4, 0, 3, 3, 7, 4, // left + 4, 5, 1, 1, 0, 4, // bottom + 3, 2, 6, 6, 7, 3, // top + ]; + + let (vertex_buffer, vertex_allocation) = create_gpu_buffer( + device, + allocator, + bytemuck::cast_slice(&vertices), + vk::BufferUsageFlags::VERTEX_BUFFER, + "Vertex Buffer", + )?; + + let (index_buffer, index_allocation) = create_gpu_buffer( + device, + allocator, + bytemuck::cast_slice(&indices), + vk::BufferUsageFlags::INDEX_BUFFER, + "Index Buffer", + )?; + + Ok(( + vertex_buffer, + vertex_allocation, + index_buffer, + index_allocation, + )) +} + +/// Creates the depth buffer resources (image, memory, and view). +fn create_depth_resources( + device: &ash::Device, + allocator: &mut Allocator, + extent: vk::Extent2D, +) -> Result<(vk::Image, Allocation, vk::ImageView), RendererError> { + let depth_format = vk::Format::D32_SFLOAT; + + let image_create_info = vk::ImageCreateInfo::default() + .image_type(vk::ImageType::TYPE_2D) + .format(depth_format) + .extent(vk::Extent3D { + width: extent.width, + height: extent.height, + depth: 1, + }) + .mip_levels(1) + .array_layers(1) + .samples(vk::SampleCountFlags::TYPE_1) + .tiling(vk::ImageTiling::OPTIMAL) + .usage(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) + .sharing_mode(vk::SharingMode::EXCLUSIVE) + .initial_layout(vk::ImageLayout::UNDEFINED); + + let depth_image = unsafe { device.create_image(&image_create_info, None)? }; + + let requirements = unsafe { device.get_image_memory_requirements(depth_image) }; + + let depth_allocation = allocator.allocate(&gpu_allocator::vulkan::AllocationCreateDesc { + name: "Depth Image", + requirements, + location: gpu_allocator::MemoryLocation::GpuOnly, + linear: false, + allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged, + })?; + + unsafe { + device.bind_image_memory( + depth_image, + depth_allocation.memory(), + depth_allocation.offset(), + )?; + } + + let view_create_info = vk::ImageViewCreateInfo::default() + .image(depth_image) + .view_type(vk::ImageViewType::TYPE_2D) + .format(depth_format) + .subresource_range(vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::DEPTH, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }); + + let depth_image_view = unsafe { device.create_image_view(&view_create_info, None)? }; + + Ok((depth_image, depth_allocation, depth_image_view)) +} + +/// Helper function to create and populate a GPU buffer. +fn create_gpu_buffer( + device: &ash::Device, + allocator: &mut Allocator, + data: &[u8], + usage: vk::BufferUsageFlags, + name: &str, +) -> Result<(vk::Buffer, Allocation), RendererError> { + let size = data.len() as u64; let buffer_info = vk::BufferCreateInfo::default() - .size(u64::try_from(std::mem::size_of_val(&vertices)).expect("Vertices size exceeds u64")) - .usage(vk::BufferUsageFlags::VERTEX_BUFFER); + .size(size) + .usage(usage) + .sharing_mode(vk::SharingMode::EXCLUSIVE); - let vertex_buffer = unsafe { device.create_buffer(&buffer_info, None)? }; - let requirements = unsafe { device.get_buffer_memory_requirements(vertex_buffer) }; + let buffer = unsafe { device.create_buffer(&buffer_info, None)? }; - let vertex_allocation = allocator.allocate(&gpu_allocator::vulkan::AllocationCreateDesc { - name: "Vertex Buffer", + let requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; + let allocation = allocator.allocate(&gpu_allocator::vulkan::AllocationCreateDesc { + name, requirements, location: gpu_allocator::MemoryLocation::CpuToGpu, linear: true, @@ -203,23 +339,17 @@ fn create_vertex_buffer( })?; unsafe { - device.bind_buffer_memory( - vertex_buffer, - vertex_allocation.memory(), - vertex_allocation.offset(), - )?; - - let ptr = vertex_allocation - .mapped_ptr() - .ok_or(RendererError::NoSuitableGpu)? // Should have a better error but for now - .as_ptr(); - - std::ptr::copy_nonoverlapping( - vertices.as_ptr().cast::(), - ptr.cast::(), - std::mem::size_of_val(&vertices), - ); + device.bind_buffer_memory(buffer, allocation.memory(), allocation.offset())?; } - Ok((vertex_buffer, vertex_allocation)) + let ptr = allocation + .mapped_ptr() + .ok_or(RendererError::NoSuitableGpu)? + .as_ptr(); + + unsafe { + std::ptr::copy_nonoverlapping(data.as_ptr(), ptr.cast(), data.len()); + } + + Ok((buffer, allocation)) } diff --git a/crates/renderer/src/mesh.rs b/crates/renderer/src/mesh.rs index c9d0e11..51bd690 100644 --- a/crates/renderer/src/mesh.rs +++ b/crates/renderer/src/mesh.rs @@ -11,8 +11,7 @@ use bytemuck::{Pod, Zeroable}; pub struct Vertex { /// 3D position of the vertex (X, Y, Z). pub position: [f32; 3], - /// 2D texture coordinates (U, V). - pub tex_coord: [f32; 2], + pub color: [f32; 3], } impl Vertex { @@ -42,15 +41,11 @@ impl Vertex { .location(0) .format(ash::vk::Format::R32G32B32_SFLOAT) .offset(0), - // Location 1: tex_coord (vec2 -> R32G32_SFLOAT) ash::vk::VertexInputAttributeDescription::default() .binding(0) .location(1) - .format(ash::vk::Format::R32G32_SFLOAT) - .offset( - u32::try_from(std::mem::size_of::<[f32; 3]>()) - .expect("Vertex offset exceeds u32 range"), - ), + .format(ash::vk::Format::R32G32B32_SFLOAT) + .offset(12), ] } } diff --git a/crates/renderer/src/pipeline.rs b/crates/renderer/src/pipeline.rs index e06277f..3c3130e 100644 --- a/crates/renderer/src/pipeline.rs +++ b/crates/renderer/src/pipeline.rs @@ -110,8 +110,16 @@ pub fn create_graphics_pipeline( vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states); let color_formats = [color_format]; - let mut rendering_info = - vk::PipelineRenderingCreateInfo::default().color_attachment_formats(&color_formats); + let mut rendering_info = vk::PipelineRenderingCreateInfo::default() + .color_attachment_formats(&color_formats) + .depth_attachment_format(vk::Format::D32_SFLOAT); + + let depth_stencil_state = &vk::PipelineDepthStencilStateCreateInfo::default() + .depth_test_enable(true) + .depth_write_enable(true) + .depth_compare_op(vk::CompareOp::LESS) + .depth_bounds_test_enable(false) + .stencil_test_enable(false); // 3. Finalize Pipeline Creation let pipeline_info = vk::GraphicsPipelineCreateInfo::default() @@ -124,7 +132,8 @@ pub fn create_graphics_pipeline( .multisample_state(&multisampling) .color_blend_state(&color_blending) .dynamic_state(&dynamic_state_info) - .layout(layout); + .layout(layout) + .depth_stencil_state(depth_stencil_state); let pipeline = unsafe { device diff --git a/crates/renderer/src/renderer.rs b/crates/renderer/src/renderer.rs index af4775a..79dfb5c 100644 --- a/crates/renderer/src/renderer.rs +++ b/crates/renderer/src/renderer.rs @@ -54,6 +54,16 @@ pub struct Renderer { pub(crate) vertex_buffer: vk::Buffer, /// Memory allocation for the vertex buffer. pub(crate) vertex_allocation: Allocation, + /// 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, + /// 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, /// Synchronization primitives for frame-by-frame execution. pub(crate) sync: SyncPrimitives, /// Index of the current frame being processed (0 to `crate::MAX_FRAMES_IN_FLIGHT` - 1). @@ -99,7 +109,95 @@ impl Renderer { let image = self.swapchain_images[image_index as usize]; let view = self.swapchain_image_views[image_index as usize]; - // 4. Transition the swapchain image to a layout suitable for drawing + // 4. Record the actual rendering commands + self.record_commands(cmd, view, image)?; + + // 5. Submit the work to the GPU + let submit_info = vk::SubmitInfo::default() + .wait_semaphores(std::slice::from_ref(&image_available_semaphore)) + .wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]) + .command_buffers(std::slice::from_ref(&cmd)) + .signal_semaphores(std::slice::from_ref(&render_finished_semaphore)); + + unsafe { + self.device + .queue_submit(self.graphics_queue, &[submit_info], in_flight_fence)?; + } + + // 6. Present the result to the screen + let present_info = vk::PresentInfoKHR::default() + .wait_semaphores(std::slice::from_ref(&render_finished_semaphore)) + .swapchains(std::slice::from_ref(&self.swapchain)) + .image_indices(std::slice::from_ref(&image_index)); + + unsafe { + self.swapchain_loader + .queue_present(self.graphics_queue, &present_info)?; + } + + // Advance the frame index for the next call + self.current_frame = (self.current_frame + 1) % crate::MAX_FRAMES_IN_FLIGHT; + + Ok(()) + } + + /// Records the drawing commands into the given command buffer. + fn record_commands( + &self, + cmd: vk::CommandBuffer, + view: vk::ImageView, + image: vk::Image, + ) -> Result<(), RendererError> { + // Transition layouts for drawing + self.transition_to_draw_layout(cmd, image); + + // Begin rendering + let color_attachment = vk::RenderingAttachmentInfo::default() + .image_view(view) + .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) + .load_op(vk::AttachmentLoadOp::CLEAR) + .store_op(vk::AttachmentStoreOp::STORE) + .clear_value(vk::ClearValue { + color: vk::ClearColorValue { + float32: [0.1, 0.2, 0.4, 1.0], + }, + }); + + let depth_attachment = vk::RenderingAttachmentInfo::default() + .image_view(self.depth_image_view) + .image_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL) + .load_op(vk::AttachmentLoadOp::CLEAR) + .store_op(vk::AttachmentStoreOp::STORE) + .clear_value(vk::ClearValue { + depth_stencil: vk::ClearDepthStencilValue { + depth: 1.0, + stencil: 0, + }, + }); + + let rendering_info = vk::RenderingInfo::default() + .render_area(vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: self.swapchain_extent, + }) + .layer_count(1) + .color_attachments(std::slice::from_ref(&color_attachment)) + .depth_attachment(&depth_attachment); + + unsafe { + self.device.cmd_begin_rendering(cmd, &rendering_info); + self.issue_draw_calls(cmd); + self.device.cmd_end_rendering(cmd); + } + + // Transition back to present + self.transition_to_present_layout(cmd, image)?; + + Ok(()) + } + + /// Transitions the swapchain and depth images to layouts suitable for drawing. + fn transition_to_draw_layout(&self, cmd: vk::CommandBuffer, image: vk::Image) { let range = vk::ImageSubresourceRange { aspect_mask: vk::ImageAspectFlags::COLOR, base_mip_level: 0, @@ -118,38 +216,105 @@ impl Renderer { .old_layout(vk::ImageLayout::UNDEFINED) .new_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL); - let dependency_info = vk::DependencyInfo::default() - .image_memory_barriers(std::slice::from_ref(&barrier_to_draw)); + let depth_range = vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::DEPTH, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }; + + let depth_barrier = vk::ImageMemoryBarrier2::default() + .image(self.depth_image) + .subresource_range(depth_range) + .src_stage_mask(vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS) + .src_access_mask(vk::AccessFlags2::empty()) + .dst_stage_mask(vk::PipelineStageFlags2::EARLY_FRAGMENT_TESTS) + .dst_access_mask(vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE) + .old_layout(vk::ImageLayout::UNDEFINED) + .new_layout(vk::ImageLayout::DEPTH_ATTACHMENT_OPTIMAL); + + let barriers = [barrier_to_draw, depth_barrier]; + let dependency_info = vk::DependencyInfo::default().image_memory_barriers(&barriers); unsafe { self.device.cmd_pipeline_barrier2(cmd, &dependency_info) }; + } - // 5. Begin Dynamic Rendering with a clear color - let color_attachment = vk::RenderingAttachmentInfo::default() - .image_view(view) - .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) - .load_op(vk::AttachmentLoadOp::CLEAR) - .store_op(vk::AttachmentStoreOp::STORE) - .clear_value(vk::ClearValue { - color: vk::ClearColorValue { - float32: [0.1, 0.2, 0.4, 1.0], // Project Catalyst Blue - }, - }); + /// Issues the actual draw calls for the frame. + fn issue_draw_calls(&self, cmd: vk::CommandBuffer) { + unsafe { + self.device.cmd_bind_pipeline( + cmd, + vk::PipelineBindPoint::GRAPHICS, + self.graphics_pipeline, + ); - let rendering_info = vk::RenderingInfo::default() - .render_area(vk::Rect2D { + #[allow(clippy::cast_precision_loss)] + let viewport = vk::Viewport { + x: 0.0, + y: 0.0, + width: self.swapchain_extent.width as f32, + height: self.swapchain_extent.height as f32, + min_depth: 0.0, + max_depth: 1.0, + }; + self.device.cmd_set_viewport(cmd, 0, &[viewport]); + + let scissor = vk::Rect2D { offset: vk::Offset2D { x: 0, y: 0 }, extent: self.swapchain_extent, - }) - .layer_count(1) - .color_attachments(std::slice::from_ref(&color_attachment)); + }; + self.device.cmd_set_scissor(cmd, 0, &[scissor]); - unsafe { - self.device.cmd_begin_rendering(cmd, &rendering_info); - // Future draw calls will go here - self.device.cmd_end_rendering(cmd); + self.device + .cmd_bind_vertex_buffers(cmd, 0, &[self.vertex_buffer], &[0]); + self.device + .cmd_bind_index_buffer(cmd, self.index_buffer, 0, vk::IndexType::UINT32); + + #[allow(clippy::cast_precision_loss)] + let aspect = + f64::from(self.swapchain_extent.width) / f64::from(self.swapchain_extent.height); + #[allow(clippy::cast_possible_truncation)] + let mut projection = + glam::Mat4::perspective_rh(45.0_f32.to_radians(), aspect as f32, 0.1, 100.0); + projection.col_mut(1).y *= -1.0; + + let view = glam::Mat4::look_at_rh( + glam::vec3(2.0, 2.0, 2.0), + glam::vec3(0.0, 0.0, 0.0), + glam::vec3(0.0, 1.0, 0.0), + ); + + let model = glam::Mat4::from_rotation_y(0.0); + let mvp = projection * view * model; + + let mvp_bytes = bytemuck::cast_slice(mvp.as_ref()); + self.device.cmd_push_constants( + cmd, + self.pipeline_layout, + vk::ShaderStageFlags::VERTEX, + 0, + mvp_bytes, + ); + + self.device.cmd_draw_indexed(cmd, 36, 1, 0, 0, 0); } + } + + /// Transitions the swapchain image back to the presentation layout. + fn transition_to_present_layout( + &self, + cmd: vk::CommandBuffer, + image: vk::Image, + ) -> Result<(), RendererError> { + let range = vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }; - // 6. Transition the image back to Present layout let barrier_to_present = vk::ImageMemoryBarrier2::default() .image(image) .subresource_range(range) @@ -168,32 +333,6 @@ impl Renderer { self.device.end_command_buffer(cmd)?; } - // 7. Submit the work to the GPU - let submit_info = vk::SubmitInfo::default() - .wait_semaphores(std::slice::from_ref(&image_available_semaphore)) - .wait_dst_stage_mask(&[vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]) - .command_buffers(std::slice::from_ref(&cmd)) - .signal_semaphores(std::slice::from_ref(&render_finished_semaphore)); - - unsafe { - self.device - .queue_submit(self.graphics_queue, &[submit_info], in_flight_fence)?; - } - - // 8. Present the result to the screen - let present_info = vk::PresentInfoKHR::default() - .wait_semaphores(std::slice::from_ref(&render_finished_semaphore)) - .swapchains(std::slice::from_ref(&self.swapchain)) - .image_indices(std::slice::from_ref(&image_index)); - - unsafe { - self.swapchain_loader - .queue_present(self.graphics_queue, &present_info)?; - } - - // Advance the frame index for the next call - self.current_frame = (self.current_frame + 1) % crate::MAX_FRAMES_IN_FLIGHT; - Ok(()) } } @@ -213,9 +352,25 @@ impl Drop for Renderer { { tracing::error!("Failed to free vertex buffer 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); + self.device.destroy_command_pool(self.command_pool, None); // Use the safe cleanup function from sync module @@ -237,9 +392,9 @@ impl Drop for Renderer { // Destroy the surface self.surface_loader.destroy_surface(self.surface, None); - // Destroy the debug messenger if we created one - if let Some(utils) = &self.debug_utils { - utils.destroy_debug_utils_messenger(self.debug_messenger, None); + // Destroy the debug messenger if it exists + if let Some(debug_utils) = self.debug_utils.as_ref() { + debug_utils.destroy_debug_utils_messenger(self.debug_messenger, None); } // Destroy the instance