extract resources into renderer_resources

This commit is contained in:
janis 2026-10-24 21:16:54 +02:00
parent 69039ba3c1
commit a173cf18ff
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
12 changed files with 221 additions and 195 deletions

View file

@ -12,7 +12,7 @@ members = [
"crates/window", "crates/window",
"crates/game", "crates/game",
"crates/text", "crates/text",
"crates/bevy_vulkan_render", "crates/renderer_device", "crates/bevy_vulkan_render", "crates/renderer_device", "crates/renderer_resources",
] ]
[dependencies] [dependencies]
@ -57,6 +57,7 @@ ahash = "0.8"
md-5 = "0.11.0" md-5 = "0.11.0"
parking_lot = "0.12.3" parking_lot = "0.12.3"
bytemuck = { version = "1.21.0", features = ["derive"] }
# tokio = "1.42" # tokio = "1.42"
# futures = "0.3" # futures = "0.3"

View file

@ -5,6 +5,7 @@ edition = "2024"
[dependencies] [dependencies]
renderer_device = { path = "../renderer_device" } renderer_device = { path = "../renderer_device" }
renderer_resources = { path = "../renderer_resources" }
renderer_macros = { path = "renderer_macros" } renderer_macros = { path = "renderer_macros" }
arrayvec = { workspace = true } arrayvec = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }

View file

@ -11,18 +11,17 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
pub use ash; pub use ash;
mod buffers;
pub mod commands; pub mod commands;
#[path = "egui.rs"] #[path = "egui.rs"]
mod egui_pass; mod egui_pass;
mod images;
mod pipeline;
pub mod render_graph; pub mod render_graph;
pub mod rendering; pub mod rendering;
pub mod swapchain; pub mod swapchain;
mod text; mod text;
pub mod util; pub mod util;
pub(crate) use renderer_resources::{buffers, images, pipeline};
pub use renderer_device::{device, instance, queue, sync}; pub use renderer_device::{device, instance, queue, sync};
pub use renderer_device::{ pub use renderer_device::{
compatible_extension_properties, extension_intersection, compatible_extension_properties, extension_intersection,

View file

@ -3,7 +3,6 @@ use std::sync::Arc;
use ash::vk; use ash::vk;
use glam::{ use glam::{
camera::rh::{proj, view}, camera::rh::{proj, view},
f32::Mat4,
vec3, vec3,
}; };
use gpu_allocator::MemoryLocation; use gpu_allocator::MemoryLocation;
@ -165,11 +164,7 @@ impl Wireframe {
); );
} }
let future = cmd.submit_async( let future = cmd.submit_async(None, None, Arc::new(sync::Fence::from_pool(&dev, None)?))?;
None,
None,
Arc::new(sync::Fence::from_pool(&dev, None)?),
)?;
let pipeline = Self::create_pipeline(dev.clone())?; let pipeline = Self::create_pipeline(dev.clone())?;

View file

