chore(workspace): enforce missing_docs and resolve all linting violations

This commit is contained in:
Serkyo 2026-05-12 18:52:30 +02:00
parent 6d4435f856
commit 34f1a9d989
11 changed files with 194 additions and 122 deletions

View file

@ -1,3 +1,8 @@
//! Main entry point for the Project Catalyst client.
//!
//! This crate handles window creation, input processing, and drives the
//! renderer to display the game world.
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
use tracing::info; use tracing::info;

View file

@ -1,3 +1,5 @@
//! Logic for selecting physical devices and creating logical devices.
use crate::error::RendererError; use crate::error::RendererError;
use ash::{Device, Instance, khr, vk}; use ash::{Device, Instance, khr, vk};
@ -63,7 +65,8 @@ pub fn find_graphics_queue_family(
let props = unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; let props = unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
for (index, prop) in props.iter().enumerate() { for (index, prop) in props.iter().enumerate() {
let index = index as u32; #[allow(clippy::expect_used)]
let index = u32::try_from(index).expect("Queue family index exceeds u32 range");
let graphics = prop.queue_flags.contains(vk::QueueFlags::GRAPHICS); let graphics = prop.queue_flags.contains(vk::QueueFlags::GRAPHICS);
let present = unsafe { let present = unsafe {
surface_loader.get_physical_device_surface_support(physical_device, index, surface)? surface_loader.get_physical_device_surface_support(physical_device, index, surface)?

View file

@ -1,6 +1,9 @@
//! Error types for the renderer crate.
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Error)] #[derive(Debug, Error)]
/// Enumerates all possible errors that can occur during rendering operations.
pub enum RendererError { pub enum RendererError {
/// The Vulkan library could not be loaded from the system. /// The Vulkan library could not be loaded from the system.
#[error("Failed to load Vulkan library")] #[error("Failed to load Vulkan library")]
@ -14,4 +17,10 @@ pub enum RendererError {
/// An error occurred during GPU memory allocation. /// An error occurred during GPU memory allocation.
#[error("GPU allocation error")] #[error("GPU allocation error")]
AllocationError(#[from] gpu_allocator::AllocationError), AllocationError(#[from] gpu_allocator::AllocationError),
/// An I/O error occurred (e.g. reading a shader).
#[error("I/O error")]
IoError(#[from] std::io::Error),
/// An invalid string was encountered.
#[error("Invalid string")]
InvalidString,
} }

View file

@ -1,3 +1,10 @@
#![allow(unsafe_code)]
//! Voxel rendering engine using Vulkan 1.3 and dynamic rendering.
//!
//! This crate provides the core `Renderer` structure and associated types
//! for handling GPU resources and drawing operations.
mod device; mod device;
pub mod error; pub mod error;
mod instance; mod instance;
@ -8,6 +15,7 @@ mod surface;
mod swapchain; mod swapchain;
mod sync; mod sync;
/// The maximum number of frames that can be processed by the GPU and CPU simultaneously.
pub const MAX_FRAMES_IN_FLIGHT: usize = 3; pub const MAX_FRAMES_IN_FLIGHT: usize = 3;
use ash::{Entry, vk}; use ash::{Entry, vk};
@ -22,6 +30,11 @@ impl Renderer {
/// ///
/// This function loads the Vulkan library, creates an instance, selects a GPU, /// This function loads the Vulkan library, creates an instance, selects a GPU,
/// and initializes a logical device with a graphics queue. /// and initializes a logical device with a graphics queue.
///
/// # Panics
///
/// Panics if `MAX_FRAMES_IN_FLIGHT` or vertex data sizes exceed `u32`/`u64` limits.
#[allow(clippy::expect_used)]
pub fn new( pub fn new(
display_handle: RawDisplayHandle, display_handle: RawDisplayHandle,
window_handle: RawWindowHandle, window_handle: RawWindowHandle,
@ -78,10 +91,11 @@ impl Renderer {
let command_pool = unsafe { device.create_command_pool(&pool_create_info, None)? }; let command_pool = unsafe { device.create_command_pool(&pool_create_info, None)? };
// 9. Command Buffers // 9. Command Buffers
#[allow(clippy::expect_used)]
let alloc_info = vk::CommandBufferAllocateInfo::default() let alloc_info = vk::CommandBufferAllocateInfo::default()
.command_pool(command_pool) .command_pool(command_pool)
.level(vk::CommandBufferLevel::PRIMARY) .level(vk::CommandBufferLevel::PRIMARY)
.command_buffer_count(MAX_FRAMES_IN_FLIGHT as u32); .command_buffer_count(u32::try_from(MAX_FRAMES_IN_FLIGHT).expect("MAX_FRAMES_IN_FLIGHT exceeds u32"));
let command_buffers = unsafe { device.allocate_command_buffers(&alloc_info)? }; let command_buffers = unsafe { device.allocate_command_buffers(&alloc_info)? };
@ -90,81 +104,16 @@ impl Renderer {
sync::create_sync_primitives(&device, MAX_FRAMES_IN_FLIGHT, swapchain_images.len())?; sync::create_sync_primitives(&device, MAX_FRAMES_IN_FLIGHT, swapchain_images.len())?;
// 11. GPU Memory Allocator // 11. GPU Memory Allocator
// The allocator handles the complexity of sub-allocating memory blocks from the GPU. let mut allocator = create_allocator(&instance, &device, physical_device)?;
let allocator_create_info = gpu_allocator::vulkan::AllocatorCreateDesc {
instance: instance.clone(),
device: device.clone(),
physical_device,
debug_settings: Default::default(),
buffer_device_address: false,
allocation_sizes: Default::default(),
};
let allocator = gpu_allocator::vulkan::Allocator::new(&allocator_create_info)
.expect("Failed to create Vulkan allocator");
// 12. Graphics Pipeline Configuration // 12. Graphics Pipeline Configuration
// The pipeline defines the fixed-function and programmable state for rendering voxel geometry. let pipeline_layout = pipeline::create_pipeline_layout(&device)?;
let pipeline_layout = pipeline::create_pipeline_layout(&device);
let graphics_pipeline = let graphics_pipeline =
pipeline::create_graphics_pipeline(&device, pipeline_layout, swapchain_format); pipeline::create_graphics_pipeline(&device, pipeline_layout, swapchain_format)?;
// 13. Vertex Buffer Initialization // 13. Vertex Buffer Initialization
// A simple triangle is defined in normalized device coordinates and moved to GPU memory. let (vertex_buffer, vertex_allocation) =
let vertices = [ create_vertex_buffer(&device, &mut allocator)?;
mesh::Vertex {
position: [0.0, -0.5, 0.0],
tex_coord: [0.5, 0.0],
},
mesh::Vertex {
position: [0.5, 0.5, 0.0],
tex_coord: [1.0, 1.0],
},
mesh::Vertex {
position: [-0.5, 0.5, 0.0],
tex_coord: [0.0, 1.0],
},
];
// Create the buffer handle and query its memory requirements.
let buffer_info = vk::BufferCreateInfo::default()
.size(std::mem::size_of_val(&vertices) as u64)
.usage(vk::BufferUsageFlags::VERTEX_BUFFER);
let vertex_buffer = unsafe { device.create_buffer(&buffer_info, None)? };
let requirements = unsafe { device.get_buffer_memory_requirements(vertex_buffer) };
let mut allocator = allocator;
// Allocate memory that is visible to the CPU for data transfer.
let vertex_allocation =
allocator.allocate(&gpu_allocator::vulkan::AllocationCreateDesc {
name: "Vertex Buffer",
requirements,
location: gpu_allocator::MemoryLocation::CpuToGpu,
linear: true,
allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged,
})?;
// Bind the allocated memory to the buffer handle and copy the vertex data.
unsafe {
device.bind_buffer_memory(
vertex_buffer,
vertex_allocation.memory(),
vertex_allocation.offset(),
)?;
let ptr = vertex_allocation
.mapped_ptr()
.expect("Failed to map vertex buffer memory")
.as_ptr();
std::ptr::copy_nonoverlapping(
vertices.as_ptr() as *const u8,
ptr as *mut u8,
std::mem::size_of_val(&vertices),
);
}
Ok(Self { Ok(Self {
_entry: entry, _entry: entry,
@ -195,3 +144,82 @@ impl Renderer {
}) })
} }
} }
/// Creates a GPU memory allocator.
fn create_allocator(
instance: &ash::Instance,
device: &ash::Device,
physical_device: vk::PhysicalDevice,
) -> Result<gpu_allocator::vulkan::Allocator, RendererError> {
let allocator_create_info = gpu_allocator::vulkan::AllocatorCreateDesc {
instance: instance.clone(),
device: device.clone(),
physical_device,
debug_settings: gpu_allocator::AllocatorDebugSettings::default(),
buffer_device_address: false,
allocation_sizes: gpu_allocator::AllocationSizes::default(),
};
let allocator = gpu_allocator::vulkan::Allocator::new(&allocator_create_info)
.map_err(RendererError::AllocationError)?;
Ok(allocator)
}
/// Creates a vertex buffer and populates it with initial triangle data.
#[allow(clippy::expect_used)]
fn create_vertex_buffer(
device: &ash::Device,
allocator: &mut gpu_allocator::vulkan::Allocator,
) -> Result<(vk::Buffer, gpu_allocator::vulkan::Allocation), RendererError> {
let vertices = [
mesh::Vertex {
position: [0.0, -0.5, 0.0],
tex_coord: [0.5, 0.0],
},
mesh::Vertex {
position: [-0.5, 0.5, 0.0],
tex_coord: [0.0, 1.0],
},
mesh::Vertex {
position: [0.5, 0.5, 0.0],
tex_coord: [1.0, 1.0],
},
];
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);
let vertex_buffer = unsafe { device.create_buffer(&buffer_info, None)? };
let requirements = unsafe { device.get_buffer_memory_requirements(vertex_buffer) };
let vertex_allocation =
allocator.allocate(&gpu_allocator::vulkan::AllocationCreateDesc {
name: "Vertex Buffer",
requirements,
location: gpu_allocator::MemoryLocation::CpuToGpu,
linear: true,
allocation_scheme: gpu_allocator::vulkan::AllocationScheme::GpuAllocatorManaged,
})?;
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::<u8>(),
ptr.cast::<u8>(),
std::mem::size_of_val(&vertices),
);
}
Ok((vertex_buffer, vertex_allocation))
}

View file

@ -1,3 +1,5 @@
//! Vertex data structures and layout descriptions.
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
/// Represents a single vertex in 3D space with position and texture coordinates. /// Represents a single vertex in 3D space with position and texture coordinates.
@ -18,16 +20,18 @@ impl Vertex {
/// ///
/// This defines the 'stride' (distance between vertices) and specifies that /// This defines the 'stride' (distance between vertices) and specifies that
/// data is read per-vertex rather than per-instance. /// data is read per-vertex rather than per-instance.
#[allow(clippy::expect_used)]
pub fn get_binding_description() -> ash::vk::VertexInputBindingDescription { pub fn get_binding_description() -> ash::vk::VertexInputBindingDescription {
ash::vk::VertexInputBindingDescription::default() ash::vk::VertexInputBindingDescription::default()
.binding(0) .binding(0)
.stride(std::mem::size_of::<Self>() as u32) .stride(u32::try_from(std::mem::size_of::<Self>()).expect("Vertex size exceeds u32 range"))
.input_rate(ash::vk::VertexInputRate::VERTEX) .input_rate(ash::vk::VertexInputRate::VERTEX)
} }
/// Describes the layout of individual fields (attributes) within a single vertex. /// Describes the layout of individual fields (attributes) within a single vertex.
/// ///
/// These 'locations' must match the `layout(location = X)` qualifiers in the vertex shader. /// These 'locations' must match the `layout(location = X)` qualifiers in the vertex shader.
#[allow(clippy::expect_used)]
pub fn get_attribute_descriptions() -> [ash::vk::VertexInputAttributeDescription; 2] { pub fn get_attribute_descriptions() -> [ash::vk::VertexInputAttributeDescription; 2] {
[ [
// Location 0: position (vec3 -> R32G32B32_SFLOAT) // Location 0: position (vec3 -> R32G32B32_SFLOAT)
@ -41,7 +45,7 @@ impl Vertex {
.binding(0) .binding(0)
.location(1) .location(1)
.format(ash::vk::Format::R32G32_SFLOAT) .format(ash::vk::Format::R32G32_SFLOAT)
.offset(std::mem::size_of::<[f32; 3]>() as u32), .offset(u32::try_from(std::mem::size_of::<[f32; 3]>()).expect("Vertex offset exceeds u32 range")),
] ]
} }
} }

View file

@ -1,3 +1,6 @@
//! Graphics pipeline creation and shader management.
use crate::error::RendererError;
use crate::mesh::Vertex; use crate::mesh::Vertex;
use ash::{Device, vk}; use ash::{Device, vk};
use std::io::Cursor; use std::io::Cursor;
@ -6,41 +9,38 @@ use std::io::Cursor;
/// ///
/// Vulkan expects shader code to be 32-bit aligned; `ash::util::read_spv` is /// Vulkan expects shader code to be 32-bit aligned; `ash::util::read_spv` is
/// used to correctly interpret the raw bytes as a slice of `u32`. /// used to correctly interpret the raw bytes as a slice of `u32`.
pub fn create_shader_module(device: &Device, bytes: &[u8]) -> vk::ShaderModule { pub fn create_shader_module(
device: &Device,
bytes: &[u8],
) -> Result<vk::ShaderModule, RendererError> {
let mut cursor = Cursor::new(bytes); let mut cursor = Cursor::new(bytes);
let code = ash::util::read_spv(&mut cursor) let code = ash::util::read_spv(&mut cursor)?;
.expect("Failed to read SPIR-V binary; check if the file is valid");
let create_info = vk::ShaderModuleCreateInfo::default().code(&code); let create_info = vk::ShaderModuleCreateInfo::default().code(&code);
unsafe { let module = unsafe { device.create_shader_module(&create_info, None)? };
device Ok(module)
.create_shader_module(&create_info, None)
.expect("Failed to create Vulkan shader module")
}
} }
/// Defines the 'interface' of the pipeline (what data we can pass to the shaders). /// Defines the 'interface' of the pipeline (what data we can pass to the shaders).
/// ///
/// This layout defines any push constants or descriptor sets (textures/UBOs) /// This layout defines any push constants or descriptor sets (textures/UBOs)
/// accessed by the shaders during execution. /// accessed by the shaders during execution.
pub fn create_pipeline_layout(device: &Device) -> vk::PipelineLayout { pub fn create_pipeline_layout(device: &Device) -> Result<vk::PipelineLayout, RendererError> {
// A single push constant range is defined for the Model-View-Projection matrix. // A single push constant range is defined for the Model-View-Projection matrix.
// This allows the matrix to be updated for every draw call with high efficiency. // This allows the matrix to be updated for every draw call with high efficiency.
#[allow(clippy::expect_used)]
let push_constant_range = vk::PushConstantRange::default() let push_constant_range = vk::PushConstantRange::default()
.stage_flags(vk::ShaderStageFlags::VERTEX) .stage_flags(vk::ShaderStageFlags::VERTEX)
.offset(0) .offset(0)
.size(std::mem::size_of::<glam::Mat4>() as u32); .size(u32::try_from(std::mem::size_of::<glam::Mat4>()).expect("Mat4 size exceeds u32 range"));
let layout_create_info = vk::PipelineLayoutCreateInfo::default() let layout_create_info = vk::PipelineLayoutCreateInfo::default()
.push_constant_ranges(std::slice::from_ref(&push_constant_range)); .push_constant_ranges(std::slice::from_ref(&push_constant_range));
unsafe { let layout = unsafe { device.create_pipeline_layout(&layout_create_info, None)? };
device Ok(layout)
.create_pipeline_layout(&layout_create_info, None)
.expect("Failed to create pipeline layout")
}
} }
/// Creates a Graphics Pipeline for voxel rendering using Vulkan 1.3 Dynamic Rendering. /// Creates a Graphics Pipeline for voxel rendering using Vulkan 1.3 Dynamic Rendering.
@ -51,16 +51,10 @@ pub fn create_graphics_pipeline(
device: &Device, device: &Device,
layout: vk::PipelineLayout, layout: vk::PipelineLayout,
color_format: vk::Format, color_format: vk::Format,
) -> vk::Pipeline { ) -> Result<vk::Pipeline, RendererError> {
// 1. Load and compile shader modules // 1. Load and compile shader modules
// Using include_bytes! embeds the shaders directly into the engine binary. let (vert_module, frag_module) = load_shader_modules(device)?;
let vert_bytes = include_bytes!("../../../assets/shaders/cube.vert.spv"); let entry_point = std::ffi::CString::new("main").map_err(|_| RendererError::InvalidString)?;
let frag_bytes = include_bytes!("../../../assets/shaders/cube.frag.spv");
let vert_module = create_shader_module(device, vert_bytes);
let frag_module = create_shader_module(device, frag_bytes);
let entry_point = std::ffi::CString::new("main").unwrap();
let shader_stages = [ let shader_stages = [
vk::PipelineShaderStageCreateInfo::default() vk::PipelineShaderStageCreateInfo::default()
@ -73,60 +67,51 @@ pub fn create_graphics_pipeline(
.name(&entry_point), .name(&entry_point),
]; ];
// 2. Configure Vertex Input // 2. Configure Fixed-Function States
let binding_descriptions = [Vertex::get_binding_description()]; let binding_descriptions = [Vertex::get_binding_description()];
let attribute_descriptions = Vertex::get_attribute_descriptions(); let attribute_descriptions = Vertex::get_attribute_descriptions();
let vertex_input_info = vk::PipelineVertexInputStateCreateInfo::default() let vertex_input_info = vk::PipelineVertexInputStateCreateInfo::default()
.vertex_binding_descriptions(&binding_descriptions) .vertex_binding_descriptions(&binding_descriptions)
.vertex_attribute_descriptions(&attribute_descriptions); .vertex_attribute_descriptions(&attribute_descriptions);
// 3. Configure Input Assembly (Drawing mode)
let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default() let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default()
.topology(vk::PrimitiveTopology::TRIANGLE_LIST) .topology(vk::PrimitiveTopology::TRIANGLE_LIST)
.primitive_restart_enable(false); .primitive_restart_enable(false);
// 4. Viewport and Scissor (Static declarations, dynamic values set at runtime)
let viewport_state = vk::PipelineViewportStateCreateInfo::default() let viewport_state = vk::PipelineViewportStateCreateInfo::default()
.viewport_count(1) .viewport_count(1)
.scissor_count(1); .scissor_count(1);
// 5. Configure Rasterizer (Triangles to Pixels)
let rasterizer = vk::PipelineRasterizationStateCreateInfo::default() let rasterizer = vk::PipelineRasterizationStateCreateInfo::default()
.depth_clamp_enable(false) .depth_clamp_enable(false)
.rasterizer_discard_enable(false) .rasterizer_discard_enable(false)
.polygon_mode(vk::PolygonMode::FILL) .polygon_mode(vk::PolygonMode::FILL)
.line_width(1.0) .line_width(1.0)
.cull_mode(vk::CullModeFlags::BACK) // Back-face culling for performance .cull_mode(vk::CullModeFlags::BACK)
.front_face(vk::FrontFace::COUNTER_CLOCKWISE) .front_face(vk::FrontFace::COUNTER_CLOCKWISE)
.depth_bias_enable(false); .depth_bias_enable(false);
// 6. Configure Multisampling (Anti-aliasing)
let multisampling = vk::PipelineMultisampleStateCreateInfo::default() let multisampling = vk::PipelineMultisampleStateCreateInfo::default()
.sample_shading_enable(false) .sample_shading_enable(false)
.rasterization_samples(vk::SampleCountFlags::TYPE_1); .rasterization_samples(vk::SampleCountFlags::TYPE_1);
// 7. Configure Color Blending
let color_blend_attachment = vk::PipelineColorBlendAttachmentState::default() let color_blend_attachment = vk::PipelineColorBlendAttachmentState::default()
.color_write_mask(vk::ColorComponentFlags::RGBA) .color_write_mask(vk::ColorComponentFlags::RGBA)
.blend_enable(false); // Transparency is not required for the initial implementation .blend_enable(false);
let color_blending = vk::PipelineColorBlendStateCreateInfo::default() let color_blending = vk::PipelineColorBlendStateCreateInfo::default()
.logic_op_enable(false) .logic_op_enable(false)
.attachments(std::slice::from_ref(&color_blend_attachment)); .attachments(std::slice::from_ref(&color_blend_attachment));
// 8. Define Dynamic States
// This allows the window to be resized without recreating the entire pipeline.
let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR];
let dynamic_state_info = let dynamic_state_info =
vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states); vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
// 9. Configure Dynamic Rendering (Vulkan 1.3)
let color_formats = [color_format]; let color_formats = [color_format];
let mut rendering_info = let mut rendering_info =
vk::PipelineRenderingCreateInfo::default().color_attachment_formats(&color_formats); vk::PipelineRenderingCreateInfo::default().color_attachment_formats(&color_formats);
// 10. Finalize Pipeline Creation // 3. Finalize Pipeline Creation
let pipeline_info = vk::GraphicsPipelineCreateInfo::default() let pipeline_info = vk::GraphicsPipelineCreateInfo::default()
.push_next(&mut rendering_info) .push_next(&mut rendering_info)
.stages(&shader_stages) .stages(&shader_stages)
@ -142,8 +127,8 @@ pub fn create_graphics_pipeline(
let pipeline = unsafe { let pipeline = unsafe {
device device
.create_graphics_pipelines(vk::PipelineCache::null(), &[pipeline_info], None) .create_graphics_pipelines(vk::PipelineCache::null(), &[pipeline_info], None)
.expect("Failed to create graphics pipeline")[0] .map_err(|(_, e)| e)?
}; }[0];
// Cleanup temporary shader modules (they are baked into the pipeline now) // Cleanup temporary shader modules (they are baked into the pipeline now)
unsafe { unsafe {
@ -151,5 +136,16 @@ pub fn create_graphics_pipeline(
device.destroy_shader_module(frag_module, None); device.destroy_shader_module(frag_module, None);
} }
pipeline Ok(pipeline)
}
/// Loads the vertex and fragment shader modules from embedded bytes.
fn load_shader_modules(device: &Device) -> Result<(vk::ShaderModule, vk::ShaderModule), RendererError> {
let vert_bytes = include_bytes!("../../../assets/shaders/cube.vert.spv");
let frag_bytes = include_bytes!("../../../assets/shaders/cube.frag.spv");
let vert_module = create_shader_module(device, vert_bytes)?;
let frag_module = create_shader_module(device, frag_bytes)?;
Ok((vert_module, frag_module))
} }

View file

@ -14,12 +14,14 @@ pub struct Renderer {
/// The debug messenger for validation layer output. /// The debug messenger for validation layer output.
pub(crate) debug_messenger: vk::DebugUtilsMessengerEXT, pub(crate) debug_messenger: vk::DebugUtilsMessengerEXT,
/// Handle to the selected physical device (GPU). /// Handle to the selected physical device (GPU).
#[allow(dead_code)]
pub(crate) physical_device: vk::PhysicalDevice, pub(crate) physical_device: vk::PhysicalDevice,
/// The logical Vulkan device. /// The logical Vulkan device.
pub(crate) device: Device, pub(crate) device: Device,
/// The queue used for graphics operations. /// The queue used for graphics operations.
pub(crate) graphics_queue: vk::Queue, pub(crate) graphics_queue: vk::Queue,
/// Index of the graphics queue family. /// Index of the graphics queue family.
#[allow(dead_code)]
pub(crate) graphics_queue_index: u32, pub(crate) graphics_queue_index: u32,
/// Surface extension loader. /// Surface extension loader.
pub(crate) surface_loader: khr::surface::Instance, pub(crate) surface_loader: khr::surface::Instance,
@ -32,6 +34,7 @@ pub struct Renderer {
/// Images acquired from the swapchain. /// Images acquired from the swapchain.
pub(crate) swapchain_images: Vec<vk::Image>, pub(crate) swapchain_images: Vec<vk::Image>,
/// The pixel format of the swapchain images. /// The pixel format of the swapchain images.
#[allow(dead_code)]
pub(crate) swapchain_format: vk::Format, pub(crate) swapchain_format: vk::Format,
/// The dimensions of the swapchain images. /// The dimensions of the swapchain images.
pub(crate) swapchain_extent: vk::Extent2D, pub(crate) swapchain_extent: vk::Extent2D,
@ -53,7 +56,7 @@ pub struct Renderer {
pub(crate) vertex_allocation: Allocation, pub(crate) vertex_allocation: Allocation,
/// Synchronization primitives for frame-by-frame execution. /// Synchronization primitives for frame-by-frame execution.
pub(crate) sync: SyncPrimitives, pub(crate) sync: SyncPrimitives,
/// Index of the current frame being processed (0 to crate::MAX_FRAMES_IN_FLIGHT - 1). /// Index of the current frame being processed (0 to `crate::MAX_FRAMES_IN_FLIGHT` - 1).
pub(crate) current_frame: usize, pub(crate) current_frame: usize,
} }
@ -204,16 +207,19 @@ impl Drop for Renderer {
self.device self.device
.destroy_pipeline_layout(self.pipeline_layout, None); .destroy_pipeline_layout(self.pipeline_layout, None);
self.allocator if let Err(e) = self
.free(std::ptr::read(&self.vertex_allocation)) .allocator
.expect(" Failed to free vertex buffer allocation"); .free(std::ptr::read(&raw const self.vertex_allocation))
{
tracing::error!("Failed to free vertex buffer allocation: {e}");
}
self.device.destroy_buffer(self.vertex_buffer, None); self.device.destroy_buffer(self.vertex_buffer, None);
self.device.destroy_command_pool(self.command_pool, None); self.device.destroy_command_pool(self.command_pool, None);
// Use the safe cleanup function from sync module // Use the safe cleanup function from sync module
let sync = std::ptr::read(&self.sync); let sync = std::ptr::read(&raw const self.sync);
crate::sync::destroy_sync_primitives(&self.device, sync); crate::sync::destroy_sync_primitives(&self.device, sync);
// Destroy the swapchain // Destroy the swapchain

View file

@ -39,9 +39,7 @@ pub fn create_swapchain(
let present_mode = vk::PresentModeKHR::FIFO; let present_mode = vk::PresentModeKHR::FIFO;
let extent = if surface_capabilities.current_extent.width != u32::MAX { let extent = if surface_capabilities.current_extent.width == u32::MAX {
surface_capabilities.current_extent
} else {
vk::Extent2D { vk::Extent2D {
width: width.clamp( width: width.clamp(
surface_capabilities.min_image_extent.width, surface_capabilities.min_image_extent.width,
@ -52,6 +50,8 @@ pub fn create_swapchain(
surface_capabilities.max_image_extent.height, surface_capabilities.max_image_extent.height,
), ),
} }
} else {
surface_capabilities.current_extent
}; };
let image_count = if surface_capabilities.max_image_count > 0 let image_count = if surface_capabilities.max_image_count > 0

View file

@ -1,3 +1,11 @@
//! Lua scripting and modding support for Project Catalyst.
//!
//! This crate handles the integration with Lua (via `mlua`) and provides
//! the API surface for both base game content and third-party mods.
/// Adds two numbers together.
///
/// This is a placeholder function for the initial crate setup.
pub fn add(left: u64, right: u64) -> u64 { pub fn add(left: u64, right: u64) -> u64 {
left + right left + right
} }

View file

@ -1,3 +1,8 @@
//! Dedicated server for Project Catalyst.
//!
//! The server handles the authoritative game simulation, including world
//! management, physics, and combat.
fn main() { fn main() {
println!("Hello, world!"); println!("Hello, world!");
} }

View file

@ -1,3 +1,11 @@
//! Shared types and logic for Project Catalyst.
//!
//! This crate contains data structures and constants that are used by both
//! the client and the server.
/// Adds two numbers together.
///
/// This is a placeholder function for the initial crate setup.
pub fn add(left: u64, right: u64) -> u64 { pub fn add(left: u64, right: u64) -> u64 {
left + right left + right
} }