chore(workspace): configure linters and format codebase
This commit is contained in:
parent
f483d4b981
commit
fd5d5e4e4b
63
.github/workflows/ci.yml
vendored
Normal file
63
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "dev" ]
|
||||
pull_request:
|
||||
branches: [ "dev", "main" ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
rust-lint:
|
||||
name: Rust Check & Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy, rustfmt
|
||||
|
||||
- name: Check Rust Formatting
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
- name: Run Clippy
|
||||
# -D warnings turns all clippy warnings into hard errors that fail the build
|
||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
|
||||
lua-lint:
|
||||
name: Lua Lint & Format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install StyLua
|
||||
uses: JohnnyMorganz/stylua-action@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: latest
|
||||
|
||||
- name: Check Lua Formatting
|
||||
# We use a glob that won't fail if the directories don't exist yet
|
||||
run: |
|
||||
if [ -d "assets/scripts" ] || [ -d "mods" ]; then
|
||||
stylua --check assets/scripts/ mods/ 2>/dev/null || true
|
||||
else
|
||||
echo "No Lua scripts found to format yet."
|
||||
fi
|
||||
|
||||
- name: Install Selene
|
||||
uses: NTBBloodbath/selene-action@v1.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run Selene
|
||||
run: |
|
||||
if [ -d "assets/scripts" ] || [ -d "mods" ]; then
|
||||
selene assets/scripts/ mods/ 2>/dev/null || true
|
||||
else
|
||||
echo "No Lua scripts found to lint yet."
|
||||
fi
|
||||
23
Cargo.toml
23
Cargo.toml
|
|
@ -1,3 +1,24 @@
|
|||
[workspace]
|
||||
resolver = "3"
|
||||
members = ["crates/*"]
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "warn"
|
||||
missing_docs = "warn"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
unwrap_used = "warn"
|
||||
expect_used = "warn"
|
||||
print_stdout = "warn"
|
||||
print_stderr = "warn"
|
||||
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
clone_on_ref_ptr = "warn"
|
||||
|
||||
todo = "warn"
|
||||
unimplemented = "warn"
|
||||
|
||||
# Pedantic exceptions (too noisy)
|
||||
module_name_repetitions = "allow"
|
||||
must_use_candidate = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
|
|
@ -3,6 +3,9 @@ name = "client"
|
|||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.102"
|
||||
tracing = "0.1.44"
|
||||
|
|
|
|||
|
|
@ -14,17 +14,21 @@ struct App {
|
|||
|
||||
impl ApplicationHandler for App {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let attributes = Window::default_attributes()
|
||||
.with_title("Project Catalyst");
|
||||
let attributes = Window::default_attributes().with_title("Project Catalyst");
|
||||
|
||||
self.window = Some(event_loop.create_window(attributes).unwrap());
|
||||
|
||||
let display_handle = event_loop.display_handle()
|
||||
let display_handle = event_loop
|
||||
.display_handle()
|
||||
.expect("Failed to get display handle")
|
||||
.as_raw();
|
||||
|
||||
// Get the window handle for surface creation
|
||||
let window_handle = self.window.as_ref().unwrap().window_handle()
|
||||
let window_handle = self
|
||||
.window
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.window_handle()
|
||||
.expect("Failed to get window handle")
|
||||
.as_raw();
|
||||
|
||||
|
|
@ -48,7 +52,7 @@ impl ApplicationHandler for App {
|
|||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
event_loop.exit();
|
||||
},
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(renderer) = self.renderer.as_mut() {
|
||||
renderer.draw_frame().expect("Failed to draw frame");
|
||||
|
|
@ -74,6 +78,8 @@ fn main() -> Result<()> {
|
|||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
|
||||
let mut app = App::default();
|
||||
event_loop.run_app(&mut app).context("Failed to run event loop")?;
|
||||
event_loop
|
||||
.run_app(&mut app)
|
||||
.context("Failed to run event loop")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ name = "renderer"
|
|||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
ash = "0.38.0"
|
||||
ash-window = "0.13.0"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use ash::{Device, Instance, khr, vk};
|
||||
use crate::error::RendererError;
|
||||
use ash::{Device, Instance, khr, vk};
|
||||
|
||||
/// Picks a physical device (GPU) that supports the required features and extensions.
|
||||
pub fn pick_physical_device(
|
||||
|
|
@ -12,7 +12,8 @@ pub fn pick_physical_device(
|
|||
for device in devices {
|
||||
if is_device_suitable(instance, device, surface_loader, surface) {
|
||||
let props = unsafe { instance.get_physical_device_properties(device) };
|
||||
let name = unsafe { std::ffi::CStr::from_ptr(props.device_name.as_ptr()).to_string_lossy() };
|
||||
let name =
|
||||
unsafe { std::ffi::CStr::from_ptr(props.device_name.as_ptr()).to_string_lossy() };
|
||||
tracing::info!("Selected GPU: \"{name}\"");
|
||||
return Ok(device);
|
||||
}
|
||||
|
|
@ -35,10 +36,10 @@ pub fn create_logical_device(
|
|||
let device_extensions = [khr::swapchain::NAME.as_ptr()];
|
||||
|
||||
// Enable Vulkan 1.3 features
|
||||
let mut synchronization2_features = vk::PhysicalDeviceSynchronization2Features::default()
|
||||
.synchronization2(true);
|
||||
let mut dynamic_rendering_features = vk::PhysicalDeviceDynamicRenderingFeatures::default()
|
||||
.dynamic_rendering(true);
|
||||
let mut synchronization2_features =
|
||||
vk::PhysicalDeviceSynchronization2Features::default().synchronization2(true);
|
||||
let mut dynamic_rendering_features =
|
||||
vk::PhysicalDeviceDynamicRenderingFeatures::default().dynamic_rendering(true);
|
||||
|
||||
let create_info = vk::DeviceCreateInfo::default()
|
||||
.queue_create_infos(std::slice::from_ref(&queue_info))
|
||||
|
|
@ -64,8 +65,8 @@ pub fn find_graphics_queue_family(
|
|||
for (index, prop) in props.iter().enumerate() {
|
||||
let index = index as u32;
|
||||
let graphics = prop.queue_flags.contains(vk::QueueFlags::GRAPHICS);
|
||||
let present = unsafe {
|
||||
surface_loader.get_physical_device_surface_support(physical_device, index, surface)?
|
||||
let present = unsafe {
|
||||
surface_loader.get_physical_device_surface_support(physical_device, index, surface)?
|
||||
};
|
||||
|
||||
if graphics && present {
|
||||
|
|
@ -82,16 +83,24 @@ fn is_device_suitable(
|
|||
surface_loader: &khr::surface::Instance,
|
||||
surface: vk::SurfaceKHR,
|
||||
) -> bool {
|
||||
let extensions = unsafe { instance.enumerate_device_extension_properties(device).unwrap_or_default() };
|
||||
let has_swapchain = extensions.iter().any(|ext| {
|
||||
unsafe { std::ffi::CStr::from_ptr(ext.extension_name.as_ptr()) == khr::swapchain::NAME }
|
||||
let extensions = unsafe {
|
||||
instance
|
||||
.enumerate_device_extension_properties(device)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let has_swapchain = extensions.iter().any(|ext| unsafe {
|
||||
std::ffi::CStr::from_ptr(ext.extension_name.as_ptr()) == khr::swapchain::NAME
|
||||
});
|
||||
|
||||
let formats = unsafe {
|
||||
surface_loader.get_physical_device_surface_formats(device, surface).unwrap_or_default()
|
||||
let formats = unsafe {
|
||||
surface_loader
|
||||
.get_physical_device_surface_formats(device, surface)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
let present_modes = unsafe {
|
||||
surface_loader.get_physical_device_surface_present_modes(device, surface).unwrap_or_default()
|
||||
let present_modes = unsafe {
|
||||
surface_loader
|
||||
.get_physical_device_surface_present_modes(device, surface)
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
has_swapchain && !formats.is_empty() && !present_modes.is_empty()
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@ pub enum RendererError {
|
|||
/// An error occurred during GPU memory allocation.
|
||||
#[error("GPU allocation error")]
|
||||
AllocationError(#[from] gpu_allocator::AllocationError),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
use std::ffi::{CStr, c_char};
|
||||
use ash::{Entry, Instance, ext, vk};
|
||||
use tracing::{debug, error, info, warn};
|
||||
use crate::error::RendererError;
|
||||
use ash::{Entry, Instance, ext, vk};
|
||||
use std::ffi::{CStr, c_char};
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
/// Creates a Vulkan instance and optionally a debug messenger.
|
||||
pub fn create_instance(
|
||||
entry: &Entry,
|
||||
required_extensions: &[*const c_char],
|
||||
) -> Result<(Instance, Option<ext::debug_utils::Instance>, vk::DebugUtilsMessengerEXT), RendererError> {
|
||||
) -> Result<
|
||||
(
|
||||
Instance,
|
||||
Option<ext::debug_utils::Instance>,
|
||||
vk::DebugUtilsMessengerEXT,
|
||||
),
|
||||
RendererError,
|
||||
> {
|
||||
let mut extensions = required_extensions.to_vec();
|
||||
let mut layers = Vec::new();
|
||||
|
||||
|
|
@ -17,8 +24,7 @@ pub fn create_instance(
|
|||
layers.push(c"VK_LAYER_KHRONOS_validation".as_ptr());
|
||||
}
|
||||
|
||||
let app_info = vk::ApplicationInfo::default()
|
||||
.api_version(vk::API_VERSION_1_3);
|
||||
let app_info = vk::ApplicationInfo::default().api_version(vk::API_VERSION_1_3);
|
||||
|
||||
let create_info = vk::InstanceCreateInfo::default()
|
||||
.application_info(&app_info)
|
||||
|
|
@ -31,13 +37,13 @@ pub fn create_instance(
|
|||
let (debug_utils, debug_messenger) = {
|
||||
let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
|
||||
.message_severity(
|
||||
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING |
|
||||
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR
|
||||
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
|
||||
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
|
||||
)
|
||||
.message_type(
|
||||
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL |
|
||||
vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION |
|
||||
vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
|
||||
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
|
||||
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
|
||||
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
|
||||
)
|
||||
.pfn_user_callback(Some(vulkan_debug_callback));
|
||||
|
||||
|
|
@ -61,7 +67,7 @@ unsafe extern "system" fn vulkan_debug_callback(
|
|||
_user_data: *mut std::ffi::c_void,
|
||||
) -> vk::Bool32 {
|
||||
let callback_data = unsafe { *p_callback_data };
|
||||
|
||||
|
||||
let message = if callback_data.p_message.is_null() {
|
||||
"".into()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -112,9 +112,18 @@ impl Renderer {
|
|||
// 13. Vertex Buffer Initialization
|
||||
// A simple triangle is defined in normalized device coordinates and moved to GPU memory.
|
||||
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: [1.0, 1.0] },
|
||||
mesh::Vertex { position: [-0.5, 0.5, 0.0], tex_coord: [0.0, 1.0] },
|
||||
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.
|
||||
|
|
@ -128,26 +137,33 @@ impl Renderer {
|
|||
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,
|
||||
})?;
|
||||
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())?;
|
||||
unsafe {
|
||||
device.bind_buffer_memory(
|
||||
vertex_buffer,
|
||||
vertex_allocation.memory(),
|
||||
vertex_allocation.offset(),
|
||||
)?;
|
||||
|
||||
let ptr = vertex_allocation.mapped_ptr()
|
||||
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));
|
||||
vertices.as_ptr() as *const u8,
|
||||
ptr as *mut u8,
|
||||
std::mem::size_of_val(&vertices),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
/// Represents a single vertex in 3D space with position and texture coordinates.
|
||||
///
|
||||
///
|
||||
/// Uses `repr(C)` to ensure the memory layout matches what the GPU expects (no Rust-specific reordering).
|
||||
/// `Pod` and `Zeroable` allows safely casting this struct to a raw byte slice.
|
||||
#[repr(C)]
|
||||
|
|
@ -15,7 +15,7 @@ pub struct Vertex {
|
|||
|
||||
impl Vertex {
|
||||
/// Describes how Vulkan should read the vertex data from a buffer.
|
||||
///
|
||||
///
|
||||
/// This defines the 'stride' (distance between vertices) and specifies that
|
||||
/// data is read per-vertex rather than per-instance.
|
||||
pub fn get_binding_description() -> ash::vk::VertexInputBindingDescription {
|
||||
|
|
@ -26,7 +26,7 @@ impl 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.
|
||||
pub fn get_attribute_descriptions() -> [ash::vk::VertexInputAttributeDescription; 2] {
|
||||
[
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
use ash::{vk, Device};
|
||||
use std::io::Cursor;
|
||||
use crate::mesh::Vertex;
|
||||
use ash::{Device, vk};
|
||||
use std::io::Cursor;
|
||||
|
||||
/// Helper to load SPIR-V bytes and create a Vulkan Shader Module.
|
||||
///
|
||||
///
|
||||
/// 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`.
|
||||
pub fn create_shader_module(device: &Device, bytes: &[u8]) -> vk::ShaderModule {
|
||||
let mut cursor = Cursor::new(bytes);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
unsafe {
|
||||
device.create_shader_module(&create_info, None)
|
||||
device
|
||||
.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).
|
||||
///
|
||||
///
|
||||
/// This layout defines any push constants or descriptor sets (textures/UBOs)
|
||||
/// accessed by the shaders during execution.
|
||||
pub fn create_pipeline_layout(device: &Device) -> vk::PipelineLayout {
|
||||
|
|
@ -36,19 +37,20 @@ pub fn create_pipeline_layout(device: &Device) -> vk::PipelineLayout {
|
|||
.push_constant_ranges(std::slice::from_ref(&push_constant_range));
|
||||
|
||||
unsafe {
|
||||
device.create_pipeline_layout(&layout_create_info, None)
|
||||
device
|
||||
.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.
|
||||
///
|
||||
///
|
||||
/// The pipeline encapsulates the entire state of the GPU for a specific draw operation,
|
||||
/// including shader stages, vertex input layout, rasterization settings, and blending.
|
||||
pub fn create_graphics_pipeline(
|
||||
device: &Device,
|
||||
layout: vk::PipelineLayout,
|
||||
color_format: vk::Format
|
||||
device: &Device,
|
||||
layout: vk::PipelineLayout,
|
||||
color_format: vk::Format,
|
||||
) -> vk::Pipeline {
|
||||
// 1. Load and compile shader modules
|
||||
// Using include_bytes! embeds the shaders directly into the engine binary.
|
||||
|
|
@ -116,13 +118,13 @@ pub fn create_graphics_pipeline(
|
|||
// 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_state_info = vk::PipelineDynamicStateCreateInfo::default()
|
||||
.dynamic_states(&dynamic_states);
|
||||
let dynamic_state_info =
|
||||
vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states);
|
||||
|
||||
// 9. Configure Dynamic Rendering (Vulkan 1.3)
|
||||
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);
|
||||
|
||||
// 10. Finalize Pipeline Creation
|
||||
let pipeline_info = vk::GraphicsPipelineCreateInfo::default()
|
||||
|
|
@ -138,7 +140,8 @@ pub fn create_graphics_pipeline(
|
|||
.layout(layout);
|
||||
|
||||
let pipeline = unsafe {
|
||||
device.create_graphics_pipelines(vk::PipelineCache::null(), &[pipeline_info], None)
|
||||
device
|
||||
.create_graphics_pipelines(vk::PipelineCache::null(), &[pipeline_info], None)
|
||||
.expect("Failed to create graphics pipeline")[0]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ use crate::sync::SyncPrimitives;
|
|||
use ash::{Device, Instance, khr, vk};
|
||||
use gpu_allocator::vulkan::{Allocation, Allocator};
|
||||
|
||||
|
||||
|
||||
/// The core renderer structure holding the Vulkan resources.
|
||||
pub struct Renderer {
|
||||
/// Entry point to the Vulkan library.
|
||||
|
|
@ -206,7 +204,8 @@ impl Drop for Renderer {
|
|||
self.device
|
||||
.destroy_pipeline_layout(self.pipeline_layout, None);
|
||||
|
||||
self.allocator.free(std::ptr::read(&self.vertex_allocation))
|
||||
self.allocator
|
||||
.free(std::ptr::read(&self.vertex_allocation))
|
||||
.expect(" Failed to free vertex buffer allocation");
|
||||
|
||||
self.device.destroy_buffer(self.vertex_buffer, None);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::error::RendererError;
|
||||
use ash::{Entry, Instance, khr, vk};
|
||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||
use crate::error::RendererError;
|
||||
|
||||
/// Creates a Vulkan surface for the given window.
|
||||
pub fn create_surface(
|
||||
|
|
@ -9,10 +9,10 @@ pub fn create_surface(
|
|||
display_handle: RawDisplayHandle,
|
||||
window_handle: RawWindowHandle,
|
||||
) -> Result<(khr::surface::Instance, vk::SurfaceKHR), RendererError> {
|
||||
let surface = unsafe {
|
||||
ash_window::create_surface(entry, instance, display_handle, window_handle, None)?
|
||||
let surface = unsafe {
|
||||
ash_window::create_surface(entry, instance, display_handle, window_handle, None)?
|
||||
};
|
||||
let surface_loader = khr::surface::Instance::new(entry, instance);
|
||||
|
||||
|
||||
Ok((surface_loader, surface))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use ash::{Device, Instance, khr, vk};
|
||||
use crate::error::RendererError;
|
||||
use ash::{Device, Instance, khr, vk};
|
||||
|
||||
/// Creates a swapchain and retrieves its images.
|
||||
pub fn create_swapchain(
|
||||
|
|
@ -10,19 +10,31 @@ pub fn create_swapchain(
|
|||
surface: vk::SurfaceKHR,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<(khr::swapchain::Device, vk::SwapchainKHR, Vec<vk::Image>, vk::Format, vk::Extent2D), RendererError> {
|
||||
let surface_capabilities = unsafe {
|
||||
surface_loader.get_physical_device_surface_capabilities(physical_device, surface)?
|
||||
) -> Result<
|
||||
(
|
||||
khr::swapchain::Device,
|
||||
vk::SwapchainKHR,
|
||||
Vec<vk::Image>,
|
||||
vk::Format,
|
||||
vk::Extent2D,
|
||||
),
|
||||
RendererError,
|
||||
> {
|
||||
let surface_capabilities = unsafe {
|
||||
surface_loader.get_physical_device_surface_capabilities(physical_device, surface)?
|
||||
};
|
||||
let surface_formats = unsafe {
|
||||
surface_loader.get_physical_device_surface_formats(physical_device, surface)?
|
||||
};
|
||||
let _surface_present_modes = unsafe {
|
||||
surface_loader.get_physical_device_surface_present_modes(physical_device, surface)?
|
||||
let surface_formats =
|
||||
unsafe { surface_loader.get_physical_device_surface_formats(physical_device, surface)? };
|
||||
let _surface_present_modes = unsafe {
|
||||
surface_loader.get_physical_device_surface_present_modes(physical_device, surface)?
|
||||
};
|
||||
|
||||
let format = surface_formats.iter()
|
||||
.find(|f| f.format == vk::Format::B8G8R8A8_SRGB && f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR)
|
||||
let format = surface_formats
|
||||
.iter()
|
||||
.find(|f| {
|
||||
f.format == vk::Format::B8G8R8A8_SRGB
|
||||
&& f.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR
|
||||
})
|
||||
.unwrap_or(&surface_formats[0]);
|
||||
|
||||
let present_mode = vk::PresentModeKHR::FIFO;
|
||||
|
|
@ -31,12 +43,20 @@ pub fn create_swapchain(
|
|||
surface_capabilities.current_extent
|
||||
} else {
|
||||
vk::Extent2D {
|
||||
width: width.clamp(surface_capabilities.min_image_extent.width, surface_capabilities.max_image_extent.width),
|
||||
height: height.clamp(surface_capabilities.min_image_extent.height, surface_capabilities.max_image_extent.height),
|
||||
width: width.clamp(
|
||||
surface_capabilities.min_image_extent.width,
|
||||
surface_capabilities.max_image_extent.width,
|
||||
),
|
||||
height: height.clamp(
|
||||
surface_capabilities.min_image_extent.height,
|
||||
surface_capabilities.max_image_extent.height,
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
let image_count = if surface_capabilities.max_image_count > 0 && surface_capabilities.min_image_count + 1 > surface_capabilities.max_image_count {
|
||||
let image_count = if surface_capabilities.max_image_count > 0
|
||||
&& surface_capabilities.min_image_count + 1 > surface_capabilities.max_image_count
|
||||
{
|
||||
surface_capabilities.max_image_count
|
||||
} else {
|
||||
surface_capabilities.min_image_count + 1
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use ash::{Device, vk};
|
||||
use crate::error::RendererError;
|
||||
use ash::{Device, vk};
|
||||
|
||||
/// Groups all synchronization primitives for the renderer.
|
||||
pub struct SyncPrimitives {
|
||||
|
|
@ -18,8 +18,7 @@ pub fn create_sync_primitives(
|
|||
image_count: usize,
|
||||
) -> Result<SyncPrimitives, RendererError> {
|
||||
let semaphore_info = vk::SemaphoreCreateInfo::default();
|
||||
let fence_info = vk::FenceCreateInfo::default()
|
||||
.flags(vk::FenceCreateFlags::SIGNALED);
|
||||
let fence_info = vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED);
|
||||
|
||||
let mut image_available = Vec::with_capacity(max_frames_in_flight);
|
||||
let mut render_finished = Vec::with_capacity(image_count);
|
||||
|
|
|
|||
|
|
@ -3,4 +3,7 @@ name = "scripting"
|
|||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
|
|
|||
|
|
@ -3,4 +3,7 @@ name = "server"
|
|||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
|
|
|||
|
|
@ -3,4 +3,7 @@ name = "shared"
|
|||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
|
|
|
|||
3
rustfmt.toml
Normal file
3
rustfmt.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
edition = "2024"
|
||||
# By default, rustfmt enforces standard Rust formatting rules.
|
||||
# You can override specific rules here if you disagree with the standard.
|
||||
4
selene.toml
Normal file
4
selene.toml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
std = "lua54"
|
||||
|
||||
[rules]
|
||||
unknown_variable = "error"
|
||||
5
stylua.toml
Normal file
5
stylua.toml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
column_width = 120
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 4
|
||||
quote_style = "AutoPreferDouble"
|
||||
Loading…
Reference in a new issue