@ -562,7 +562,17 @@ impl Swapchain {
let idx = idx as usize; let idx = idx as usize;
let image = self.images[idx]; 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 { let view = image.create_view(ImageViewDesc {
name: Some(format!("swapchain-{:x}-image-view-{idx}", self.raw().as_raw()).into()), name: Some(format!("swapchain-{:x}-image-view-{idx}", self.raw().as_raw()).into()),
kind: vk::ImageViewType::TYPE_2D, kind: vk::ImageViewType::TYPE_2D,

View file

@ -6,75 +6,7 @@ use std::{
use ash::vk; use ash::vk;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
pub(crate) mod weak_vec { pub use renderer_resources::util::{F32, eq_f32, hash_f32, ord_f32};
#![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<T> {
inner: Vec<Weak<T>>,
}
impl<T> Default for WeakVec<T> {
fn default() -> Self {
Self {
inner: Default::default(),
}
}
}
impl<T> WeakVec<T> {
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<T>) {
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<T> {
inner: std::vec::IntoIter<Weak<T>>,
}
impl<T> Iterator for WeakVecIter<T> {
type Item = Weak<T>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl<T> IntoIterator for WeakVec<T> {
type Item = Weak<T>;
type IntoIter = WeakVecIter<T>;
fn into_iter(self) -> Self::IntoIter {
WeakVecIter {
inner: self.inner.into_iter(),
}
}
}
}
#[derive(derive_more::Debug)] #[derive(derive_more::Debug)]
pub(crate) struct DropGuard(#[debug(skip)] ManuallyDrop<Box<dyn FnOnce()>>); pub(crate) struct DropGuard(#[debug(skip)] ManuallyDrop<Box<dyn FnOnce()>>);
@ -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<H: std::hash::Hasher>(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<f32> for F32 {
fn from(value: f32) -> Self {
Self(value)
}
}
impl From<F32> 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<std::cmp::Ordering> {
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<H: std::hash::Hasher>(&self, state: &mut H) {
hash_f32(state, self.0)
}
}
pub fn timed<T, F: FnOnce() -> T>(label: &str, f: F) -> T { pub fn timed<T, F: FnOnce() -> T>(label: &str, f: F) -> T {
let now = std::time::Instant::now(); let now = std::time::Instant::now();
let out = f(); let out = f();

View file

@ -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" }

View file

@ -49,7 +49,6 @@ impl PartialEq for BufferDesc {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.flags == other.flags self.flags == other.flags
// for hashmaps, `Eq` may be more strict than `Hash` // for hashmaps, `Eq` may be more strict than `Hash`
&& self.name == other.name
&& self.size == other.size && self.size == other.size
&& self.usage == other.usage && self.usage == other.usage
&& self.queue_families == other.queue_families && self.queue_families == other.queue_families

View file

@ -6,11 +6,8 @@ use std::{
}; };
use crate::{ use crate::{
device::{ device::{Allocation, AllocationStrategy, DeviceObject, QueueFlags},
Allocation, AllocationStrategy, DeviceObject, QueueFlags, util::WeakVec,
},
swapchain::Swapchain,
util::weak_vec::WeakVec,
}; };
use super::Device; use super::Device;
@ -75,7 +72,6 @@ impl Eq for ImageDesc {}
impl PartialEq for ImageDesc { impl PartialEq for ImageDesc {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.flags == other.flags self.flags == other.flags
&& self.name == other.name
&& self.format == other.format && self.format == other.format
&& self.kind == other.kind && self.kind == other.kind
&& self.mip_levels == other.mip_levels && 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 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ImageDesc") f.debug_struct("ImageDesc")
.field("flags", &self.flags) .field("flags", &self.flags)
@ -255,22 +251,24 @@ impl Image {
Self::new_with_allocation_unchecked(device, image, allocation, desc) 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 { Self {
image: ImageInner::Swapchain(image, swapchain.swapchain.device().clone()), image: ImageInner::Swapchain(image, device),
desc: ImageDesc { desc: ImageDesc {
format: swapchain.config.format, format,
kind: vk::ImageType::TYPE_2D, kind: vk::ImageType::TYPE_2D,
mip_levels: 1, mip_levels: 1,
array_layers: 1, array_layers: 1,
samples: vk::SampleCountFlags::TYPE_1, samples: vk::SampleCountFlags::TYPE_1,
extent: vk::Extent3D { extent,
width: swapchain.config.extent.width,
height: swapchain.config.extent.height,
depth: 1,
},
tiling: vk::ImageTiling::OPTIMAL, tiling: vk::ImageTiling::OPTIMAL,
usage: swapchain.config.usage, usage,
queue_families: QueueFlags::PRESENT, queue_families: QueueFlags::PRESENT,
layout: vk::ImageLayout::UNDEFINED, layout: vk::ImageLayout::UNDEFINED,
mem_location: gpu_allocator::MemoryLocation::GpuOnly, mem_location: gpu_allocator::MemoryLocation::GpuOnly,

View file

@ -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;

View file

@ -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<T> {
inner: Vec<Weak<T>>,
}
impl<T> Default for WeakVec<T> {
fn default() -> Self {
Self {
inner: Default::default(),
}
}
}
impl<T> WeakVec<T> {
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<T>) {
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<T> {
inner: std::vec::IntoIter<Weak<T>>,
}
impl<T> Iterator for WeakVecIter<T> {
type Item = Weak<T>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl<T> IntoIterator for WeakVec<T> {
type Item = Weak<T>;
type IntoIter = WeakVecIter<T>;
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<H: std::hash::Hasher>(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<f32> for F32 {
fn from(value: f32) -> Self {
Self(value)
}
}
impl From<F32> 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<std::cmp::Ordering> {
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<H: std::hash::Hasher>(&self, state: &mut H) {
hash_f32(state, self.0)
}
}