diff --git a/Cargo.toml b/Cargo.toml index a232693..2681be5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ "crates/window", "crates/game", "crates/text", - "crates/bevy_vulkan_render", "crates/renderer_device", + "crates/bevy_vulkan_render", "crates/renderer_device", "crates/renderer_resources", ] [dependencies] @@ -57,6 +57,7 @@ ahash = "0.8" md-5 = "0.11.0" parking_lot = "0.12.3" +bytemuck = { version = "1.21.0", features = ["derive"] } # tokio = "1.42" # futures = "0.3" diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml index f9f80b4..e74de5e 100644 --- a/crates/renderer/Cargo.toml +++ b/crates/renderer/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] renderer_device = { path = "../renderer_device" } +renderer_resources = { path = "../renderer_resources" } renderer_macros = { path = "renderer_macros" } arrayvec = { workspace = true } indexmap = { workspace = true } diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index d67a002..9d2c7ba 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -11,18 +11,17 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; pub use ash; -mod buffers; pub mod commands; #[path = "egui.rs"] mod egui_pass; -mod images; -mod pipeline; pub mod render_graph; pub mod rendering; pub mod swapchain; mod text; pub mod util; +pub(crate) use renderer_resources::{buffers, images, pipeline}; + pub use renderer_device::{device, instance, queue, sync}; pub use renderer_device::{ compatible_extension_properties, extension_intersection, diff --git a/crates/renderer/src/rendering/mod.rs b/crates/renderer/src/rendering/mod.rs index d88d573..11baddb 100644 --- a/crates/renderer/src/rendering/mod.rs +++ b/crates/renderer/src/rendering/mod.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use ash::vk; use glam::{ camera::rh::{proj, view}, - f32::Mat4, vec3, }; use gpu_allocator::MemoryLocation; @@ -165,11 +164,7 @@ impl Wireframe { ); } - let future = cmd.submit_async( - None, - None, - Arc::new(sync::Fence::from_pool(&dev, None)?), - )?; + let future = cmd.submit_async(None, None, Arc::new(sync::Fence::from_pool(&dev, None)?))?; let pipeline = Self::create_pipeline(dev.clone())?; diff --git a/crates/renderer/src/swapchain.rs b/crates/renderer/src/swapchain.rs index bd93529..5275af7 100644 --- a/crates/renderer/src/swapchain.rs +++ b/crates/renderer/src/swapchain.rs @@ -562,7 +562,17 @@ impl Swapchain { let idx = idx as usize; let image = self.images[idx]; - let image = Arc::new(images::Image::from_swapchain_image(image, &self)); + let image = Arc::new(images::Image::from_swapchain_raw( + image, + self.swapchain.device().clone(), + self.config.format, + vk::Extent3D { + width: self.config.extent.width, + height: self.config.extent.height, + depth: 1, + }, + self.config.usage, + )); let view = image.create_view(ImageViewDesc { name: Some(format!("swapchain-{:x}-image-view-{idx}", self.raw().as_raw()).into()), kind: vk::ImageViewType::TYPE_2D, diff --git a/crates/renderer/src/util.rs b/crates/renderer/src/util.rs index d1433e9..7c8b7ae 100644 --- a/crates/renderer/src/util.rs +++ b/crates/renderer/src/util.rs @@ -6,75 +6,7 @@ use std::{ use ash::vk; use bytemuck::{Pod, Zeroable}; -pub(crate) mod weak_vec { - #![allow(dead_code)] - //! Module containing the [`WeakVec`] API. - - use std::{sync::Weak, vec::Vec}; - - /// An optimized container for `Weak` references of `T` that minimizes reallocations by - /// dropping older elements that no longer have strong references to them. - #[derive(Debug)] - pub(crate) struct WeakVec { - inner: Vec>, - } - - impl Default for WeakVec { - fn default() -> Self { - Self { - inner: Default::default(), - } - } - } - - impl WeakVec { - pub(crate) fn new() -> Self { - Self { inner: Vec::new() } - } - - /// Pushes a new element to this collection. - /// - /// If the inner Vec needs to be reallocated, we will first drop older elements that - /// no longer have strong references to them. - pub(crate) fn push(&mut self, value: Weak) { - if self.inner.len() == self.inner.capacity() { - // Iterating backwards has the advantage that we don't do more work than we have to. - for i in (0..self.inner.len()).rev() { - if self.inner[i].strong_count() == 0 { - self.inner.swap_remove(i); - } - } - - // Make sure our capacity is twice the number of live elements. - // Leaving some spare capacity ensures that we won't re-scan immediately. - self.inner.reserve_exact(self.inner.len()); - } - - self.inner.push(value); - } - } - - pub(crate) struct WeakVecIter { - inner: std::vec::IntoIter>, - } - - impl Iterator for WeakVecIter { - type Item = Weak; - fn next(&mut self) -> Option { - self.inner.next() - } - } - - impl IntoIterator for WeakVec { - type Item = Weak; - type IntoIter = WeakVecIter; - fn into_iter(self) -> Self::IntoIter { - WeakVecIter { - inner: self.inner.into_iter(), - } - } - } -} +pub use renderer_resources::util::{F32, eq_f32, hash_f32, ord_f32}; #[derive(derive_more::Debug)] pub(crate) struct DropGuard(#[debug(skip)] ManuallyDrop>); @@ -419,104 +351,6 @@ impl Rect2D { } } -pub fn eq_f32(lhs: f32, rhs: f32) -> bool { - use core::num::FpCategory::*; - match (lhs.classify(), rhs.classify()) { - (Zero, Zero) | (Nan, Nan) => true, - (Infinite, Infinite) => lhs.signum() == rhs.signum(), - _ => lhs == rhs, - } -} -pub fn ord_f32(lhs: f32, rhs: f32) -> core::cmp::Ordering { - use core::cmp::Ordering; - use core::num::FpCategory::*; - match (lhs.classify(), rhs.classify()) { - (Zero, Zero) | (Nan, Nan) => Ordering::Equal, - (Infinite, Infinite) => { - core::cmp::PartialOrd::partial_cmp(&lhs.signum(), &rhs.signum()).unwrap() - } - _ => core::cmp::PartialOrd::partial_cmp(&lhs, &rhs).unwrap(), - } -} - -pub fn hash_f32(state: &mut H, f: f32) { - use std::hash::Hash; - let classify = f.classify(); - match classify { - std::num::FpCategory::Nan => (classify as u8).hash(state), - std::num::FpCategory::Infinite | std::num::FpCategory::Zero => { - (classify as u8, f.signum() as i8).hash(state) - } - std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => f.to_bits().hash(state), - } -} - -#[repr(transparent)] -#[derive(Debug, Clone, Copy)] -pub struct F32(f32); - -impl F32 { - #[inline] - pub fn from_bits(bits: u32) -> Self { - Self(f32::from_bits(bits)) - } - #[inline] - pub fn into_bits(self) -> u32 { - self.0.to_bits() - } -} - -impl From for F32 { - fn from(value: f32) -> Self { - Self(value) - } -} -impl From for f32 { - fn from(value: F32) -> Self { - value.0 - } -} - -impl Deref for F32 { - type Target = f32; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for F32 { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl PartialEq for F32 { - fn eq(&self, other: &Self) -> bool { - eq_f32(self.0, other.0) - } -} - -impl Eq for F32 {} - -impl PartialOrd for F32 { - fn partial_cmp(&self, other: &Self) -> Option { - Some(ord_f32(self.0, other.0)) - } -} - -impl Ord for F32 { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - ord_f32(self.0, other.0) - } -} - -impl core::hash::Hash for F32 { - fn hash(&self, state: &mut H) { - hash_f32(state, self.0) - } -} - pub fn timed T>(label: &str, f: F) -> T { let now = std::time::Instant::now(); let out = f(); diff --git a/crates/renderer_resources/Cargo.toml b/crates/renderer_resources/Cargo.toml new file mode 100644 index 0000000..e51badd --- /dev/null +++ b/crates/renderer_resources/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "renderer_resources" +version = "0.1.0" +edition = "2024" + +[dependencies] +renderer_device = { path = "../renderer_device" } +ash = { workspace = true } +gpu-allocator = { workspace = true } +parking_lot = { workspace = true } +bytemuck = { workspace = true } +tracing = { workspace = true } +ash-formats = { git = "https://github.com/janis-bhm/ash-formats", branch = "main" } diff --git a/crates/renderer/src/buffers.rs b/crates/renderer_resources/src/buffers.rs similarity index 99% rename from crates/renderer/src/buffers.rs rename to crates/renderer_resources/src/buffers.rs index 95469b0..64b27fd 100644 --- a/crates/renderer/src/buffers.rs +++ b/crates/renderer_resources/src/buffers.rs @@ -49,7 +49,6 @@ impl PartialEq for BufferDesc { fn eq(&self, other: &Self) -> bool { self.flags == other.flags // for hashmaps, `Eq` may be more strict than `Hash` - && self.name == other.name && self.size == other.size && self.usage == other.usage && self.queue_families == other.queue_families diff --git a/crates/renderer/src/images.rs b/crates/renderer_resources/src/images.rs similarity index 97% rename from crates/renderer/src/images.rs rename to crates/renderer_resources/src/images.rs index 45fbb68..dd224c4 100644 --- a/crates/renderer/src/images.rs +++ b/crates/renderer_resources/src/images.rs @@ -6,11 +6,8 @@ use std::{ }; use crate::{ - device::{ - Allocation, AllocationStrategy, DeviceObject, QueueFlags, - }, - swapchain::Swapchain, - util::weak_vec::WeakVec, + device::{Allocation, AllocationStrategy, DeviceObject, QueueFlags}, + util::WeakVec, }; use super::Device; @@ -75,7 +72,6 @@ impl Eq for ImageDesc {} impl PartialEq for ImageDesc { fn eq(&self, other: &Self) -> bool { self.flags == other.flags - && self.name == other.name && self.format == other.format && self.kind == other.kind && self.mip_levels == other.mip_levels @@ -90,7 +86,7 @@ impl PartialEq for ImageDesc { } } -impl<'a> std::fmt::Debug for ImageDesc { +impl std::fmt::Debug for ImageDesc { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ImageDesc") .field("flags", &self.flags) @@ -255,22 +251,24 @@ impl Image { Self::new_with_allocation_unchecked(device, image, allocation, desc) } - pub fn from_swapchain_image(image: vk::Image, swapchain: &Swapchain) -> Self { + pub fn from_swapchain_raw( + image: vk::Image, + device: Device, + format: vk::Format, + extent: vk::Extent3D, + usage: vk::ImageUsageFlags, + ) -> Self { Self { - image: ImageInner::Swapchain(image, swapchain.swapchain.device().clone()), + image: ImageInner::Swapchain(image, device), desc: ImageDesc { - format: swapchain.config.format, + format, kind: vk::ImageType::TYPE_2D, mip_levels: 1, array_layers: 1, samples: vk::SampleCountFlags::TYPE_1, - extent: vk::Extent3D { - width: swapchain.config.extent.width, - height: swapchain.config.extent.height, - depth: 1, - }, + extent, tiling: vk::ImageTiling::OPTIMAL, - usage: swapchain.config.usage, + usage, queue_families: QueueFlags::PRESENT, layout: vk::ImageLayout::UNDEFINED, mem_location: gpu_allocator::MemoryLocation::GpuOnly, diff --git a/crates/renderer_resources/src/lib.rs b/crates/renderer_resources/src/lib.rs new file mode 100644 index 0000000..2518d7b --- /dev/null +++ b/crates/renderer_resources/src/lib.rs @@ -0,0 +1,6 @@ +pub use renderer_device::{Device, Error, Result, device, make_extension}; + +pub mod buffers; +pub mod images; +pub mod pipeline; +pub mod util; diff --git a/crates/renderer/src/pipeline.rs b/crates/renderer_resources/src/pipeline.rs similarity index 100% rename from crates/renderer/src/pipeline.rs rename to crates/renderer_resources/src/pipeline.rs diff --git a/crates/renderer_resources/src/util.rs b/crates/renderer_resources/src/util.rs new file mode 100644 index 0000000..54ceb73 --- /dev/null +++ b/crates/renderer_resources/src/util.rs @@ -0,0 +1,170 @@ +pub(crate) mod weak_vec { + #![allow(dead_code)] + //! Module containing the [`WeakVec`] API. + + use std::{sync::Weak, vec::Vec}; + + /// An optimized container for `Weak` references of `T` that minimizes reallocations by + /// dropping older elements that no longer have strong references to them. + #[derive(Debug)] + pub(crate) struct WeakVec { + inner: Vec>, + } + + impl Default for WeakVec { + fn default() -> Self { + Self { + inner: Default::default(), + } + } + } + + impl WeakVec { + pub(crate) fn new() -> Self { + Self { inner: Vec::new() } + } + + /// Pushes a new element to this collection. + /// + /// If the inner Vec needs to be reallocated, we will first drop older elements that + /// no longer have strong references to them. + pub(crate) fn push(&mut self, value: Weak) { + if self.inner.len() == self.inner.capacity() { + // Iterating backwards has the advantage that we don't do more work than we have to. + for i in (0..self.inner.len()).rev() { + if self.inner[i].strong_count() == 0 { + self.inner.swap_remove(i); + } + } + + // Make sure our capacity is twice the number of live elements. + // Leaving some spare capacity ensures that we won't re-scan immediately. + self.inner.reserve_exact(self.inner.len()); + } + + self.inner.push(value); + } + } + + pub(crate) struct WeakVecIter { + inner: std::vec::IntoIter>, + } + + impl Iterator for WeakVecIter { + type Item = Weak; + fn next(&mut self) -> Option { + self.inner.next() + } + } + + impl IntoIterator for WeakVec { + type Item = Weak; + type IntoIter = WeakVecIter; + fn into_iter(self) -> Self::IntoIter { + WeakVecIter { + inner: self.inner.into_iter(), + } + } + } +} + +pub(crate) use weak_vec::WeakVec; + +pub fn eq_f32(lhs: f32, rhs: f32) -> bool { + use core::num::FpCategory::*; + match (lhs.classify(), rhs.classify()) { + (Zero, Zero) | (Nan, Nan) => true, + (Infinite, Infinite) => lhs.signum() == rhs.signum(), + _ => lhs == rhs, + } +} + +pub fn ord_f32(lhs: f32, rhs: f32) -> core::cmp::Ordering { + use core::cmp::Ordering; + use core::num::FpCategory::*; + match (lhs.classify(), rhs.classify()) { + (Zero, Zero) | (Nan, Nan) => Ordering::Equal, + (Infinite, Infinite) => { + core::cmp::PartialOrd::partial_cmp(&lhs.signum(), &rhs.signum()).unwrap() + } + _ => core::cmp::PartialOrd::partial_cmp(&lhs, &rhs).unwrap(), + } +} + +pub fn hash_f32(state: &mut H, f: f32) { + use std::hash::Hash; + let classify = f.classify(); + match classify { + std::num::FpCategory::Nan => (classify as u8).hash(state), + std::num::FpCategory::Infinite | std::num::FpCategory::Zero => { + (classify as u8, f.signum() as i8).hash(state) + } + std::num::FpCategory::Subnormal | std::num::FpCategory::Normal => f.to_bits().hash(state), + } +} + +#[repr(transparent)] +#[derive(Debug, Clone, Copy)] +pub struct F32(f32); + +impl F32 { + #[inline] + pub fn from_bits(bits: u32) -> Self { + Self(f32::from_bits(bits)) + } + #[inline] + pub fn into_bits(self) -> u32 { + self.0.to_bits() + } +} + +impl From for F32 { + fn from(value: f32) -> Self { + Self(value) + } +} +impl From for f32 { + fn from(value: F32) -> Self { + value.0 + } +} + +impl std::ops::Deref for F32 { + type Target = f32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for F32 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl PartialEq for F32 { + fn eq(&self, other: &Self) -> bool { + eq_f32(self.0, other.0) + } +} + +impl Eq for F32 {} + +impl PartialOrd for F32 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(ord_f32(self.0, other.0)) + } +} + +impl Ord for F32 { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + ord_f32(self.0, other.0) + } +} + +impl core::hash::Hash for F32 { + fn hash(&self, state: &mut H) { + hash_f32(state, self.0) + } +}