diff --git a/Cargo.toml b/Cargo.toml index 7454ef2..a232693 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ "crates/window", "crates/game", "crates/text", - "crates/bevy_vulkan_render", + "crates/bevy_vulkan_render", "crates/renderer_device", ] [dependencies] diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml index 115a6bc..f9f80b4 100644 --- a/crates/renderer/Cargo.toml +++ b/crates/renderer/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" edition = "2024" [dependencies] +renderer_device = { path = "../renderer_device" } renderer_macros = { path = "renderer_macros" } -tinyvec = { workspace = true } arrayvec = { workspace = true } indexmap = { workspace = true } petgraph = { workspace = true } @@ -18,7 +18,6 @@ bitflags = { workspace = true } thread_local = {workspace = true} thiserror = { workspace = true } -anyhow = { workspace = true } tracing = { workspace = true } ash = { workspace = true } @@ -27,8 +26,6 @@ ash-window = { workspace = true } gpu-allocator = { workspace = true } rectangle-pack = { workspace = true } -md-5 = { workspace = true } - raw-window-handle = { workspace = true } egui = { workspace = true , features = ["bytemuck"]} # egui_winit_platform = { workspace = true } @@ -42,7 +39,6 @@ smol = { workspace = true } derive_more = { workspace = true , features = ["debug"] } cfg-if = "1.0.0" -dyn-clone = "1" crossbeam = "0.8.4" bytemuck = { version = "1.21.0", features = ["derive"] } diff --git a/crates/renderer/renderer_macros/Cargo.toml b/crates/renderer/renderer_macros/Cargo.toml index 31cf8d6..7428c4f 100644 --- a/crates/renderer/renderer_macros/Cargo.toml +++ b/crates/renderer/renderer_macros/Cargo.toml @@ -9,4 +9,4 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = "2" +syn = { version = "2", features = ["full"] } diff --git a/crates/renderer/renderer_macros/src/lib.rs b/crates/renderer/renderer_macros/src/lib.rs index 8b13789..675386c 100644 --- a/crates/renderer/renderer_macros/src/lib.rs +++ b/crates/renderer/renderer_macros/src/lib.rs @@ -1 +1,122 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DeriveInput, Fields}; +#[proc_macro_derive(DeviceRef, attributes(device))] +pub fn derive_device_ref(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let field = match &input.data { + Data::Struct(data) => match &data.fields { + Fields::Named(fields) => { + let marked_fields = fields + .named.iter() + .filter(|field| { + field + .attrs + .iter() + .any(|attr| attr.path().is_ident("device")) + }) + .collect::>(); + + let field = match marked_fields.as_slice() { + [] => { + return syn::Error::new_spanned( + &input.ident, + "no field marked with #[device]", + ) + .to_compile_error() + .into(); + } + [field] => *field, + duplicates => { + let mut errors = duplicates.iter().skip(1).map(|field| { + let name = field.ident.as_ref().expect("named fields have identifiers"); + syn::Error::new_spanned( + name, + format!("duplicate #[device] field `{name}`"), + ) + }); + let mut error = errors.next().expect("at least two marked fields"); + for duplicate in errors { + error.combine(duplicate); + } + return error.to_compile_error().into(); + } + }; + + field + } + Fields::Unnamed(fields) => { + let marked_fields = fields + .unnamed.iter() + .filter(|field| { + field + .attrs + .iter() + .any(|attr| attr.path().is_ident("device")) + }) + .collect::>(); + + let field = match marked_fields.as_slice() { + [] => { + return syn::Error::new_spanned( + &input.ident, + "no field marked with #[device]", + ) + .to_compile_error() + .into(); + } + [field] => *field, + duplicates => { + let mut errors = duplicates.iter().skip(1).map(|field| { + let name = field.ident.as_ref().expect("named fields have identifiers"); + syn::Error::new_spanned( + name, + format!("duplicate #[device] field `{name}`"), + ) + }); + let mut error = errors.next().expect("at least two marked fields"); + for duplicate in errors { + error.combine(duplicate); + } + return error.to_compile_error().into(); + } + }; + + field + } + _ => { + return syn::Error::new_spanned( + &input.ident, + "DeviceRef can only be derived for structs with named fields", + ) + .to_compile_error() + .into(); + } + }, + Data::Enum(_) | Data::Union(_) => { + return syn::Error::new_spanned( + &input.ident, + "DeviceRef can only be derived for structs with named fields", + ) + .to_compile_error() + .into(); + } + }; + + let field_ident = field.ident.as_ref().expect("named fields have identifiers"); + let ident = &input.ident; + let generics = input.generics; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + quote! { + impl #impl_generics ::renderer_device::device::DeviceRef for #ident #ty_generics #where_clause { + fn device(&self) -> &::renderer_device::device::Device { + ::renderer_device::device::DeviceRef::device(&self.#field_ident) + } + } + } + .into() +} diff --git a/crates/renderer/src/commands.rs b/crates/renderer/src/commands.rs index 77d8698..909e538 100644 --- a/crates/renderer/src/commands.rs +++ b/crates/renderer/src/commands.rs @@ -288,7 +288,7 @@ impl SingleUseCommand { wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, signal: Option, ) -> crate::Result<()> { - let fence = Arc::new(sync::Fence::from_pool(&self.device().pools.fences, None)?); + let fence = Arc::new(sync::Fence::from_pool(&self.device().pools().fences, None)?); let future = self.submit_async(wait, signal, fence)?; future.block()?; Ok(()) diff --git a/crates/renderer/src/images.rs b/crates/renderer/src/images.rs index 4248692..45fbb68 100644 --- a/crates/renderer/src/images.rs +++ b/crates/renderer/src/images.rs @@ -7,8 +7,7 @@ use std::{ use crate::{ device::{ - Allocation, AllocationStrategy, DeviceInner, DeviceObject, QueueFlags, - asdf::traits::ExternallyManagedObject, + Allocation, AllocationStrategy, DeviceObject, QueueFlags, }, swapchain::Swapchain, util::weak_vec::WeakVec, @@ -139,14 +138,6 @@ enum ImageInner { Allocated(DeviceObject, Allocation), } -impl> ExternallyManagedObject for vk::Image { - unsafe fn destroy(self, device: &T) { - unsafe { - device.as_ref().raw.destroy_image(self, None); - } - } -} - impl ImageInner { fn image(&self) -> vk::Image { match self { @@ -1017,14 +1008,6 @@ impl ImageView { } } -impl> ExternallyManagedObject for vk::ImageView { - unsafe fn destroy(self, device: &T) { - unsafe { - device.as_ref().raw.destroy_image_view(self, None); - } - } -} - pub struct QueueOwnership { pub src: u32, pub dst: u32, diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index 422e11e..399c481 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -2,41 +2,37 @@ negative_impls, map_try_insert, debug_closure_helpers, - core_io, - core_io_borrowed_buf, - slice_partition_dedup )] -use std::{collections::HashMap, fmt::Debug, path::PathBuf, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; +use ash::{prelude::VkResult, vk}; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; -use ash::{ - ext, - prelude::VkResult, - vk::{self, SurfaceCapabilitiesKHR}, -}; -use dyn_clone::DynClone; - pub use ash; mod buffers; pub mod commands; -mod debug; -pub mod device; #[path = "egui.rs"] mod egui_pass; mod images; -pub mod instance; mod pipeline; pub mod render_graph; pub mod rendering; pub mod swapchain; -pub mod sync; mod text; pub mod util; -use device::{Device, DeviceOwned}; +pub use renderer_device::{device, instance, queue, sync}; +pub use renderer_device::{ + compatible_extension_properties, define_device_owned_handle, extension_intersection, + get_physical_device_features, get_physical_device_properties, make_extension, + DebugUtils, DebugUtilsCreateInfo, Device, DeviceOwned, DeviceRef, Error, + ExtendsDeviceFeatures2Debug, ExtendsDeviceProperties2Debug, Extension, Instance, + PhysicalDeviceFeatures, PhysicalDeviceInfo, PhysicalDeviceProperties, Result, + SurfaceCapabilities, +}; +pub use renderer_macros::DeviceRef; mod texture { use std::{collections::BTreeMap, sync::Arc}; @@ -80,581 +76,7 @@ mod texture { use util::Rgba; -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("No Physical Device found for this instance.")] - NoAdapter, - #[error("Swapchain suboptimal.")] - SuboptimalSwapchain, - #[error(transparent)] - LoadingError(#[from] ash::LoadingError), - #[error(transparent)] - Result(#[from] ash::vk::Result), - #[error(transparent)] - CStrError(#[from] core::ffi::c_str::FromBytesUntilNulError), - #[error(transparent)] - Utf8Error(#[from] core::str::Utf8Error), - #[error(transparent)] - NulError(#[from] std::ffi::NulError), - #[error(transparent)] - GpuAllocatorError(#[from] gpu_allocator::AllocationError), - #[error("No Physical Device found.")] - NoPhysicalDevice, - #[error(transparent)] - Io(#[from] std::io::Error), - #[error( - "Image dimensions ({width}x{height}) exceed the maximum allowed size of {max_size}x{max_size}." - )] - ImageTooLarge { - width: u32, - height: u32, - max_size: u32, - }, - #[error("Image dimensions cannot be zero.")] - ImageZeroSized, - #[error("Incompatible image view kind {view_kind:?} for image kind {image_kind:?}.")] - IncompatibleImageViewKind { - view_kind: vk::ImageViewType, - image_kind: vk::ImageType, - }, - #[error("Asset missing at path {0:?}")] - AssetMissing(PathBuf), - #[error("Unspecified Error")] - Unspecified, - #[error("BEEP BOOP create an error variant for {0} BEEP BOOP")] - Todo(&'static str), -} -pub type Result = core::result::Result; - -#[derive(Debug, Clone)] -#[allow(dead_code)] -struct DeviceExtension<'a> { - name: &'a core::ffi::CStr, - version: u32, -} - -/// returns true if lhs and rhs have the same name and lhs spec_version is less -/// than or equal to rhs spec_version -#[allow(dead_code)] -fn compatible_extension_properties( - lhs: &vk::ExtensionProperties, - rhs: &vk::ExtensionProperties, -) -> bool { - let Some(lhs_name) = lhs.extension_name_as_c_str().ok() else { - return false; - }; - let Some(rhs_name) = rhs.extension_name_as_c_str().ok() else { - return false; - }; - - if lhs_name == rhs_name { - lhs.spec_version <= rhs.spec_version - } else { - false - } -} - -pub mod queue; - -pub trait ExtendsDeviceFeatures2Debug: - vk::ExtendsPhysicalDeviceFeatures2 + Debug + Send + Sync -{ -} -pub trait ExtendsDeviceProperties2Debug: - vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync -{ -} - -impl ExtendsDeviceFeatures2Debug - for T -{ -} -impl - ExtendsDeviceProperties2Debug for T -{ -} - -#[derive(Debug)] -pub struct PhysicalDeviceInfo { - pub pdev: vk::PhysicalDevice, - pub properties: PhysicalDeviceProperties, - pub features: PhysicalDeviceFeatures, -} - -#[derive(Debug)] -pub struct SurfaceCapabilities { - pub capabilities: SurfaceCapabilitiesKHR, - /// The minimum number of images that must be present in the swapchain for - /// the surface for any present mode supported by the surface. - pub min_image_count: u32, - pub formats: Vec, - pub present_modes: Vec, -} - -#[derive(Default, Debug)] -pub struct PhysicalDeviceFeatures { - pub core: vk::PhysicalDeviceFeatures, - pub core11: vk::PhysicalDeviceVulkan11Features<'static>, - pub core12: vk::PhysicalDeviceVulkan12Features<'static>, - pub core13: vk::PhysicalDeviceVulkan13Features<'static>, - pub mesh_shader: Option>, -} - -impl PhysicalDeviceFeatures { - pub fn push_to_device_create_info<'a>( - &'a mut self, - mut create_info: vk::DeviceCreateInfo<'a>, - ) -> vk::DeviceCreateInfo<'a> { - create_info = create_info - .enabled_features(&self.core) - .push_next(&mut self.core11) - .push_next(&mut self.core12) - .push_next(&mut self.core13); - - if let Some(mesh_shader) = &mut self.mesh_shader { - create_info = create_info.push_next(mesh_shader); - } - create_info - } - pub fn superset_of(&self, other: &PhysicalDeviceFeatures) -> bool { - fn core_superset_of( - a: &vk::PhysicalDeviceFeatures, - b: &vk::PhysicalDeviceFeatures, - ) -> bool { - (b.robust_buffer_access == vk::FALSE - || a.robust_buffer_access != b.robust_buffer_access) - && (b.full_draw_index_uint32 == vk::FALSE - || a.full_draw_index_uint32 != b.full_draw_index_uint32) - && (b.image_cube_array == vk::FALSE || a.image_cube_array != b.image_cube_array) - && (b.independent_blend == vk::FALSE || a.independent_blend != b.independent_blend) - && (b.geometry_shader == vk::FALSE || a.geometry_shader != b.geometry_shader) - && (b.tessellation_shader == vk::FALSE - || a.tessellation_shader != b.tessellation_shader) - && (b.sample_rate_shading == vk::FALSE - || a.sample_rate_shading != b.sample_rate_shading) - && (b.dual_src_blend == vk::FALSE || a.dual_src_blend != b.dual_src_blend) - && (b.logic_op == vk::FALSE || a.logic_op != b.logic_op) - && (b.multi_draw_indirect == vk::FALSE - || a.multi_draw_indirect != b.multi_draw_indirect) - && (b.draw_indirect_first_instance == vk::FALSE - || a.draw_indirect_first_instance != b.draw_indirect_first_instance) - && (b.depth_clamp == vk::FALSE || a.depth_clamp != b.depth_clamp) - && (b.depth_bias_clamp == vk::FALSE || a.depth_bias_clamp != b.depth_bias_clamp) - && (b.fill_mode_non_solid == vk::FALSE - || a.fill_mode_non_solid != b.fill_mode_non_solid) - && (b.depth_bounds == vk::FALSE || a.depth_bounds != b.depth_bounds) - && (b.wide_lines == vk::FALSE || a.wide_lines != b.wide_lines) - && (b.large_points == vk::FALSE || a.large_points != b.large_points) - && (b.alpha_to_one == vk::FALSE || a.alpha_to_one != b.alpha_to_one) - && (b.multi_viewport == vk::FALSE || a.multi_viewport != b.multi_viewport) - && (b.sampler_anisotropy == vk::FALSE - || a.sampler_anisotropy != b.sampler_anisotropy) - && (b.texture_compression_etc2 == vk::FALSE - || a.texture_compression_etc2 != b.texture_compression_etc2) - && (b.texture_compression_astc_ldr == vk::FALSE - || a.texture_compression_astc_ldr != b.texture_compression_astc_ldr) - && (b.texture_compression_bc == vk::FALSE - || a.texture_compression_bc != b.texture_compression_bc) - && (b.occlusion_query_precise == vk::FALSE - || a.occlusion_query_precise != b.occlusion_query_precise) - && (b.pipeline_statistics_query == vk::FALSE - || a.pipeline_statistics_query != b.pipeline_statistics_query) - && (b.vertex_pipeline_stores_and_atomics == vk::FALSE - || a.vertex_pipeline_stores_and_atomics != b.vertex_pipeline_stores_and_atomics) - && (b.fragment_stores_and_atomics == vk::FALSE - || a.fragment_stores_and_atomics != b.fragment_stores_and_atomics) - && (b.shader_tessellation_and_geometry_point_size == vk::FALSE - || a.shader_tessellation_and_geometry_point_size - != b.shader_tessellation_and_geometry_point_size) - && (b.shader_image_gather_extended == vk::FALSE - || a.shader_image_gather_extended != b.shader_image_gather_extended) - && (b.shader_storage_image_extended_formats == vk::FALSE - || a.shader_storage_image_extended_formats - != b.shader_storage_image_extended_formats) - && (b.shader_storage_image_multisample == vk::FALSE - || a.shader_storage_image_multisample != b.shader_storage_image_multisample) - && (b.shader_storage_image_read_without_format == vk::FALSE - || a.shader_storage_image_read_without_format - != b.shader_storage_image_read_without_format) - && (b.shader_storage_image_write_without_format == vk::FALSE - || a.shader_storage_image_write_without_format - != b.shader_storage_image_write_without_format) - && (b.shader_uniform_buffer_array_dynamic_indexing == vk::FALSE - || a.shader_uniform_buffer_array_dynamic_indexing - != b.shader_uniform_buffer_array_dynamic_indexing) - && (b.shader_sampled_image_array_dynamic_indexing == vk::FALSE - || a.shader_sampled_image_array_dynamic_indexing - != b.shader_sampled_image_array_dynamic_indexing) - && (b.shader_storage_buffer_array_dynamic_indexing == vk::FALSE - || a.shader_storage_buffer_array_dynamic_indexing - != b.shader_storage_buffer_array_dynamic_indexing) - && (b.shader_storage_image_array_dynamic_indexing == vk::FALSE - || a.shader_storage_image_array_dynamic_indexing - != b.shader_storage_image_array_dynamic_indexing) - && (b.shader_clip_distance == vk::FALSE - || a.shader_clip_distance != b.shader_clip_distance) - && (b.shader_cull_distance == vk::FALSE - || a.shader_cull_distance != b.shader_cull_distance) - && (b.shader_float64 == vk::FALSE || a.shader_float64 != b.shader_float64) - && (b.shader_int64 == vk::FALSE || a.shader_int64 != b.shader_int64) - && (b.shader_int16 == vk::FALSE || a.shader_int16 != b.shader_int16) - && (b.shader_resource_residency == vk::FALSE - || a.shader_resource_residency != b.shader_resource_residency) - && (b.shader_resource_min_lod == vk::FALSE - || a.shader_resource_min_lod != b.shader_resource_min_lod) - && (b.sparse_binding == vk::FALSE || a.sparse_binding != b.sparse_binding) - && (b.sparse_residency_buffer == vk::FALSE - || a.sparse_residency_buffer != b.sparse_residency_buffer) - && (b.sparse_residency_image2_d == vk::FALSE - || a.sparse_residency_image2_d != b.sparse_residency_image2_d) - && (b.sparse_residency_image3_d == vk::FALSE - || a.sparse_residency_image3_d != b.sparse_residency_image3_d) - && (b.sparse_residency2_samples == vk::FALSE - || a.sparse_residency2_samples != b.sparse_residency2_samples) - && (b.sparse_residency4_samples == vk::FALSE - || a.sparse_residency4_samples != b.sparse_residency4_samples) - && (b.sparse_residency8_samples == vk::FALSE - || a.sparse_residency8_samples != b.sparse_residency8_samples) - && (b.sparse_residency16_samples == vk::FALSE - || a.sparse_residency16_samples != b.sparse_residency16_samples) - && (b.sparse_residency_aliased == vk::FALSE - || a.sparse_residency_aliased != b.sparse_residency_aliased) - && (b.variable_multisample_rate == vk::FALSE - || a.variable_multisample_rate != b.variable_multisample_rate) - && (b.inherited_queries == vk::FALSE || a.inherited_queries != b.inherited_queries) - } - - fn core11_superset_of( - a: &vk::PhysicalDeviceVulkan11Features, - b: &vk::PhysicalDeviceVulkan11Features, - ) -> bool { - (b.storage_buffer16_bit_access == vk::FALSE - || a.storage_buffer16_bit_access != b.storage_buffer16_bit_access) - && (b.uniform_and_storage_buffer16_bit_access == vk::FALSE - || a.uniform_and_storage_buffer16_bit_access - != b.uniform_and_storage_buffer16_bit_access) - && (b.storage_push_constant16 == vk::FALSE - || a.storage_push_constant16 != b.storage_push_constant16) - && (b.storage_input_output16 == vk::FALSE - || a.storage_input_output16 != b.storage_input_output16) - && (b.multiview == vk::FALSE || a.multiview != b.multiview) - && (b.multiview_geometry_shader == vk::FALSE - || a.multiview_geometry_shader != b.multiview_geometry_shader) - && (b.multiview_tessellation_shader == vk::FALSE - || a.multiview_tessellation_shader != b.multiview_tessellation_shader) - && (b.variable_pointers_storage_buffer == vk::FALSE - || a.variable_pointers_storage_buffer != b.variable_pointers_storage_buffer) - && (b.variable_pointers == vk::FALSE || a.variable_pointers != b.variable_pointers) - && (b.protected_memory == vk::FALSE || a.protected_memory != b.protected_memory) - && (b.sampler_ycbcr_conversion == vk::FALSE - || a.sampler_ycbcr_conversion != b.sampler_ycbcr_conversion) - && (b.shader_draw_parameters == vk::FALSE - || a.shader_draw_parameters != b.shader_draw_parameters) - } - - fn core12_superset_of( - a: &vk::PhysicalDeviceVulkan12Features, - b: &vk::PhysicalDeviceVulkan12Features, - ) -> bool { - (b.sampler_mirror_clamp_to_edge == vk::FALSE - || a.sampler_mirror_clamp_to_edge != b.sampler_mirror_clamp_to_edge) - && (b.draw_indirect_count == vk::FALSE - || a.draw_indirect_count != b.draw_indirect_count) - && (b.storage_buffer8_bit_access == vk::FALSE - || a.storage_buffer8_bit_access != b.storage_buffer8_bit_access) - && (b.uniform_and_storage_buffer8_bit_access == vk::FALSE - || a.uniform_and_storage_buffer8_bit_access - != b.uniform_and_storage_buffer8_bit_access) - && (b.storage_push_constant8 == vk::FALSE - || a.storage_push_constant8 != b.storage_push_constant8) - && (b.shader_buffer_int64_atomics == vk::FALSE - || a.shader_buffer_int64_atomics != b.shader_buffer_int64_atomics) - && (b.shader_shared_int64_atomics == vk::FALSE - || a.shader_shared_int64_atomics != b.shader_shared_int64_atomics) - && (b.shader_float16 == vk::FALSE || a.shader_float16 != b.shader_float16) - && (b.shader_int8 == vk::FALSE || a.shader_int8 != b.shader_int8) - && (b.descriptor_indexing == vk::FALSE - || a.descriptor_indexing != b.descriptor_indexing) - && (b.shader_input_attachment_array_dynamic_indexing == vk::FALSE - || a.shader_input_attachment_array_dynamic_indexing - != b.shader_input_attachment_array_dynamic_indexing) - && (b.shader_uniform_texel_buffer_array_dynamic_indexing == vk::FALSE - || a.shader_uniform_texel_buffer_array_dynamic_indexing - != b.shader_uniform_texel_buffer_array_dynamic_indexing) - && (b.shader_storage_texel_buffer_array_dynamic_indexing == vk::FALSE - || a.shader_storage_texel_buffer_array_dynamic_indexing - != b.shader_storage_texel_buffer_array_dynamic_indexing) - && (b.shader_uniform_buffer_array_non_uniform_indexing == vk::FALSE - || a.shader_uniform_buffer_array_non_uniform_indexing - != b.shader_uniform_buffer_array_non_uniform_indexing) - && (b.shader_sampled_image_array_non_uniform_indexing == vk::FALSE - || a.shader_sampled_image_array_non_uniform_indexing - != b.shader_sampled_image_array_non_uniform_indexing) - && (b.shader_storage_buffer_array_non_uniform_indexing == vk::FALSE - || a.shader_storage_buffer_array_non_uniform_indexing - != b.shader_storage_buffer_array_non_uniform_indexing) - && (b.shader_storage_image_array_non_uniform_indexing == vk::FALSE - || a.shader_storage_image_array_non_uniform_indexing - != b.shader_storage_image_array_non_uniform_indexing) - && (b.shader_input_attachment_array_non_uniform_indexing == vk::FALSE - || a.shader_input_attachment_array_non_uniform_indexing - != b.shader_input_attachment_array_non_uniform_indexing) - && (b.shader_uniform_texel_buffer_array_non_uniform_indexing == vk::FALSE - || a.shader_uniform_texel_buffer_array_non_uniform_indexing - != b.shader_uniform_texel_buffer_array_non_uniform_indexing) - && (b.shader_storage_texel_buffer_array_non_uniform_indexing == vk::FALSE - || a.shader_storage_texel_buffer_array_non_uniform_indexing - != b.shader_storage_texel_buffer_array_non_uniform_indexing) - && (b.descriptor_binding_uniform_buffer_update_after_bind == vk::FALSE - || a.descriptor_binding_uniform_buffer_update_after_bind - != b.descriptor_binding_uniform_buffer_update_after_bind) - && (b.descriptor_binding_sampled_image_update_after_bind == vk::FALSE - || a.descriptor_binding_sampled_image_update_after_bind - != b.descriptor_binding_sampled_image_update_after_bind) - && (b.descriptor_binding_storage_image_update_after_bind == vk::FALSE - || a.descriptor_binding_storage_image_update_after_bind - != b.descriptor_binding_storage_image_update_after_bind) - && (b.descriptor_binding_storage_buffer_update_after_bind == vk::FALSE - || a.descriptor_binding_storage_buffer_update_after_bind - != b.descriptor_binding_storage_buffer_update_after_bind) - && (b.descriptor_binding_uniform_texel_buffer_update_after_bind == vk::FALSE - || a.descriptor_binding_uniform_texel_buffer_update_after_bind - != b.descriptor_binding_uniform_texel_buffer_update_after_bind) - && (b.descriptor_binding_storage_texel_buffer_update_after_bind == vk::FALSE - || a.descriptor_binding_storage_texel_buffer_update_after_bind - != b.descriptor_binding_storage_texel_buffer_update_after_bind) - && (b.descriptor_binding_update_unused_while_pending == vk::FALSE - || a.descriptor_binding_update_unused_while_pending - != b.descriptor_binding_update_unused_while_pending) - && (b.descriptor_binding_partially_bound == vk::FALSE - || a.descriptor_binding_partially_bound != b.descriptor_binding_partially_bound) - && (b.descriptor_binding_variable_descriptor_count == vk::FALSE - || a.descriptor_binding_variable_descriptor_count - != b.descriptor_binding_variable_descriptor_count) - && (b.runtime_descriptor_array == vk::FALSE - || a.runtime_descriptor_array != b.runtime_descriptor_array) - && (b.sampler_filter_minmax == vk::FALSE - || a.sampler_filter_minmax != b.sampler_filter_minmax) - && (b.scalar_block_layout == vk::FALSE - || a.scalar_block_layout != b.scalar_block_layout) - && (b.imageless_framebuffer == vk::FALSE - || a.imageless_framebuffer != b.imageless_framebuffer) - && (b.uniform_buffer_standard_layout == vk::FALSE - || a.uniform_buffer_standard_layout != b.uniform_buffer_standard_layout) - && (b.shader_subgroup_extended_types == vk::FALSE - || a.shader_subgroup_extended_types != b.shader_subgroup_extended_types) - && (b.separate_depth_stencil_layouts == vk::FALSE - || a.separate_depth_stencil_layouts != b.separate_depth_stencil_layouts) - && (b.host_query_reset == vk::FALSE || a.host_query_reset != b.host_query_reset) - && (b.timeline_semaphore == vk::FALSE - || a.timeline_semaphore != b.timeline_semaphore) - && (b.buffer_device_address == vk::FALSE - || a.buffer_device_address != b.buffer_device_address) - && (b.buffer_device_address_capture_replay == vk::FALSE - || a.buffer_device_address_capture_replay - != b.buffer_device_address_capture_replay) - && (b.buffer_device_address_multi_device == vk::FALSE - || a.buffer_device_address_multi_device != b.buffer_device_address_multi_device) - && (b.vulkan_memory_model == vk::FALSE - || a.vulkan_memory_model != b.vulkan_memory_model) - && (b.vulkan_memory_model_device_scope == vk::FALSE - || a.vulkan_memory_model_device_scope != b.vulkan_memory_model_device_scope) - && (b.vulkan_memory_model_availability_visibility_chains == vk::FALSE - || a.vulkan_memory_model_availability_visibility_chains - != b.vulkan_memory_model_availability_visibility_chains) - && (b.shader_output_viewport_index == vk::FALSE - || a.shader_output_viewport_index != b.shader_output_viewport_index) - && (b.shader_output_layer == vk::FALSE - || a.shader_output_layer != b.shader_output_layer) - && (b.subgroup_broadcast_dynamic_id == vk::FALSE - || a.subgroup_broadcast_dynamic_id != b.subgroup_broadcast_dynamic_id) - } - - fn core13_superset_of( - a: &vk::PhysicalDeviceVulkan13Features, - b: &vk::PhysicalDeviceVulkan13Features, - ) -> bool { - (b.robust_image_access == vk::FALSE || a.robust_image_access != b.robust_image_access) - && (b.inline_uniform_block == vk::FALSE - || a.inline_uniform_block != b.inline_uniform_block) - && (b.descriptor_binding_inline_uniform_block_update_after_bind == vk::FALSE - || a.descriptor_binding_inline_uniform_block_update_after_bind - != b.descriptor_binding_inline_uniform_block_update_after_bind) - && (b.pipeline_creation_cache_control == vk::FALSE - || a.pipeline_creation_cache_control != b.pipeline_creation_cache_control) - && (b.private_data == vk::FALSE || a.private_data != b.private_data) - && (b.shader_demote_to_helper_invocation == vk::FALSE - || a.shader_demote_to_helper_invocation != b.shader_demote_to_helper_invocation) - && (b.shader_terminate_invocation == vk::FALSE - || a.shader_terminate_invocation != b.shader_terminate_invocation) - && (b.subgroup_size_control == vk::FALSE - || a.subgroup_size_control != b.subgroup_size_control) - && (b.compute_full_subgroups == vk::FALSE - || a.compute_full_subgroups != b.compute_full_subgroups) - && (b.synchronization2 == vk::FALSE || a.synchronization2 != b.synchronization2) - && (b.texture_compression_astc_hdr == vk::FALSE - || a.texture_compression_astc_hdr != b.texture_compression_astc_hdr) - && (b.shader_zero_initialize_workgroup_memory == vk::FALSE - || a.shader_zero_initialize_workgroup_memory - != b.shader_zero_initialize_workgroup_memory) - && (b.dynamic_rendering == vk::FALSE || a.dynamic_rendering != b.dynamic_rendering) - && (b.shader_integer_dot_product == vk::FALSE - || a.shader_integer_dot_product != b.shader_integer_dot_product) - && (b.maintenance4 == vk::FALSE || a.maintenance4 != b.maintenance4) - } - - fn mesh_shader_superset_of( - a: &vk::PhysicalDeviceMeshShaderFeaturesEXT, - b: &vk::PhysicalDeviceMeshShaderFeaturesEXT, - ) -> bool { - (b.task_shader == vk::FALSE || a.task_shader != b.task_shader) - && (b.mesh_shader == vk::FALSE || a.mesh_shader != b.mesh_shader) - && (b.multiview_mesh_shader == vk::FALSE - || a.multiview_mesh_shader != b.multiview_mesh_shader) - && (b.primitive_fragment_shading_rate_mesh_shader == vk::FALSE - || a.primitive_fragment_shading_rate_mesh_shader - != b.primitive_fragment_shading_rate_mesh_shader) - && (b.mesh_shader_queries == vk::FALSE - || a.mesh_shader_queries != b.mesh_shader_queries) - } - - core_superset_of(&self.core, &other.core) - && core11_superset_of(&self.core11, &other.core11) - && core12_superset_of(&self.core12, &other.core12) - && core13_superset_of(&self.core13, &other.core13) - && match (&self.mesh_shader, &other.mesh_shader) { - (Some(a), Some(b)) => mesh_shader_superset_of(a, b), - (None, Some(_)) => false, - _ => true, - } - } -} - -#[derive(Debug, Default)] -pub struct PhysicalDeviceProperties { - /// Extensions supported by the device, as reported by `enumerate_device_extension_properties`. - supported_extensions: Vec, - /// Vulkan 1.0 properties. - core: vk::PhysicalDeviceProperties, - /// Vulkan 1.1 properties. - core11: vk::PhysicalDeviceVulkan11Properties<'static>, - /// Vulkan 1.2 properties. - core12: vk::PhysicalDeviceVulkan12Properties<'static>, - /// Vulkan 1.3 properties. - core13: vk::PhysicalDeviceVulkan13Properties<'static>, - /// Mesh shader properties from the `VK_EXT_mesh_shader` extension, if supported. - mesh_shader: Option>, - /// Memory properties of the device, as reported by `get_physical_device_memory_properties`. - memory_properties: vk::PhysicalDeviceMemoryProperties, - /// The API version supported by the device. - device_api_version: u32, -} - -impl PhysicalDeviceProperties { - pub(crate) fn get_device_local_memory_count(&self) -> u64 { - self.memory_properties - .memory_heaps - .iter() - .filter(|heap| heap.flags.contains(vk::MemoryHeapFlags::DEVICE_LOCAL)) - .map(|heap| heap.size) - .sum::() - } -} - -fn get_physical_device_features( - instance: &InstanceInner, - pdev: vk::PhysicalDevice, - properties: &PhysicalDeviceProperties, -) -> PhysicalDeviceFeatures { - let mut features = PhysicalDeviceFeatures::default(); - let mut features2 = vk::PhysicalDeviceFeatures2::default(); - - features2 = features2 - .push_next(&mut features.core11) - .push_next(&mut features.core12) - .push_next(&mut features.core13); - - if properties.supports_extension(make_extension!(ext::mesh_shader)) { - features2 = features2.push_next( - features - .mesh_shader - .insert(vk::PhysicalDeviceMeshShaderFeaturesEXT::default()), - ); - } - - unsafe { - instance - .raw - .get_physical_device_features2(pdev, &mut features2); - } - - features.core = features2.features; - - features -} - -fn get_physical_device_properties( - instance: &InstanceInner, - pdev: vk::PhysicalDevice, -) -> Result { - let mut props = PhysicalDeviceProperties::default(); - props.core = unsafe { instance.raw.get_physical_device_properties(pdev) }; - props.device_api_version = props.core.api_version; - props.supported_extensions = - unsafe { instance.raw.enumerate_device_extension_properties(pdev)? }; - props.memory_properties = unsafe { instance.raw.get_physical_device_memory_properties(pdev) }; - - let supports_mesh_shader = props.supports_extension(make_extension!(ext::mesh_shader)); - - // TODO: fallibly check anything beyond 1.0 - let mut props2 = vk::PhysicalDeviceProperties2::default() - .push_next(&mut props.core11) - .push_next(&mut props.core12) - .push_next(&mut props.core13); - - if supports_mesh_shader { - let next = props - .mesh_shader - .insert(vk::PhysicalDeviceMeshShaderPropertiesEXT::default()); - props2 = props2.push_next(next); - } - - unsafe { - instance - .raw - .get_physical_device_properties2(pdev, &mut props2); - } - - Ok(props) -} - -#[allow(dead_code)] -pub(crate) fn extension_intersection<'a>( - supported: &'a [vk::ExtensionProperties], - required: &[Extension<'a>], -) -> Vec { - supported - .iter() - .filter(|ext| { - required.iter().any(|req| { - ext.extension_name_as_c_str() == Ok(req.name) && ext.spec_version >= req.version - }) - }) - .cloned() - .collect() -} - -impl PhysicalDeviceProperties { - pub(crate) fn supports_extension(&self, e: Extension) -> bool { - self.supported_extensions - .iter() - .any(|ext| ext.extension_name_as_c_str() == Ok(e.name) && ext.spec_version >= e.version) - } -} - -pub(crate) use instance::DebugUtils; -pub use instance::{DebugUtilsCreateInfo, Instance}; pub struct SamplerCache { device: Device, @@ -923,7 +345,9 @@ impl EguiState { } } +#[derive(renderer_macros::DeviceRef)] pub struct Renderer2 { + #[device] pub device: Device, pub samplers: SamplerCache, pub texture_manager: texture::TextureManager, @@ -1036,7 +460,7 @@ impl Renderer2 { let future = cmds.submit( Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)), Some(frame.release), - Arc::new(sync::Fence::from_pool(&self.device.pools.fences, None)?), + Arc::new(sync::Fence::from_pool(&self.device.pools().fences, None)?), )?; future.await; @@ -1052,8 +476,7 @@ impl Renderer2 { pub use vk::Extent2D; use crate::{ - device::Extension, - instance::{InstanceDesc, InstanceInner}, + instance::InstanceDesc, }; pub mod utils { diff --git a/crates/renderer/src/pipeline.rs b/crates/renderer/src/pipeline.rs index 2269dbf..ee29070 100644 --- a/crates/renderer/src/pipeline.rs +++ b/crates/renderer/src/pipeline.rs @@ -5,7 +5,7 @@ use parking_lot::Mutex; use crate::{ device::{ - Device, DeviceHandle, DeviceInner, DeviceObject, asdf::traits::ExternallyManagedObject, + Device, DeviceObject, }, make_extension, }; @@ -229,20 +229,6 @@ pub struct DescriptorSetAllocDesc<'a> { pub layout: &'a DescriptorSetLayout, } -impl DeviceHandle for vk::DescriptorPool { - unsafe fn destroy(&mut self, device: &Device) { - unsafe { device.dev().destroy_descriptor_pool(*self, None) }; - } -} -impl> ExternallyManagedObject for vk::DescriptorPool { - unsafe fn destroy(self, device: &T) { - // SAFETY: We have exclusive ownership of the descriptor pool, so it's safe to destroy it. - unsafe { - device.as_ref().raw.destroy_descriptor_pool(self, None); - } - } -} - #[derive(Debug)] pub struct DescriptorPool { pool: DeviceObject, @@ -300,17 +286,6 @@ impl DescriptorPool { } } -impl> ExternallyManagedObject for vk::DescriptorSetLayout { - unsafe fn destroy(self, device: &T) { - unsafe { - device - .as_ref() - .raw - .destroy_descriptor_set_layout(self, None); - } - } -} - #[derive(Debug)] pub struct DescriptorSetLayout { layout: DeviceObject, @@ -360,14 +335,6 @@ impl DescriptorSetLayout { } } -impl> ExternallyManagedObject for vk::PipelineLayout { - unsafe fn destroy(self, device: &T) { - unsafe { - device.as_ref().raw.destroy_pipeline_layout(self, None); - } - } -} - #[derive(Debug)] pub struct PipelineLayout { layout: DeviceObject, @@ -457,17 +424,6 @@ impl std::hash::Hash for SamplerDesc { } } -impl ExternallyManagedObject for vk::Sampler -where - T: AsRef, -{ - unsafe fn destroy(self, device: &T) { - unsafe { - device.as_ref().raw.destroy_sampler(self, None); - } - } -} - #[derive(Debug)] pub struct Sampler { sampler: DeviceObject, @@ -505,14 +461,6 @@ impl Sampler { } } -impl> ExternallyManagedObject for vk::ShaderModule { - unsafe fn destroy(self, device: &T) { - unsafe { - device.as_ref().raw.destroy_shader_module(self, None); - } - } -} - #[derive(Debug)] pub struct ShaderModule { module: DeviceObject, @@ -557,14 +505,6 @@ pub struct Pipeline { layout: Arc, } -impl> ExternallyManagedObject for vk::Pipeline { - unsafe fn destroy(self, device: &T) { - unsafe { - device.as_ref().raw.destroy_pipeline(self, None); - } - } -} - impl Pipeline { pub fn new_compute(device: Device, desc: ComputePipelineDesc) -> crate::Result { let info = &vk::ComputePipelineCreateInfo::default() @@ -581,7 +521,7 @@ impl Pipeline { device .dev() .create_compute_pipelines( - device.pools.pipeline_cache.raw, + device.pools().pipeline_cache.raw, core::slice::from_ref(info), None, ) @@ -750,7 +690,7 @@ impl Pipeline { device .dev() .create_graphics_pipelines( - device.pools.pipeline_cache.raw, + device.pools().pipeline_cache.raw, core::slice::from_ref(&info), None, ) @@ -777,122 +717,3 @@ impl Pipeline { &self.layout } } - -pub(crate) mod pipeline_cache { - use std::sync::Arc; - - use ash::vk; - - use ash::Device; - - use crate::PhysicalDeviceInfo; - use crate::device::DeviceInner; - - #[derive(Debug)] - pub struct PipelineCache { - #[allow(dead_code)] - key: u128, - pub(crate) raw: vk::PipelineCache, - } - - impl crate::device::asdf::traits::ExternallyManagedObject> for PipelineCache { - unsafe fn destroy(self, owner: &Arc) { - tracing::info!("destroying pipeline cache with key {:x}", self.key); - if let Ok(data) = self.export(&owner.raw) { - tracing::info!( - "exported pipeline cache with key {:x} and size {} bytes", - self.key, - data.len() - ); - _ = Self::write_to_disk(self.key, &data).inspect_err(|err| { - tracing::error!("failed to write pipeline cache to disk: {err}"); - }); - } - unsafe { owner.raw.destroy_pipeline_cache(self.raw, None) }; - } - } - - impl PipelineCache { - const MAGIC: [u8; 4] = *b"VYPC"; - const KEY_VERSION: u32 = 1; - const PATH: &'static str = "pipeline_cache.bin"; - fn calculate_key(adapter: &PhysicalDeviceInfo) -> u128 { - use md5::Digest; - let mut hasher = md5::Md5::new(); - let props = &adapter.properties; - hasher.update(bytemuck::bytes_of(&[ - props.core.vendor_id, - props.core.api_version, - props.core.device_id, - props.core.driver_version, - ])); - u128::from_le_bytes(hasher.finalize().into()) - } - - fn load_from_disk(key: u128) -> Option<(u128, Vec)> { - use std::io::Read; - - let file = std::fs::File::open(Self::PATH).ok()?; - let mut reader = std::io::BufReader::new(file); - let mut magic = [0; 4]; - reader.read_exact(&mut magic).ok()?; - if magic != Self::MAGIC { - return None; - } - - let mut version = 0; - reader - .read_exact(bytemuck::bytes_of_mut(&mut version)) - .ok()?; - if version != Self::KEY_VERSION { - return None; - } - - let mut disk_key = 0; - reader - .read_exact(bytemuck::bytes_of_mut(&mut disk_key)) - .ok()?; - if disk_key != key { - return None; - } - - let mut data = Vec::new(); - reader.read_to_end(&mut data).ok()?; - - Some((key, data)) - } - - fn write_to_disk(key: u128, data: &[u8]) -> std::io::Result<()> { - use std::io::Write; - - let file = std::fs::File::create(Self::PATH)?; - let mut writer = std::io::BufWriter::new(file); - writer.write_all(&Self::MAGIC)?; - writer.write_all(bytemuck::bytes_of(&Self::KEY_VERSION))?; - writer.write_all(bytemuck::bytes_of(&key))?; - writer.write_all(data)?; - Ok(()) - } - - pub fn new(device: &Device, adapter: &PhysicalDeviceInfo) -> crate::Result { - let key = Self::calculate_key(adapter); - let data = Self::load_from_disk(key).map(|(key, data)| { - tracing::info!("loaded pipeline cache from disk with key {key:x}"); - data - }); - - let info = vk::PipelineCacheCreateInfo::default() - // .flags(vk::PipelineCacheCreateFlags::EXTERNALLY_SYNCHRONIZED) - .initial_data(data.as_deref().unwrap_or_default()); - - let cache = unsafe { device.create_pipeline_cache(&info, None)? }; - - Ok(Self { key, raw: cache }) - } - - pub fn export(&self, device: &ash::Device) -> crate::Result> { - let data = unsafe { device.get_pipeline_cache_data(self.raw)? }; - Ok(data) - } - } -} diff --git a/crates/renderer/src/rendering/mod.rs b/crates/renderer/src/rendering/mod.rs index 6d0ef0f..7c4d30d 100644 --- a/crates/renderer/src/rendering/mod.rs +++ b/crates/renderer/src/rendering/mod.rs @@ -168,7 +168,7 @@ impl Wireframe { let future = cmd.submit_async( None, None, - Arc::new(sync::Fence::from_pool(&dev.pools.fences, None)?), + Arc::new(sync::Fence::from_pool(&dev.pools().fences, None)?), )?; let pipeline = Self::create_pipeline(dev.clone())?; diff --git a/crates/renderer/src/swapchain.rs b/crates/renderer/src/swapchain.rs index 2a2152f..52ec4f9 100644 --- a/crates/renderer/src/swapchain.rs +++ b/crates/renderer/src/swapchain.rs @@ -147,7 +147,12 @@ impl Surface { adapter: &PhysicalDeviceInfo, config: &mut SwapchainConfiguration, ) -> Result { - let surface_caps = instance.get_adapter_surface_capabilities(adapter.pdev, self, None)?; + let surface_caps = instance.get_adapter_surface_capabilities( + adapter.pdev, + &self.functor, + self.raw, + None, + )?; let max_image_dim = adapter.properties.core.limits.max_image_dimension2_d; if config.extent.width > max_image_dim || config.extent.height > max_image_dim { @@ -203,7 +208,8 @@ impl Surface { // capabilities to get the correct image counts. let surface_caps = instance.get_adapter_surface_capabilities( adapter.pdev, - self, + &self.functor, + self.raw, Some(config.present_mode), )?; @@ -240,7 +246,12 @@ impl Surface { instance: &Instance, adapter: &PhysicalDeviceInfo, ) -> Result { - let surface_caps = instance.get_adapter_surface_capabilities(adapter.pdev, self, None)?; + let surface_caps = instance.get_adapter_surface_capabilities( + adapter.pdev, + &self.functor, + self.raw, + None, + )?; let present_mode = surface_caps .present_modes @@ -531,7 +542,7 @@ impl Swapchain { tracing::trace!(frame, "acquiring image for frame {frame}"); async move { - let fence = Fence::from_pool(&self.swapchain.device().pools.fences, None)?; + let fence = Fence::from_pool(&self.swapchain.device().pools().fences, None)?; let acquire = self.acquire_semaphores[frame]; // spawn on threadpool because it might block. @@ -832,7 +843,7 @@ mod tests { let surface = Arc::new(Surface::headless(&instance)?); - let adapter = instance.choose_adapter_default(Some(&surface), &[], Some(&features))?; + let adapter = instance.choose_adapter_default(Some(surface.raw), &[], Some(&features))?; let device = adapter.create_logical_device(&instance, &[], features, None)?; surface.configure( diff --git a/crates/renderer/src/util.rs b/crates/renderer/src/util.rs index be727a7..d1433e9 100644 --- a/crates/renderer/src/util.rs +++ b/crates/renderer/src/util.rs @@ -1,5 +1,4 @@ use std::{ - borrow::Cow, mem::ManuallyDrop, ops::{Deref, DerefMut}, }; @@ -94,62 +93,6 @@ impl Drop for DropGuard { } } -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct DebugName { - #[cfg(debug_assertions)] - name: Cow<'static, str>, -} - -impl core::fmt::Display for DebugName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", &**self) - } -} - -impl core::fmt::Debug for DebugName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", &**self) - } -} - -impl core::ops::Deref for DebugName { - type Target = str; - - fn deref(&self) -> &Self::Target { - #[cfg(debug_assertions)] - return &self.name; - #[cfg(not(debug_assertions))] - return "FEATURE_DISABLED"; - } -} - -impl From> for DebugName { - fn from(_value: Cow<'static, str>) -> Self { - Self { - #[cfg(debug_assertions)] - name: _value, - } - } -} - -impl From<&'static str> for DebugName { - fn from(_value: &'static str) -> Self { - Self { - #[cfg(debug_assertions)] - name: Cow::Borrowed(_value), - } - } -} - -impl From for DebugName { - fn from(_value: String) -> Self { - Self { - #[cfg(debug_assertions)] - name: Cow::Owned(_value), - } - } -} - #[macro_export] macro_rules! def_monotonic_id { ($vis:vis $ty:ident) => { diff --git a/crates/renderer_device/Cargo.toml b/crates/renderer_device/Cargo.toml new file mode 100644 index 0000000..8cc57b1 --- /dev/null +++ b/crates/renderer_device/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "renderer_device" +version = "0.1.0" +edition = "2024" + +[dependencies] +ash = { workspace = true } +ash-window = { workspace = true } +gpu-allocator = { workspace = true } +parking_lot = { workspace = true } +tracing = { workspace = true } +raw-window-handle = { workspace = true } +bitflags = { workspace = true } +tinyvec = { workspace = true } +thiserror = { workspace = true } +md-5 = { workspace = true } +crossbeam = "0.8.4" +dyn-clone = "1" +bytemuck = { version = "1.21.0", features = ["derive"] } + +[dev-dependencies] +renderer_macros = { path = "../renderer/renderer_macros" } diff --git a/crates/renderer/src/debug.rs b/crates/renderer_device/src/debug.rs similarity index 100% rename from crates/renderer/src/debug.rs rename to crates/renderer_device/src/debug.rs diff --git a/crates/renderer/src/device.rs b/crates/renderer_device/src/device.rs similarity index 87% rename from crates/renderer/src/device.rs rename to crates/renderer_device/src/device.rs index 32395fa..f1677b3 100644 --- a/crates/renderer/src/device.rs +++ b/crates/renderer_device/src/device.rs @@ -14,10 +14,67 @@ use ash::{ use parking_lot::Mutex; use raw_window_handle::RawDisplayHandle; +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct DebugName { + #[cfg(debug_assertions)] + name: Cow<'static, str>, +} + +impl core::fmt::Display for DebugName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &**self) + } +} + +impl core::fmt::Debug for DebugName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &**self) + } +} + +impl core::ops::Deref for DebugName { + type Target = str; + + fn deref(&self) -> &Self::Target { + #[cfg(debug_assertions)] + return &self.name; + #[cfg(not(debug_assertions))] + return "FEATURE_DISABLED"; + } +} + +impl From> for DebugName { + fn from(_value: Cow<'static, str>) -> Self { + Self { + #[cfg(debug_assertions)] + name: _value, + } + } +} + +impl From<&'static str> for DebugName { + fn from(_value: &'static str) -> Self { + Self { + #[cfg(debug_assertions)] + name: Cow::Borrowed(_value), + } + } +} + +impl From for DebugName { + fn from(_value: String) -> Self { + Self { + #[cfg(debug_assertions)] + name: Cow::Owned(_value), + } + } +} + + use crate::{ Instance, PhysicalDeviceFeatures, PhysicalDeviceInfo, Result, device::asdf::traits::ExternallyManagedObject, - pipeline::pipeline_cache::PipelineCache, + pipeline_cache::PipelineCache, queue::{DeviceQueueInfos, DeviceQueues, Queue}, sync::{self, BinarySemaphore, TimelineSemaphore}, }; @@ -46,11 +103,11 @@ impl Drop for DeviceDrop { } } -pub(crate) struct DeviceExtensions { - pub(crate) debug_utils: ext::debug_utils::Device, - pub(crate) swapchain: Option, +pub struct DeviceExtensions { + pub debug_utils: ext::debug_utils::Device, + pub swapchain: Option, #[allow(dead_code)] - pub(crate) mesh_shader: Option, + pub mesh_shader: Option, } type GpuAllocation = gpu_allocator::vulkan::Allocation; @@ -75,7 +132,7 @@ pub enum Allocation { } impl Allocation { - pub(crate) fn allocation(&self) -> Option<&GpuAllocation> { + pub fn allocation(&self) -> Option<&GpuAllocation> { match self { Allocation::Owned(obj) => Some(obj), Allocation::Shared(arc) => Some(arc.as_ref()), @@ -83,7 +140,7 @@ impl Allocation { } } - pub(crate) fn allocation_mut(&mut self) -> Option<&mut GpuAllocation> { + pub fn allocation_mut(&mut self) -> Option<&mut GpuAllocation> { match self { Allocation::Owned(obj) => Some(obj), Allocation::Shared(arc) => Arc::get_mut(arc).map(DerefMut::deref_mut), @@ -93,15 +150,15 @@ impl Allocation { } pub struct DeviceInner { - pub(crate) alloc2: Mutex, - pub(crate) raw: ash::Device, - pub(crate) adapter: PhysicalDeviceInfo, - pub(crate) instance: Instance, - pub(crate) queues: DeviceQueues, - pub(crate) sync_threadpool: sync::SyncThreadpool, - pub(crate) device_extensions: DeviceExtensions, + pub alloc2: Mutex, + pub raw: ash::Device, + pub adapter: PhysicalDeviceInfo, + pub instance: Instance, + pub queues: DeviceQueues, + pub sync_threadpool: sync::SyncThreadpool, + pub device_extensions: DeviceExtensions, #[allow(dead_code)] - pub(crate) enabled_extensions: Vec<&'static CStr>, + pub enabled_extensions: Vec<&'static CStr>, _drop: DeviceDrop, } @@ -150,7 +207,7 @@ impl<'a> std::hash::Hash for Extension<'a> { } } -pub(crate) fn get_available_extensions( +pub fn get_available_extensions( entry: &ash::Entry, layers: &[&CStr], ) -> Result> { @@ -170,7 +227,7 @@ pub(crate) fn get_available_extensions( } /// returns a tuple of supported-or-enabled extensions and unsupported-and-requested extensions -pub(crate) fn get_extensions<'a>( +pub fn get_extensions<'a>( entry: &ash::Entry, layers: &[&'a CStr], mut extensions: Vec>, @@ -288,7 +345,7 @@ pub(crate) fn get_extensions<'a>( } /// returns a list of enabled, or a tuple of enabled and unsupported but requested layers. -pub(crate) fn get_layers<'a>( +pub fn get_layers<'a>( entry: &ash::Entry, wants_layers: Vec<&'a CStr>, ) -> core::result::Result, (Vec<&'a CStr>, Vec<&'a CStr>)> { @@ -438,11 +495,11 @@ impl PhysicalDeviceInfo { } #[derive(Debug)] -pub(crate) struct DevicePools { - pub(crate) pipeline_cache: asdf::DeviceObject>, - pub(crate) fences: Arc>, - pub(crate) binary_semaphores: Pool, - pub(crate) timeline_semaphores: Pool, +pub struct DevicePools { + pub pipeline_cache: asdf::DeviceObject>, + pub fences: Arc>, + pub binary_semaphores: Pool, + pub timeline_semaphores: Pool, } impl AsRef for DevicePools { @@ -465,10 +522,44 @@ impl DevicePools { } } +pub trait DeviceRef { + fn device(&self) -> &Device; +} + +impl DeviceRef for Device { + fn device(&self) -> &Device { + self + } +} + +impl DeviceRef for Arc { + fn device(&self) -> &Device { + self.as_ref() + } +} + +impl DeviceRef for &Device { + fn device(&self) -> &Device { + self + } +} + +impl DeviceRef for &Arc { + fn device(&self) -> &Device { + self.as_ref() + } +} + #[derive(Clone, Debug)] pub struct Device { pub(crate) shared: Arc, - pub(crate) pools: Arc, + pools: Arc, +} + +impl Device { + pub fn pools(&self) -> &Arc { + &self.pools + } } impl PartialEq for Device { @@ -516,7 +607,7 @@ impl DeviceInner { pub fn features(&self) -> &crate::PhysicalDeviceFeatures { &self.adapter.features } - pub(crate) fn properties(&self) -> &crate::PhysicalDeviceProperties { + pub fn properties(&self) -> &crate::PhysicalDeviceProperties { &self.adapter.properties } pub fn physical_device(&self) -> &PhysicalDeviceInfo { @@ -627,8 +718,8 @@ impl DeviceInner { #[derive(Clone)] pub struct DeviceOwnedDebugObject { - pub(crate) device: Device, - pub(crate) object: T, + pub device: Device, + pub object: T, #[cfg(debug_assertions)] name: Option>, } @@ -716,6 +807,60 @@ impl> ExternallyManagedObject for vk::Buffer { } } +impl> ExternallyManagedObject for vk::Image { + unsafe fn destroy(self, device: &T) { + unsafe { device.as_ref().raw.destroy_image(self, None) }; + } +} + +impl> ExternallyManagedObject for vk::ImageView { + unsafe fn destroy(self, device: &T) { + unsafe { device.as_ref().raw.destroy_image_view(self, None) }; + } +} + +impl DeviceHandle for vk::DescriptorPool { + unsafe fn destroy(&mut self, device: &Device) { + unsafe { device.dev().destroy_descriptor_pool(*self, None) }; + } +} + +impl> ExternallyManagedObject for vk::DescriptorPool { + unsafe fn destroy(self, device: &T) { + unsafe { device.as_ref().raw.destroy_descriptor_pool(self, None) }; + } +} + +impl> ExternallyManagedObject for vk::DescriptorSetLayout { + unsafe fn destroy(self, device: &T) { + unsafe { device.as_ref().raw.destroy_descriptor_set_layout(self, None) }; + } +} + +impl> ExternallyManagedObject for vk::PipelineLayout { + unsafe fn destroy(self, device: &T) { + unsafe { device.as_ref().raw.destroy_pipeline_layout(self, None) }; + } +} + +impl> ExternallyManagedObject for vk::Sampler { + unsafe fn destroy(self, device: &T) { + unsafe { device.as_ref().raw.destroy_sampler(self, None) }; + } +} + +impl> ExternallyManagedObject for vk::ShaderModule { + unsafe fn destroy(self, device: &T) { + unsafe { device.as_ref().raw.destroy_shader_module(self, None) }; + } +} + +impl> ExternallyManagedObject for vk::Pipeline { + unsafe fn destroy(self, device: &T) { + unsafe { device.as_ref().raw.destroy_pipeline(self, None) }; + } +} + impl> ExternallyManagedObject for vk::SwapchainKHR { unsafe fn destroy(self, device: &T) { unsafe { @@ -737,8 +882,8 @@ pub trait Pooled: Sized { #[derive(Debug)] pub struct Pool { - pub(crate) pool: Mutex>, - pub(crate) device: Arc, + pub pool: Mutex>, + pub device: Arc, } impl Pool { @@ -850,7 +995,7 @@ macro_rules! define_device_owned_handle { // This module is an experiment in a more generic way to manage device-owned resources. // #[cfg(false)] -pub(crate) mod asdf { +pub mod asdf { use std::{ mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, @@ -860,7 +1005,7 @@ pub(crate) mod asdf { use ash::vk; use ash::vk::Handle; - use crate::{device::DeviceInner, util::DebugName}; + use crate::device::{DebugName, DeviceInner}; pub mod traits { /// A trait describing an object owned by some manager-type, which is @@ -961,7 +1106,6 @@ pub(crate) mod asdf { pub type InnerDeviceObject = DeviceObject>; /// A wrapper for vulkan types which are owned by the device, taking care of destruction. - #[derive(Debug)] pub struct DeviceObject< T: traits::ExternallyManagedObject, O: AsRef = super::Device, diff --git a/crates/renderer/src/instance.rs b/crates/renderer_device/src/instance.rs similarity index 94% rename from crates/renderer/src/instance.rs rename to crates/renderer_device/src/instance.rs index 60945ff..ba17fab 100644 --- a/crates/renderer/src/instance.rs +++ b/crates/renderer_device/src/instance.rs @@ -13,7 +13,6 @@ use crate::{ Error, PhysicalDeviceFeatures, PhysicalDeviceInfo, SurfaceCapabilities, device::{Extension, get_extensions, get_layers}, get_physical_device_features, get_physical_device_properties, make_extension, - swapchain::Surface, }; pub struct DebugUtilsCreateInfo { @@ -21,21 +20,21 @@ pub struct DebugUtilsCreateInfo { pub message_type: vk::DebugUtilsMessageTypeFlagsEXT, } -pub(crate) struct InstanceExtensions { - pub(crate) get_surface_capabilities2: Option, - pub(crate) surface_maintenance1: Option<()>, +pub struct InstanceExtensions { + pub get_surface_capabilities2: Option, + pub surface_maintenance1: Option<()>, } pub struct InstanceInner { pub raw: ash::Instance, - pub(crate) extensions: InstanceExtensions, + pub extensions: InstanceExtensions, pub entry: Entry, - pub(crate) _debug_utils: DebugUtils, + pub _debug_utils: DebugUtils, } #[derive(Clone)] pub struct Instance { - pub(crate) inner: Arc, + pub inner: Arc, } impl Deref for Instance { @@ -255,28 +254,23 @@ impl Instance { /// extensions are available, it will also query present mode compatibility /// information to filter the present modes based on the provided initial /// mode. - pub(crate) fn get_adapter_surface_capabilities( + pub fn get_adapter_surface_capabilities( &self, pdev: vk::PhysicalDevice, - surface: &Surface, + functor: &khr::surface::Instance, + raw: vk::SurfaceKHR, present_mode: Option, ) -> crate::Result { let formats = unsafe { - surface - .functor - .get_physical_device_surface_formats(pdev, surface.raw)? + functor.get_physical_device_surface_formats(pdev, raw)? }; let present_modes = unsafe { - surface - .functor - .get_physical_device_surface_present_modes(pdev, surface.raw)? + functor.get_physical_device_surface_present_modes(pdev, raw)? }; let generic_caps = unsafe { - surface - .functor - .get_physical_device_surface_capabilities(pdev, surface.raw)? + functor.get_physical_device_surface_capabilities(pdev, raw)? }; let (capabilities, present_modes) = if let Some(ref functor) = @@ -285,7 +279,7 @@ impl Instance { { let mut capabilities = vk::SurfaceCapabilities2KHR::default(); let mut surface_info = - vk::PhysicalDeviceSurfaceInfo2KHR::default().surface(surface.raw()); + vk::PhysicalDeviceSurfaceInfo2KHR::default().surface(raw); let present_mode = present_mode.unwrap_or(present_modes[0]); let mut surface_present_mode = @@ -324,7 +318,7 @@ impl Instance { }) } - pub(crate) fn expose_adapter( + pub fn expose_adapter( &self, pdev: vk::PhysicalDevice, ) -> crate::Result { @@ -338,9 +332,9 @@ impl Instance { }) } - pub(crate) fn choose_adapter_default( + pub fn choose_adapter_default( &self, - surface: Option<&Surface>, + surface: Option, required_extensions: &[Extension], required_features: Option<&PhysicalDeviceFeatures>, ) -> crate::Result { @@ -453,7 +447,7 @@ impl Instance { } /// So... basically this probably doesn't matter because the graphics queue will always be a present queue as well... - pub(crate) fn query_presentation_support( + pub fn query_presentation_support( &self, pdev: vk::PhysicalDevice, queue_family: u32, @@ -497,7 +491,7 @@ impl Instance { } } -pub(crate) struct DebugUtils { +pub struct DebugUtils { pub instance: ext::debug_utils::Instance, pub messenger: vk::DebugUtilsMessengerEXT, } diff --git a/crates/renderer_device/src/lib.rs b/crates/renderer_device/src/lib.rs new file mode 100644 index 0000000..288dbe9 --- /dev/null +++ b/crates/renderer_device/src/lib.rs @@ -0,0 +1,626 @@ +#![feature(debug_closure_helpers, slice_partition_dedup)] + +extern crate self as renderer_device; + +use std::{fmt::Debug, path::PathBuf}; +use ash::{ext, vk::{self, SurfaceCapabilitiesKHR}}; +use dyn_clone::DynClone; +use crate::instance::InstanceInner; + +pub mod device; +pub mod instance; +pub mod pipeline_cache; +pub mod queue; +pub mod sync; +mod debug; + +pub use device::{Device, DeviceOwned, DeviceRef, Extension}; +pub use instance::{DebugUtils, DebugUtilsCreateInfo, Instance}; + +#[cfg(test)] +mod device_ref_derive_tests { + #![allow(dead_code)] + + use crate::device::{Device, DeviceRef}; + use std::sync::Arc; + + #[derive(renderer_macros::DeviceRef)] + struct Wrapper { + #[device] + device: Device, + count: u32, + } + + #[derive(renderer_macros::DeviceRef)] + struct ArcWrapper { + #[device] + device: Arc, + } + + #[derive(renderer_macros::DeviceRef)] + struct GenericWrapper { + #[device] + device: D, + tag: &'static str, + } + + #[test] + fn derives_impl_device_ref() { + fn assert_impl() {} + assert_impl::(); + assert_impl::(); + assert_impl::>(); + assert_impl::>>(); + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("No Physical Device found for this instance.")] + NoAdapter, + #[error("Swapchain suboptimal.")] + SuboptimalSwapchain, + #[error(transparent)] + LoadingError(#[from] ash::LoadingError), + #[error(transparent)] + Result(#[from] ash::vk::Result), + #[error(transparent)] + CStrError(#[from] core::ffi::c_str::FromBytesUntilNulError), + #[error(transparent)] + Utf8Error(#[from] core::str::Utf8Error), + #[error(transparent)] + NulError(#[from] std::ffi::NulError), + #[error(transparent)] + GpuAllocatorError(#[from] gpu_allocator::AllocationError), + #[error("No Physical Device found.")] + NoPhysicalDevice, + #[error(transparent)] + Io(#[from] std::io::Error), + #[error( + "Image dimensions ({width}x{height}) exceed the maximum allowed size of {max_size}x{max_size}." + )] + ImageTooLarge { + width: u32, + height: u32, + max_size: u32, + }, + #[error("Image dimensions cannot be zero.")] + ImageZeroSized, + #[error("Incompatible image view kind {view_kind:?} for image kind {image_kind:?}.")] + IncompatibleImageViewKind { + view_kind: vk::ImageViewType, + image_kind: vk::ImageType, + }, + #[error("Asset missing at path {0:?}")] + AssetMissing(PathBuf), + #[error("Unspecified Error")] + Unspecified, + #[error("BEEP BOOP create an error variant for {0} BEEP BOOP")] + Todo(&'static str), +} + +pub type Result = core::result::Result; + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct DeviceExtension<'a> { + name: &'a core::ffi::CStr, + version: u32, +} + +/// returns true if lhs and rhs have the same name and lhs spec_version is less +/// than or equal to rhs spec_version +#[allow(dead_code)] +pub fn compatible_extension_properties( + lhs: &vk::ExtensionProperties, + rhs: &vk::ExtensionProperties, +) -> bool { + let Some(lhs_name) = lhs.extension_name_as_c_str().ok() else { + return false; + }; + let Some(rhs_name) = rhs.extension_name_as_c_str().ok() else { + return false; + }; + + if lhs_name == rhs_name { + lhs.spec_version <= rhs.spec_version + } else { + false + } +} + +pub trait ExtendsDeviceFeatures2Debug: + vk::ExtendsPhysicalDeviceFeatures2 + Debug + Send + Sync +{ +} +pub trait ExtendsDeviceProperties2Debug: + vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync +{ +} + +impl ExtendsDeviceFeatures2Debug + for T +{ +} +impl + ExtendsDeviceProperties2Debug for T +{ +} + +#[derive(Debug)] +pub struct PhysicalDeviceInfo { + pub pdev: vk::PhysicalDevice, + pub properties: PhysicalDeviceProperties, + pub features: PhysicalDeviceFeatures, +} + +#[derive(Debug)] +pub struct SurfaceCapabilities { + pub capabilities: SurfaceCapabilitiesKHR, + /// The minimum number of images that must be present in the swapchain for + /// the surface for any present mode supported by the surface. + pub min_image_count: u32, + pub formats: Vec, + pub present_modes: Vec, +} + +#[derive(Default, Debug)] +pub struct PhysicalDeviceFeatures { + pub core: vk::PhysicalDeviceFeatures, + pub core11: vk::PhysicalDeviceVulkan11Features<'static>, + pub core12: vk::PhysicalDeviceVulkan12Features<'static>, + pub core13: vk::PhysicalDeviceVulkan13Features<'static>, + pub mesh_shader: Option>, +} + +impl PhysicalDeviceFeatures { + pub fn push_to_device_create_info<'a>( + &'a mut self, + mut create_info: vk::DeviceCreateInfo<'a>, + ) -> vk::DeviceCreateInfo<'a> { + create_info = create_info + .enabled_features(&self.core) + .push_next(&mut self.core11) + .push_next(&mut self.core12) + .push_next(&mut self.core13); + + if let Some(mesh_shader) = &mut self.mesh_shader { + create_info = create_info.push_next(mesh_shader); + } + create_info + } + pub fn superset_of(&self, other: &PhysicalDeviceFeatures) -> bool { + fn core_superset_of( + a: &vk::PhysicalDeviceFeatures, + b: &vk::PhysicalDeviceFeatures, + ) -> bool { + (b.robust_buffer_access == vk::FALSE + || a.robust_buffer_access != b.robust_buffer_access) + && (b.full_draw_index_uint32 == vk::FALSE + || a.full_draw_index_uint32 != b.full_draw_index_uint32) + && (b.image_cube_array == vk::FALSE || a.image_cube_array != b.image_cube_array) + && (b.independent_blend == vk::FALSE || a.independent_blend != b.independent_blend) + && (b.geometry_shader == vk::FALSE || a.geometry_shader != b.geometry_shader) + && (b.tessellation_shader == vk::FALSE + || a.tessellation_shader != b.tessellation_shader) + && (b.sample_rate_shading == vk::FALSE + || a.sample_rate_shading != b.sample_rate_shading) + && (b.dual_src_blend == vk::FALSE || a.dual_src_blend != b.dual_src_blend) + && (b.logic_op == vk::FALSE || a.logic_op != b.logic_op) + && (b.multi_draw_indirect == vk::FALSE + || a.multi_draw_indirect != b.multi_draw_indirect) + && (b.draw_indirect_first_instance == vk::FALSE + || a.draw_indirect_first_instance != b.draw_indirect_first_instance) + && (b.depth_clamp == vk::FALSE || a.depth_clamp != b.depth_clamp) + && (b.depth_bias_clamp == vk::FALSE || a.depth_bias_clamp != b.depth_bias_clamp) + && (b.fill_mode_non_solid == vk::FALSE + || a.fill_mode_non_solid != b.fill_mode_non_solid) + && (b.depth_bounds == vk::FALSE || a.depth_bounds != b.depth_bounds) + && (b.wide_lines == vk::FALSE || a.wide_lines != b.wide_lines) + && (b.large_points == vk::FALSE || a.large_points != b.large_points) + && (b.alpha_to_one == vk::FALSE || a.alpha_to_one != b.alpha_to_one) + && (b.multi_viewport == vk::FALSE || a.multi_viewport != b.multi_viewport) + && (b.sampler_anisotropy == vk::FALSE + || a.sampler_anisotropy != b.sampler_anisotropy) + && (b.texture_compression_etc2 == vk::FALSE + || a.texture_compression_etc2 != b.texture_compression_etc2) + && (b.texture_compression_astc_ldr == vk::FALSE + || a.texture_compression_astc_ldr != b.texture_compression_astc_ldr) + && (b.texture_compression_bc == vk::FALSE + || a.texture_compression_bc != b.texture_compression_bc) + && (b.occlusion_query_precise == vk::FALSE + || a.occlusion_query_precise != b.occlusion_query_precise) + && (b.pipeline_statistics_query == vk::FALSE + || a.pipeline_statistics_query != b.pipeline_statistics_query) + && (b.vertex_pipeline_stores_and_atomics == vk::FALSE + || a.vertex_pipeline_stores_and_atomics != b.vertex_pipeline_stores_and_atomics) + && (b.fragment_stores_and_atomics == vk::FALSE + || a.fragment_stores_and_atomics != b.fragment_stores_and_atomics) + && (b.shader_tessellation_and_geometry_point_size == vk::FALSE + || a.shader_tessellation_and_geometry_point_size + != b.shader_tessellation_and_geometry_point_size) + && (b.shader_image_gather_extended == vk::FALSE + || a.shader_image_gather_extended != b.shader_image_gather_extended) + && (b.shader_storage_image_extended_formats == vk::FALSE + || a.shader_storage_image_extended_formats + != b.shader_storage_image_extended_formats) + && (b.shader_storage_image_multisample == vk::FALSE + || a.shader_storage_image_multisample != b.shader_storage_image_multisample) + && (b.shader_storage_image_read_without_format == vk::FALSE + || a.shader_storage_image_read_without_format + != b.shader_storage_image_read_without_format) + && (b.shader_storage_image_write_without_format == vk::FALSE + || a.shader_storage_image_write_without_format + != b.shader_storage_image_write_without_format) + && (b.shader_uniform_buffer_array_dynamic_indexing == vk::FALSE + || a.shader_uniform_buffer_array_dynamic_indexing + != b.shader_uniform_buffer_array_dynamic_indexing) + && (b.shader_sampled_image_array_dynamic_indexing == vk::FALSE + || a.shader_sampled_image_array_dynamic_indexing + != b.shader_sampled_image_array_dynamic_indexing) + && (b.shader_storage_buffer_array_dynamic_indexing == vk::FALSE + || a.shader_storage_buffer_array_dynamic_indexing + != b.shader_storage_buffer_array_dynamic_indexing) + && (b.shader_storage_image_array_dynamic_indexing == vk::FALSE + || a.shader_storage_image_array_dynamic_indexing + != b.shader_storage_image_array_dynamic_indexing) + && (b.shader_clip_distance == vk::FALSE + || a.shader_clip_distance != b.shader_clip_distance) + && (b.shader_cull_distance == vk::FALSE + || a.shader_cull_distance != b.shader_cull_distance) + && (b.shader_float64 == vk::FALSE || a.shader_float64 != b.shader_float64) + && (b.shader_int64 == vk::FALSE || a.shader_int64 != b.shader_int64) + && (b.shader_int16 == vk::FALSE || a.shader_int16 != b.shader_int16) + && (b.shader_resource_residency == vk::FALSE + || a.shader_resource_residency != b.shader_resource_residency) + && (b.shader_resource_min_lod == vk::FALSE + || a.shader_resource_min_lod != b.shader_resource_min_lod) + && (b.sparse_binding == vk::FALSE || a.sparse_binding != b.sparse_binding) + && (b.sparse_residency_buffer == vk::FALSE + || a.sparse_residency_buffer != b.sparse_residency_buffer) + && (b.sparse_residency_image2_d == vk::FALSE + || a.sparse_residency_image2_d != b.sparse_residency_image2_d) + && (b.sparse_residency_image3_d == vk::FALSE + || a.sparse_residency_image3_d != b.sparse_residency_image3_d) + && (b.sparse_residency2_samples == vk::FALSE + || a.sparse_residency2_samples != b.sparse_residency2_samples) + && (b.sparse_residency4_samples == vk::FALSE + || a.sparse_residency4_samples != b.sparse_residency4_samples) + && (b.sparse_residency8_samples == vk::FALSE + || a.sparse_residency8_samples != b.sparse_residency8_samples) + && (b.sparse_residency16_samples == vk::FALSE + || a.sparse_residency16_samples != b.sparse_residency16_samples) + && (b.sparse_residency_aliased == vk::FALSE + || a.sparse_residency_aliased != b.sparse_residency_aliased) + && (b.variable_multisample_rate == vk::FALSE + || a.variable_multisample_rate != b.variable_multisample_rate) + && (b.inherited_queries == vk::FALSE || a.inherited_queries != b.inherited_queries) + } + + fn core11_superset_of( + a: &vk::PhysicalDeviceVulkan11Features, + b: &vk::PhysicalDeviceVulkan11Features, + ) -> bool { + (b.storage_buffer16_bit_access == vk::FALSE + || a.storage_buffer16_bit_access != b.storage_buffer16_bit_access) + && (b.uniform_and_storage_buffer16_bit_access == vk::FALSE + || a.uniform_and_storage_buffer16_bit_access + != b.uniform_and_storage_buffer16_bit_access) + && (b.storage_push_constant16 == vk::FALSE + || a.storage_push_constant16 != b.storage_push_constant16) + && (b.storage_input_output16 == vk::FALSE + || a.storage_input_output16 != b.storage_input_output16) + && (b.multiview == vk::FALSE || a.multiview != b.multiview) + && (b.multiview_geometry_shader == vk::FALSE + || a.multiview_geometry_shader != b.multiview_geometry_shader) + && (b.multiview_tessellation_shader == vk::FALSE + || a.multiview_tessellation_shader != b.multiview_tessellation_shader) + && (b.variable_pointers_storage_buffer == vk::FALSE + || a.variable_pointers_storage_buffer != b.variable_pointers_storage_buffer) + && (b.variable_pointers == vk::FALSE || a.variable_pointers != b.variable_pointers) + && (b.protected_memory == vk::FALSE || a.protected_memory != b.protected_memory) + && (b.sampler_ycbcr_conversion == vk::FALSE + || a.sampler_ycbcr_conversion != b.sampler_ycbcr_conversion) + && (b.shader_draw_parameters == vk::FALSE + || a.shader_draw_parameters != b.shader_draw_parameters) + } + + fn core12_superset_of( + a: &vk::PhysicalDeviceVulkan12Features, + b: &vk::PhysicalDeviceVulkan12Features, + ) -> bool { + (b.sampler_mirror_clamp_to_edge == vk::FALSE + || a.sampler_mirror_clamp_to_edge != b.sampler_mirror_clamp_to_edge) + && (b.draw_indirect_count == vk::FALSE + || a.draw_indirect_count != b.draw_indirect_count) + && (b.storage_buffer8_bit_access == vk::FALSE + || a.storage_buffer8_bit_access != b.storage_buffer8_bit_access) + && (b.uniform_and_storage_buffer8_bit_access == vk::FALSE + || a.uniform_and_storage_buffer8_bit_access + != b.uniform_and_storage_buffer8_bit_access) + && (b.storage_push_constant8 == vk::FALSE + || a.storage_push_constant8 != b.storage_push_constant8) + && (b.shader_buffer_int64_atomics == vk::FALSE + || a.shader_buffer_int64_atomics != b.shader_buffer_int64_atomics) + && (b.shader_shared_int64_atomics == vk::FALSE + || a.shader_shared_int64_atomics != b.shader_shared_int64_atomics) + && (b.shader_float16 == vk::FALSE || a.shader_float16 != b.shader_float16) + && (b.shader_int8 == vk::FALSE || a.shader_int8 != b.shader_int8) + && (b.descriptor_indexing == vk::FALSE + || a.descriptor_indexing != b.descriptor_indexing) + && (b.shader_input_attachment_array_dynamic_indexing == vk::FALSE + || a.shader_input_attachment_array_dynamic_indexing + != b.shader_input_attachment_array_dynamic_indexing) + && (b.shader_uniform_texel_buffer_array_dynamic_indexing == vk::FALSE + || a.shader_uniform_texel_buffer_array_dynamic_indexing + != b.shader_uniform_texel_buffer_array_dynamic_indexing) + && (b.shader_storage_texel_buffer_array_dynamic_indexing == vk::FALSE + || a.shader_storage_texel_buffer_array_dynamic_indexing + != b.shader_storage_texel_buffer_array_dynamic_indexing) + && (b.shader_uniform_buffer_array_non_uniform_indexing == vk::FALSE + || a.shader_uniform_buffer_array_non_uniform_indexing + != b.shader_uniform_buffer_array_non_uniform_indexing) + && (b.shader_sampled_image_array_non_uniform_indexing == vk::FALSE + || a.shader_sampled_image_array_non_uniform_indexing + != b.shader_sampled_image_array_non_uniform_indexing) + && (b.shader_storage_buffer_array_non_uniform_indexing == vk::FALSE + || a.shader_storage_buffer_array_non_uniform_indexing + != b.shader_storage_buffer_array_non_uniform_indexing) + && (b.shader_storage_image_array_non_uniform_indexing == vk::FALSE + || a.shader_storage_image_array_non_uniform_indexing + != b.shader_storage_image_array_non_uniform_indexing) + && (b.shader_input_attachment_array_non_uniform_indexing == vk::FALSE + || a.shader_input_attachment_array_non_uniform_indexing + != b.shader_input_attachment_array_non_uniform_indexing) + && (b.shader_uniform_texel_buffer_array_non_uniform_indexing == vk::FALSE + || a.shader_uniform_texel_buffer_array_non_uniform_indexing + != b.shader_uniform_texel_buffer_array_non_uniform_indexing) + && (b.shader_storage_texel_buffer_array_non_uniform_indexing == vk::FALSE + || a.shader_storage_texel_buffer_array_non_uniform_indexing + != b.shader_storage_texel_buffer_array_non_uniform_indexing) + && (b.descriptor_binding_uniform_buffer_update_after_bind == vk::FALSE + || a.descriptor_binding_uniform_buffer_update_after_bind + != b.descriptor_binding_uniform_buffer_update_after_bind) + && (b.descriptor_binding_sampled_image_update_after_bind == vk::FALSE + || a.descriptor_binding_sampled_image_update_after_bind + != b.descriptor_binding_sampled_image_update_after_bind) + && (b.descriptor_binding_storage_image_update_after_bind == vk::FALSE + || a.descriptor_binding_storage_image_update_after_bind + != b.descriptor_binding_storage_image_update_after_bind) + && (b.descriptor_binding_storage_buffer_update_after_bind == vk::FALSE + || a.descriptor_binding_storage_buffer_update_after_bind + != b.descriptor_binding_storage_buffer_update_after_bind) + && (b.descriptor_binding_uniform_texel_buffer_update_after_bind == vk::FALSE + || a.descriptor_binding_uniform_texel_buffer_update_after_bind + != b.descriptor_binding_uniform_texel_buffer_update_after_bind) + && (b.descriptor_binding_storage_texel_buffer_update_after_bind == vk::FALSE + || a.descriptor_binding_storage_texel_buffer_update_after_bind + != b.descriptor_binding_storage_texel_buffer_update_after_bind) + && (b.descriptor_binding_update_unused_while_pending == vk::FALSE + || a.descriptor_binding_update_unused_while_pending + != b.descriptor_binding_update_unused_while_pending) + && (b.descriptor_binding_partially_bound == vk::FALSE + || a.descriptor_binding_partially_bound != b.descriptor_binding_partially_bound) + && (b.descriptor_binding_variable_descriptor_count == vk::FALSE + || a.descriptor_binding_variable_descriptor_count + != b.descriptor_binding_variable_descriptor_count) + && (b.runtime_descriptor_array == vk::FALSE + || a.runtime_descriptor_array != b.runtime_descriptor_array) + && (b.sampler_filter_minmax == vk::FALSE + || a.sampler_filter_minmax != b.sampler_filter_minmax) + && (b.scalar_block_layout == vk::FALSE + || a.scalar_block_layout != b.scalar_block_layout) + && (b.imageless_framebuffer == vk::FALSE + || a.imageless_framebuffer != b.imageless_framebuffer) + && (b.uniform_buffer_standard_layout == vk::FALSE + || a.uniform_buffer_standard_layout != b.uniform_buffer_standard_layout) + && (b.shader_subgroup_extended_types == vk::FALSE + || a.shader_subgroup_extended_types != b.shader_subgroup_extended_types) + && (b.separate_depth_stencil_layouts == vk::FALSE + || a.separate_depth_stencil_layouts != b.separate_depth_stencil_layouts) + && (b.host_query_reset == vk::FALSE || a.host_query_reset != b.host_query_reset) + && (b.timeline_semaphore == vk::FALSE + || a.timeline_semaphore != b.timeline_semaphore) + && (b.buffer_device_address == vk::FALSE + || a.buffer_device_address != b.buffer_device_address) + && (b.buffer_device_address_capture_replay == vk::FALSE + || a.buffer_device_address_capture_replay + != b.buffer_device_address_capture_replay) + && (b.buffer_device_address_multi_device == vk::FALSE + || a.buffer_device_address_multi_device != b.buffer_device_address_multi_device) + && (b.vulkan_memory_model == vk::FALSE + || a.vulkan_memory_model != b.vulkan_memory_model) + && (b.vulkan_memory_model_device_scope == vk::FALSE + || a.vulkan_memory_model_device_scope != b.vulkan_memory_model_device_scope) + && (b.vulkan_memory_model_availability_visibility_chains == vk::FALSE + || a.vulkan_memory_model_availability_visibility_chains + != b.vulkan_memory_model_availability_visibility_chains) + && (b.shader_output_viewport_index == vk::FALSE + || a.shader_output_viewport_index != b.shader_output_viewport_index) + && (b.shader_output_layer == vk::FALSE + || a.shader_output_layer != b.shader_output_layer) + && (b.subgroup_broadcast_dynamic_id == vk::FALSE + || a.subgroup_broadcast_dynamic_id != b.subgroup_broadcast_dynamic_id) + } + + fn core13_superset_of( + a: &vk::PhysicalDeviceVulkan13Features, + b: &vk::PhysicalDeviceVulkan13Features, + ) -> bool { + (b.robust_image_access == vk::FALSE || a.robust_image_access != b.robust_image_access) + && (b.inline_uniform_block == vk::FALSE + || a.inline_uniform_block != b.inline_uniform_block) + && (b.descriptor_binding_inline_uniform_block_update_after_bind == vk::FALSE + || a.descriptor_binding_inline_uniform_block_update_after_bind + != b.descriptor_binding_inline_uniform_block_update_after_bind) + && (b.pipeline_creation_cache_control == vk::FALSE + || a.pipeline_creation_cache_control != b.pipeline_creation_cache_control) + && (b.private_data == vk::FALSE || a.private_data != b.private_data) + && (b.shader_demote_to_helper_invocation == vk::FALSE + || a.shader_demote_to_helper_invocation != b.shader_demote_to_helper_invocation) + && (b.shader_terminate_invocation == vk::FALSE + || a.shader_terminate_invocation != b.shader_terminate_invocation) + && (b.subgroup_size_control == vk::FALSE + || a.subgroup_size_control != b.subgroup_size_control) + && (b.compute_full_subgroups == vk::FALSE + || a.compute_full_subgroups != b.compute_full_subgroups) + && (b.synchronization2 == vk::FALSE || a.synchronization2 != b.synchronization2) + && (b.texture_compression_astc_hdr == vk::FALSE + || a.texture_compression_astc_hdr != b.texture_compression_astc_hdr) + && (b.shader_zero_initialize_workgroup_memory == vk::FALSE + || a.shader_zero_initialize_workgroup_memory + != b.shader_zero_initialize_workgroup_memory) + && (b.dynamic_rendering == vk::FALSE || a.dynamic_rendering != b.dynamic_rendering) + && (b.shader_integer_dot_product == vk::FALSE + || a.shader_integer_dot_product != b.shader_integer_dot_product) + && (b.maintenance4 == vk::FALSE || a.maintenance4 != b.maintenance4) + } + + fn mesh_shader_superset_of( + a: &vk::PhysicalDeviceMeshShaderFeaturesEXT, + b: &vk::PhysicalDeviceMeshShaderFeaturesEXT, + ) -> bool { + (b.task_shader == vk::FALSE || a.task_shader != b.task_shader) + && (b.mesh_shader == vk::FALSE || a.mesh_shader != b.mesh_shader) + && (b.multiview_mesh_shader == vk::FALSE + || a.multiview_mesh_shader != b.multiview_mesh_shader) + && (b.primitive_fragment_shading_rate_mesh_shader == vk::FALSE + || a.primitive_fragment_shading_rate_mesh_shader + != b.primitive_fragment_shading_rate_mesh_shader) + && (b.mesh_shader_queries == vk::FALSE + || a.mesh_shader_queries != b.mesh_shader_queries) + } + + core_superset_of(&self.core, &other.core) + && core11_superset_of(&self.core11, &other.core11) + && core12_superset_of(&self.core12, &other.core12) + && core13_superset_of(&self.core13, &other.core13) + && match (&self.mesh_shader, &other.mesh_shader) { + (Some(a), Some(b)) => mesh_shader_superset_of(a, b), + (None, Some(_)) => false, + _ => true, + } + } +} + +#[derive(Debug, Default)] +pub struct PhysicalDeviceProperties { + /// Extensions supported by the device, as reported by `enumerate_device_extension_properties`. + pub supported_extensions: Vec, + /// Vulkan 1.0 properties. + pub core: vk::PhysicalDeviceProperties, + /// Vulkan 1.1 properties. + pub core11: vk::PhysicalDeviceVulkan11Properties<'static>, + /// Vulkan 1.2 properties. + pub core12: vk::PhysicalDeviceVulkan12Properties<'static>, + /// Vulkan 1.3 properties. + pub core13: vk::PhysicalDeviceVulkan13Properties<'static>, + /// Mesh shader properties from the `VK_EXT_mesh_shader` extension, if supported. + pub mesh_shader: Option>, + /// Memory properties of the device, as reported by `get_physical_device_memory_properties`. + pub memory_properties: vk::PhysicalDeviceMemoryProperties, + /// The API version supported by the device. + pub device_api_version: u32, +} + +impl PhysicalDeviceProperties { + pub fn get_device_local_memory_count(&self) -> u64 { + self.memory_properties + .memory_heaps + .iter() + .filter(|heap| heap.flags.contains(vk::MemoryHeapFlags::DEVICE_LOCAL)) + .map(|heap| heap.size) + .sum::() + } +} + +pub fn get_physical_device_features( + instance: &InstanceInner, + pdev: vk::PhysicalDevice, + properties: &PhysicalDeviceProperties, +) -> PhysicalDeviceFeatures { + let mut features = PhysicalDeviceFeatures::default(); + let mut features2 = vk::PhysicalDeviceFeatures2::default(); + + features2 = features2 + .push_next(&mut features.core11) + .push_next(&mut features.core12) + .push_next(&mut features.core13); + + if properties.supports_extension(make_extension!(ext::mesh_shader)) { + features2 = features2.push_next( + features + .mesh_shader + .insert(vk::PhysicalDeviceMeshShaderFeaturesEXT::default()), + ); + } + + unsafe { + instance + .raw + .get_physical_device_features2(pdev, &mut features2); + } + + features.core = features2.features; + + features +} + +pub fn get_physical_device_properties( + instance: &InstanceInner, + pdev: vk::PhysicalDevice, +) -> Result { + let mut props = PhysicalDeviceProperties::default(); + props.core = unsafe { instance.raw.get_physical_device_properties(pdev) }; + props.device_api_version = props.core.api_version; + props.supported_extensions = + unsafe { instance.raw.enumerate_device_extension_properties(pdev)? }; + props.memory_properties = unsafe { instance.raw.get_physical_device_memory_properties(pdev) }; + + let supports_mesh_shader = props.supports_extension(make_extension!(ext::mesh_shader)); + + // TODO: fallibly check anything beyond 1.0 + let mut props2 = vk::PhysicalDeviceProperties2::default() + .push_next(&mut props.core11) + .push_next(&mut props.core12) + .push_next(&mut props.core13); + + if supports_mesh_shader { + let next = props + .mesh_shader + .insert(vk::PhysicalDeviceMeshShaderPropertiesEXT::default()); + props2 = props2.push_next(next); + } + + unsafe { + instance + .raw + .get_physical_device_properties2(pdev, &mut props2); + } + + Ok(props) +} + +#[allow(dead_code)] +pub fn extension_intersection<'a>( + supported: &'a [vk::ExtensionProperties], + required: &[Extension<'a>], +) -> Vec { + supported + .iter() + .filter(|ext| { + required.iter().any(|req| { + ext.extension_name_as_c_str() == Ok(req.name) && ext.spec_version >= req.version + }) + }) + .cloned() + .collect() +} + +impl PhysicalDeviceProperties { + pub fn supports_extension(&self, e: Extension) -> bool { + self.supported_extensions + .iter() + .any(|ext| ext.extension_name_as_c_str() == Ok(e.name) && ext.spec_version >= e.version) + } +} diff --git a/crates/renderer_device/src/pipeline_cache.rs b/crates/renderer_device/src/pipeline_cache.rs new file mode 100644 index 0000000..3dc14d1 --- /dev/null +++ b/crates/renderer_device/src/pipeline_cache.rs @@ -0,0 +1,116 @@ +use std::sync::Arc; + +use ash::vk; + +use ash::Device; + +use crate::PhysicalDeviceInfo; +use crate::device::DeviceInner; + +#[derive(Debug)] +pub struct PipelineCache { + #[allow(dead_code)] + key: u128, + pub raw: vk::PipelineCache, +} + +impl crate::device::asdf::traits::ExternallyManagedObject> for PipelineCache { + unsafe fn destroy(self, owner: &Arc) { + tracing::info!("destroying pipeline cache with key {:x}", self.key); + if let Ok(data) = self.export(&owner.raw) { + tracing::info!( + "exported pipeline cache with key {:x} and size {} bytes", + self.key, + data.len() + ); + _ = Self::write_to_disk(self.key, &data).inspect_err(|err| { + tracing::error!("failed to write pipeline cache to disk: {err}"); + }); + } + unsafe { owner.raw.destroy_pipeline_cache(self.raw, None) }; + } +} + +impl PipelineCache { + const MAGIC: [u8; 4] = *b"VYPC"; + const KEY_VERSION: u32 = 1; + const PATH: &'static str = "pipeline_cache.bin"; + fn calculate_key(adapter: &PhysicalDeviceInfo) -> u128 { + use md5::Digest; + let mut hasher = md5::Md5::new(); + let props = &adapter.properties; + hasher.update(bytemuck::bytes_of(&[ + props.core.vendor_id, + props.core.api_version, + props.core.device_id, + props.core.driver_version, + ])); + u128::from_le_bytes(hasher.finalize().into()) + } + + fn load_from_disk(key: u128) -> Option<(u128, Vec)> { + use std::io::Read; + + let file = std::fs::File::open(Self::PATH).ok()?; + let mut reader = std::io::BufReader::new(file); + let mut magic = [0; 4]; + reader.read_exact(&mut magic).ok()?; + if magic != Self::MAGIC { + return None; + } + + let mut version = 0; + reader + .read_exact(bytemuck::bytes_of_mut(&mut version)) + .ok()?; + if version != Self::KEY_VERSION { + return None; + } + + let mut disk_key = 0; + reader + .read_exact(bytemuck::bytes_of_mut(&mut disk_key)) + .ok()?; + if disk_key != key { + return None; + } + + let mut data = Vec::new(); + reader.read_to_end(&mut data).ok()?; + + Some((key, data)) + } + + fn write_to_disk(key: u128, data: &[u8]) -> std::io::Result<()> { + use std::io::Write; + + let file = std::fs::File::create(Self::PATH)?; + let mut writer = std::io::BufWriter::new(file); + writer.write_all(&Self::MAGIC)?; + writer.write_all(bytemuck::bytes_of(&Self::KEY_VERSION))?; + writer.write_all(bytemuck::bytes_of(&key))?; + writer.write_all(data)?; + Ok(()) + } + + pub fn new(device: &Device, adapter: &PhysicalDeviceInfo) -> crate::Result { + let key = Self::calculate_key(adapter); + let data = Self::load_from_disk(key).map(|(key, data)| { + tracing::info!("loaded pipeline cache from disk with key {key:x}"); + data + }); + + let info = vk::PipelineCacheCreateInfo::default() + // .flags(vk::PipelineCacheCreateFlags::EXTERNALLY_SYNCHRONIZED) + .initial_data(data.as_deref().unwrap_or_default()); + + let cache = unsafe { device.create_pipeline_cache(&info, None)? }; + + Ok(Self { key, raw: cache }) + } + + pub fn export(&self, device: &ash::Device) -> crate::Result> { + let data = unsafe { device.get_pipeline_cache_data(self.raw)? }; + Ok(data) + } +} diff --git a/crates/renderer/src/queue.rs b/crates/renderer_device/src/queue.rs similarity index 99% rename from crates/renderer/src/queue.rs rename to crates/renderer_device/src/queue.rs index 5afd33c..263e673 100644 --- a/crates/renderer/src/queue.rs +++ b/crates/renderer_device/src/queue.rs @@ -42,9 +42,9 @@ impl Queue { #[derive(Debug)] pub struct QueueInner { - pub(crate) raw: vk::Queue, - pub(crate) family: QueueFamily, - pub(crate) lock: Mutex<()>, + pub raw: vk::Queue, + pub family: QueueFamily, + pub lock: Mutex<()>, } impl QueueInner { diff --git a/crates/renderer/src/sync.rs b/crates/renderer_device/src/sync.rs similarity index 97% rename from crates/renderer/src/sync.rs rename to crates/renderer_device/src/sync.rs index c30f92c..86f10f5 100644 --- a/crates/renderer/src/sync.rs +++ b/crates/renderer_device/src/sync.rs @@ -345,9 +345,9 @@ pub enum Semaphore { } #[derive(Debug, Clone, Copy)] -pub(crate) struct BinarySemaphore(pub(crate) vk::Semaphore); +pub struct BinarySemaphore(pub vk::Semaphore); #[derive(Debug, Clone, Copy)] -pub(crate) struct TimelineSemaphore(pub(crate) vk::Semaphore); +pub struct TimelineSemaphore(pub vk::Semaphore); // This is just so that ash can name these semaphore newtypes impl vk::Handle for BinarySemaphore { @@ -433,12 +433,12 @@ impl Semaphore { let semaphore = match semaphore_type { SemaphoreType::Binary => { let semaphore: SemaphoreInner = - device.pools.binary_semaphores.get_debug_named(name)?.into(); - PoolObject::new(semaphore, device.pools.clone()) + device.pools().binary_semaphores.get_debug_named(name)?.into(); + PoolObject::new(semaphore, device.pools().clone()) } SemaphoreType::Timeline(value) => { let semaphore: SemaphoreInner = device - .pools + .pools() .timeline_semaphores .get_debug_named(name)? .into(); @@ -450,7 +450,7 @@ impl Semaphore { device.raw.signal_semaphore(&info)?; } - PoolObject::new(semaphore, device.pools.clone()) + PoolObject::new(semaphore, device.pools().clone()) } };