extract device into renderer_device

add deviceref macro
This commit is contained in:
janis 2026-10-24 17:36:43 +02:00
parent c20cee38de
commit ce46ca1801
Signed by: janis
SSH key fingerprint: SHA256:bB1qbbqmDXZNT0KKD5c2Dfjg53JGhj7B3CFcLIzSqq8
19 changed files with 1129 additions and 929 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/bevy_vulkan_render", "crates/renderer_device",
] ]
[dependencies] [dependencies]

View file

@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
renderer_device = { path = "../renderer_device" }
renderer_macros = { path = "renderer_macros" } renderer_macros = { path = "renderer_macros" }
tinyvec = { workspace = true }
arrayvec = { workspace = true } arrayvec = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
petgraph = { workspace = true } petgraph = { workspace = true }
@ -18,7 +18,6 @@ bitflags = { workspace = true }
thread_local = {workspace = true} thread_local = {workspace = true}
thiserror = { workspace = true } thiserror = { workspace = true }
anyhow = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
ash = { workspace = true } ash = { workspace = true }
@ -27,8 +26,6 @@ ash-window = { workspace = true }
gpu-allocator = { workspace = true } gpu-allocator = { workspace = true }
rectangle-pack = { workspace = true } rectangle-pack = { workspace = true }
md-5 = { workspace = true }
raw-window-handle = { workspace = true } raw-window-handle = { workspace = true }
egui = { workspace = true , features = ["bytemuck"]} egui = { workspace = true , features = ["bytemuck"]}
# egui_winit_platform = { workspace = true } # egui_winit_platform = { workspace = true }
@ -42,7 +39,6 @@ smol = { workspace = true }
derive_more = { workspace = true , features = ["debug"] } derive_more = { workspace = true , features = ["debug"] }
cfg-if = "1.0.0" cfg-if = "1.0.0"
dyn-clone = "1"
crossbeam = "0.8.4" crossbeam = "0.8.4"
bytemuck = { version = "1.21.0", features = ["derive"] } bytemuck = { version = "1.21.0", features = ["derive"] }

View file

@ -9,4 +9,4 @@ proc-macro = true
[dependencies] [dependencies]
proc-macro2 = "1.0" proc-macro2 = "1.0"
quote = "1.0" quote = "1.0"
syn = "2" syn = { version = "2", features = ["full"] }

View file

@ -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::<Vec<_>>();
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::<Vec<_>>();
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()
}

View file

@ -288,7 +288,7 @@ impl SingleUseCommand {
wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>, wait: Option<(vk::Semaphore, vk::PipelineStageFlags)>,
signal: Option<vk::Semaphore>, signal: Option<vk::Semaphore>,
) -> crate::Result<()> { ) -> 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)?; let future = self.submit_async(wait, signal, fence)?;
future.block()?; future.block()?;
Ok(()) Ok(())

View file

@ -7,8 +7,7 @@ use std::{
use crate::{ use crate::{
device::{ device::{
Allocation, AllocationStrategy, DeviceInner, DeviceObject, QueueFlags, Allocation, AllocationStrategy, DeviceObject, QueueFlags,
asdf::traits::ExternallyManagedObject,
}, },
swapchain::Swapchain, swapchain::Swapchain,
util::weak_vec::WeakVec, util::weak_vec::WeakVec,
@ -139,14 +138,6 @@ enum ImageInner {
Allocated(DeviceObject<vk::Image>, Allocation), Allocated(DeviceObject<vk::Image>, Allocation),
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Image {
unsafe fn destroy(self, device: &T) {
unsafe {
device.as_ref().raw.destroy_image(self, None);
}
}
}
impl ImageInner { impl ImageInner {
fn image(&self) -> vk::Image { fn image(&self) -> vk::Image {
match self { match self {
@ -1017,14 +1008,6 @@ impl ImageView {
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::ImageView {
unsafe fn destroy(self, device: &T) {
unsafe {
device.as_ref().raw.destroy_image_view(self, None);
}
}
}
pub struct QueueOwnership { pub struct QueueOwnership {
pub src: u32, pub src: u32,
pub dst: u32, pub dst: u32,

View file

@ -2,41 +2,37 @@
negative_impls, negative_impls,
map_try_insert, map_try_insert,
debug_closure_helpers, 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 raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use ash::{
ext,
prelude::VkResult,
vk::{self, SurfaceCapabilitiesKHR},
};
use dyn_clone::DynClone;
pub use ash; pub use ash;
mod buffers; mod buffers;
pub mod commands; pub mod commands;
mod debug;
pub mod device;
#[path = "egui.rs"] #[path = "egui.rs"]
mod egui_pass; mod egui_pass;
mod images; mod images;
pub mod instance;
mod pipeline; mod pipeline;
pub mod render_graph; pub mod render_graph;
pub mod rendering; pub mod rendering;
pub mod swapchain; pub mod swapchain;
pub mod sync;
mod text; mod text;
pub mod util; 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 { mod texture {
use std::{collections::BTreeMap, sync::Arc}; use std::{collections::BTreeMap, sync::Arc};
@ -80,581 +76,7 @@ mod texture {
use util::Rgba; 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<T> = core::result::Result<T, Error>;
#[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<T: vk::ExtendsPhysicalDeviceFeatures2 + Debug + Send + Sync> ExtendsDeviceFeatures2Debug
for T
{
}
impl<T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync>
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<vk::SurfaceFormatKHR>,
pub present_modes: Vec<vk::PresentModeKHR>,
}
#[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<vk::PhysicalDeviceMeshShaderFeaturesEXT<'static>>,
}
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<vk::ExtensionProperties>,
/// 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<vk::PhysicalDeviceMeshShaderPropertiesEXT<'static>>,
/// 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::<u64>()
}
}
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<PhysicalDeviceProperties> {
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<vk::ExtensionProperties> {
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 { pub struct SamplerCache {
device: Device, device: Device,
@ -923,7 +345,9 @@ impl EguiState {
} }
} }
#[derive(renderer_macros::DeviceRef)]
pub struct Renderer2 { pub struct Renderer2 {
#[device]
pub device: Device, pub device: Device,
pub samplers: SamplerCache, pub samplers: SamplerCache,
pub texture_manager: texture::TextureManager, pub texture_manager: texture::TextureManager,
@ -1036,7 +460,7 @@ impl Renderer2 {
let future = cmds.submit( let future = cmds.submit(
Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)), Some((frame.acquire, vk::PipelineStageFlags::TRANSFER)),
Some(frame.release), 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; future.await;
@ -1052,8 +476,7 @@ impl Renderer2 {
pub use vk::Extent2D; pub use vk::Extent2D;
use crate::{ use crate::{
device::Extension, instance::InstanceDesc,
instance::{InstanceDesc, InstanceInner},
}; };
pub mod utils { pub mod utils {

View file

@ -5,7 +5,7 @@ use parking_lot::Mutex;
use crate::{ use crate::{
device::{ device::{
Device, DeviceHandle, DeviceInner, DeviceObject, asdf::traits::ExternallyManagedObject, Device, DeviceObject,
}, },
make_extension, make_extension,
}; };
@ -229,20 +229,6 @@ pub struct DescriptorSetAllocDesc<'a> {
pub layout: &'a DescriptorSetLayout, 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<T: AsRef<DeviceInner>> ExternallyManagedObject<T> 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)] #[derive(Debug)]
pub struct DescriptorPool { pub struct DescriptorPool {
pool: DeviceObject<vk::DescriptorPool>, pool: DeviceObject<vk::DescriptorPool>,
@ -300,17 +286,6 @@ impl DescriptorPool {
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::DescriptorSetLayout {
unsafe fn destroy(self, device: &T) {
unsafe {
device
.as_ref()
.raw
.destroy_descriptor_set_layout(self, None);
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct DescriptorSetLayout { pub struct DescriptorSetLayout {
layout: DeviceObject<vk::DescriptorSetLayout>, layout: DeviceObject<vk::DescriptorSetLayout>,
@ -360,14 +335,6 @@ impl DescriptorSetLayout {
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::PipelineLayout {
unsafe fn destroy(self, device: &T) {
unsafe {
device.as_ref().raw.destroy_pipeline_layout(self, None);
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct PipelineLayout { pub struct PipelineLayout {
layout: DeviceObject<vk::PipelineLayout>, layout: DeviceObject<vk::PipelineLayout>,
@ -457,17 +424,6 @@ impl std::hash::Hash for SamplerDesc {
} }
} }
impl<T> ExternallyManagedObject<T> for vk::Sampler
where
T: AsRef<DeviceInner>,
{
unsafe fn destroy(self, device: &T) {
unsafe {
device.as_ref().raw.destroy_sampler(self, None);
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Sampler { pub struct Sampler {
sampler: DeviceObject<vk::Sampler>, sampler: DeviceObject<vk::Sampler>,
@ -505,14 +461,6 @@ impl Sampler {
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::ShaderModule {
unsafe fn destroy(self, device: &T) {
unsafe {
device.as_ref().raw.destroy_shader_module(self, None);
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct ShaderModule { pub struct ShaderModule {
module: DeviceObject<vk::ShaderModule>, module: DeviceObject<vk::ShaderModule>,
@ -557,14 +505,6 @@ pub struct Pipeline {
layout: Arc<PipelineLayout>, layout: Arc<PipelineLayout>,
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Pipeline {
unsafe fn destroy(self, device: &T) {
unsafe {
device.as_ref().raw.destroy_pipeline(self, None);
}
}
}
impl Pipeline { impl Pipeline {
pub fn new_compute(device: Device, desc: ComputePipelineDesc) -> crate::Result<Self> { pub fn new_compute(device: Device, desc: ComputePipelineDesc) -> crate::Result<Self> {
let info = &vk::ComputePipelineCreateInfo::default() let info = &vk::ComputePipelineCreateInfo::default()
@ -581,7 +521,7 @@ impl Pipeline {
device device
.dev() .dev()
.create_compute_pipelines( .create_compute_pipelines(
device.pools.pipeline_cache.raw, device.pools().pipeline_cache.raw,
core::slice::from_ref(info), core::slice::from_ref(info),
None, None,
) )
@ -750,7 +690,7 @@ impl Pipeline {
device device
.dev() .dev()
.create_graphics_pipelines( .create_graphics_pipelines(
device.pools.pipeline_cache.raw, device.pools().pipeline_cache.raw,
core::slice::from_ref(&info), core::slice::from_ref(&info),
None, None,
) )
@ -777,122 +717,3 @@ impl Pipeline {
&self.layout &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<Arc<DeviceInner>> for PipelineCache {
unsafe fn destroy(self, owner: &Arc<DeviceInner>) {
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<u8>)> {
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<Self> {
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<Vec<u8>> {
let data = unsafe { device.get_pipeline_cache_data(self.raw)? };
Ok(data)
}
}
}

View file

@ -168,7 +168,7 @@ impl Wireframe {
let future = cmd.submit_async( let future = cmd.submit_async(
None, None,
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())?; let pipeline = Self::create_pipeline(dev.clone())?;

View file

@ -147,7 +147,12 @@ impl Surface {
adapter: &PhysicalDeviceInfo, adapter: &PhysicalDeviceInfo,
config: &mut SwapchainConfiguration, config: &mut SwapchainConfiguration,
) -> Result<SurfaceCapabilities> { ) -> Result<SurfaceCapabilities> {
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; 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 { 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. // capabilities to get the correct image counts.
let surface_caps = instance.get_adapter_surface_capabilities( let surface_caps = instance.get_adapter_surface_capabilities(
adapter.pdev, adapter.pdev,
self, &self.functor,
self.raw,
Some(config.present_mode), Some(config.present_mode),
)?; )?;
@ -240,7 +246,12 @@ impl Surface {
instance: &Instance, instance: &Instance,
adapter: &PhysicalDeviceInfo, adapter: &PhysicalDeviceInfo,
) -> Result<SwapchainConfiguration> { ) -> Result<SwapchainConfiguration> {
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 let present_mode = surface_caps
.present_modes .present_modes
@ -531,7 +542,7 @@ impl Swapchain {
tracing::trace!(frame, "acquiring image for frame {frame}"); tracing::trace!(frame, "acquiring image for frame {frame}");
async move { 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]; let acquire = self.acquire_semaphores[frame];
// spawn on threadpool because it might block. // spawn on threadpool because it might block.
@ -832,7 +843,7 @@ mod tests {
let surface = Arc::new(Surface::headless(&instance)?); 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)?; let device = adapter.create_logical_device(&instance, &[], features, None)?;
surface.configure( surface.configure(

View file

@ -1,5 +1,4 @@
use std::{ use std::{
borrow::Cow,
mem::ManuallyDrop, mem::ManuallyDrop,
ops::{Deref, DerefMut}, 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<Cow<'static, str>> 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<String> for DebugName {
fn from(_value: String) -> Self {
Self {
#[cfg(debug_assertions)]
name: Cow::Owned(_value),
}
}
}
#[macro_export] #[macro_export]
macro_rules! def_monotonic_id { macro_rules! def_monotonic_id {
($vis:vis $ty:ident) => { ($vis:vis $ty:ident) => {

View file

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

View file

@ -14,10 +14,67 @@ use ash::{
use parking_lot::Mutex; use parking_lot::Mutex;
use raw_window_handle::RawDisplayHandle; 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<Cow<'static, str>> 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<String> for DebugName {
fn from(_value: String) -> Self {
Self {
#[cfg(debug_assertions)]
name: Cow::Owned(_value),
}
}
}
use crate::{ use crate::{
Instance, PhysicalDeviceFeatures, PhysicalDeviceInfo, Result, Instance, PhysicalDeviceFeatures, PhysicalDeviceInfo, Result,
device::asdf::traits::ExternallyManagedObject, device::asdf::traits::ExternallyManagedObject,
pipeline::pipeline_cache::PipelineCache, pipeline_cache::PipelineCache,
queue::{DeviceQueueInfos, DeviceQueues, Queue}, queue::{DeviceQueueInfos, DeviceQueues, Queue},
sync::{self, BinarySemaphore, TimelineSemaphore}, sync::{self, BinarySemaphore, TimelineSemaphore},
}; };
@ -46,11 +103,11 @@ impl Drop for DeviceDrop {
} }
} }
pub(crate) struct DeviceExtensions { pub struct DeviceExtensions {
pub(crate) debug_utils: ext::debug_utils::Device, pub debug_utils: ext::debug_utils::Device,
pub(crate) swapchain: Option<khr::swapchain::Device>, pub swapchain: Option<khr::swapchain::Device>,
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) mesh_shader: Option<ext::mesh_shader::Device>, pub mesh_shader: Option<ext::mesh_shader::Device>,
} }
type GpuAllocation = gpu_allocator::vulkan::Allocation; type GpuAllocation = gpu_allocator::vulkan::Allocation;
@ -75,7 +132,7 @@ pub enum Allocation {
} }
impl Allocation { impl Allocation {
pub(crate) fn allocation(&self) -> Option<&GpuAllocation> { pub fn allocation(&self) -> Option<&GpuAllocation> {
match self { match self {
Allocation::Owned(obj) => Some(obj), Allocation::Owned(obj) => Some(obj),
Allocation::Shared(arc) => Some(arc.as_ref()), 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 { match self {
Allocation::Owned(obj) => Some(obj), Allocation::Owned(obj) => Some(obj),
Allocation::Shared(arc) => Arc::get_mut(arc).map(DerefMut::deref_mut), Allocation::Shared(arc) => Arc::get_mut(arc).map(DerefMut::deref_mut),
@ -93,15 +150,15 @@ impl Allocation {
} }
pub struct DeviceInner { pub struct DeviceInner {
pub(crate) alloc2: Mutex<gpu_allocator::vulkan::Allocator>, pub alloc2: Mutex<gpu_allocator::vulkan::Allocator>,
pub(crate) raw: ash::Device, pub raw: ash::Device,
pub(crate) adapter: PhysicalDeviceInfo, pub adapter: PhysicalDeviceInfo,
pub(crate) instance: Instance, pub instance: Instance,
pub(crate) queues: DeviceQueues, pub queues: DeviceQueues,
pub(crate) sync_threadpool: sync::SyncThreadpool, pub sync_threadpool: sync::SyncThreadpool,
pub(crate) device_extensions: DeviceExtensions, pub device_extensions: DeviceExtensions,
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) enabled_extensions: Vec<&'static CStr>, pub enabled_extensions: Vec<&'static CStr>,
_drop: DeviceDrop, _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, entry: &ash::Entry,
layers: &[&CStr], layers: &[&CStr],
) -> Result<Vec<ash::vk::ExtensionProperties>> { ) -> Result<Vec<ash::vk::ExtensionProperties>> {
@ -170,7 +227,7 @@ pub(crate) fn get_available_extensions(
} }
/// returns a tuple of supported-or-enabled extensions and unsupported-and-requested 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, entry: &ash::Entry,
layers: &[&'a CStr], layers: &[&'a CStr],
mut extensions: Vec<Extension<'a>>, mut extensions: Vec<Extension<'a>>,
@ -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. /// 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, entry: &ash::Entry,
wants_layers: Vec<&'a CStr>, wants_layers: Vec<&'a CStr>,
) -> core::result::Result<Vec<&'a CStr>, (Vec<&'a CStr>, Vec<&'a CStr>)> { ) -> core::result::Result<Vec<&'a CStr>, (Vec<&'a CStr>, Vec<&'a CStr>)> {
@ -438,11 +495,11 @@ impl PhysicalDeviceInfo {
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct DevicePools { pub struct DevicePools {
pub(crate) pipeline_cache: asdf::DeviceObject<PipelineCache, Arc<DeviceInner>>, pub pipeline_cache: asdf::DeviceObject<PipelineCache, Arc<DeviceInner>>,
pub(crate) fences: Arc<Pool<vk::Fence>>, pub fences: Arc<Pool<vk::Fence>>,
pub(crate) binary_semaphores: Pool<BinarySemaphore>, pub binary_semaphores: Pool<BinarySemaphore>,
pub(crate) timeline_semaphores: Pool<TimelineSemaphore>, pub timeline_semaphores: Pool<TimelineSemaphore>,
} }
impl AsRef<DevicePools> for DevicePools { impl AsRef<DevicePools> 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<Device> {
fn device(&self) -> &Device {
self.as_ref()
}
}
impl DeviceRef for &Device {
fn device(&self) -> &Device {
self
}
}
impl DeviceRef for &Arc<Device> {
fn device(&self) -> &Device {
self.as_ref()
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Device { pub struct Device {
pub(crate) shared: Arc<DeviceInner>, pub(crate) shared: Arc<DeviceInner>,
pub(crate) pools: Arc<DevicePools>, pools: Arc<DevicePools>,
}
impl Device {
pub fn pools(&self) -> &Arc<DevicePools> {
&self.pools
}
} }
impl PartialEq for Device { impl PartialEq for Device {
@ -516,7 +607,7 @@ impl DeviceInner {
pub fn features(&self) -> &crate::PhysicalDeviceFeatures { pub fn features(&self) -> &crate::PhysicalDeviceFeatures {
&self.adapter.features &self.adapter.features
} }
pub(crate) fn properties(&self) -> &crate::PhysicalDeviceProperties { pub fn properties(&self) -> &crate::PhysicalDeviceProperties {
&self.adapter.properties &self.adapter.properties
} }
pub fn physical_device(&self) -> &PhysicalDeviceInfo { pub fn physical_device(&self) -> &PhysicalDeviceInfo {
@ -627,8 +718,8 @@ impl DeviceInner {
#[derive(Clone)] #[derive(Clone)]
pub struct DeviceOwnedDebugObject<T> { pub struct DeviceOwnedDebugObject<T> {
pub(crate) device: Device, pub device: Device,
pub(crate) object: T, pub object: T,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
name: Option<Cow<'static, str>>, name: Option<Cow<'static, str>>,
} }
@ -716,6 +807,60 @@ impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Buffer {
} }
} }
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Image {
unsafe fn destroy(self, device: &T) {
unsafe { device.as_ref().raw.destroy_image(self, None) };
}
}
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> 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<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::DescriptorPool {
unsafe fn destroy(self, device: &T) {
unsafe { device.as_ref().raw.destroy_descriptor_pool(self, None) };
}
}
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::DescriptorSetLayout {
unsafe fn destroy(self, device: &T) {
unsafe { device.as_ref().raw.destroy_descriptor_set_layout(self, None) };
}
}
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::PipelineLayout {
unsafe fn destroy(self, device: &T) {
unsafe { device.as_ref().raw.destroy_pipeline_layout(self, None) };
}
}
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Sampler {
unsafe fn destroy(self, device: &T) {
unsafe { device.as_ref().raw.destroy_sampler(self, None) };
}
}
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::ShaderModule {
unsafe fn destroy(self, device: &T) {
unsafe { device.as_ref().raw.destroy_shader_module(self, None) };
}
}
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::Pipeline {
unsafe fn destroy(self, device: &T) {
unsafe { device.as_ref().raw.destroy_pipeline(self, None) };
}
}
impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::SwapchainKHR { impl<T: AsRef<DeviceInner>> ExternallyManagedObject<T> for vk::SwapchainKHR {
unsafe fn destroy(self, device: &T) { unsafe fn destroy(self, device: &T) {
unsafe { unsafe {
@ -737,8 +882,8 @@ pub trait Pooled: Sized {
#[derive(Debug)] #[derive(Debug)]
pub struct Pool<T> { pub struct Pool<T> {
pub(crate) pool: Mutex<Vec<T>>, pub pool: Mutex<Vec<T>>,
pub(crate) device: Arc<DeviceInner>, pub device: Arc<DeviceInner>,
} }
impl<T> Pool<T> { impl<T> Pool<T> {
@ -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. // This module is an experiment in a more generic way to manage device-owned resources.
// #[cfg(false)] // #[cfg(false)]
pub(crate) mod asdf { pub mod asdf {
use std::{ use std::{
mem::{ManuallyDrop, MaybeUninit}, mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
@ -860,7 +1005,7 @@ pub(crate) mod asdf {
use ash::vk; use ash::vk;
use ash::vk::Handle; use ash::vk::Handle;
use crate::{device::DeviceInner, util::DebugName}; use crate::device::{DebugName, DeviceInner};
pub mod traits { pub mod traits {
/// A trait describing an object owned by some manager-type, which is /// A trait describing an object owned by some manager-type, which is
@ -961,7 +1106,6 @@ pub(crate) mod asdf {
pub type InnerDeviceObject<T> = DeviceObject<T, Arc<DeviceInner>>; pub type InnerDeviceObject<T> = DeviceObject<T, Arc<DeviceInner>>;
/// A wrapper for vulkan types which are owned by the device, taking care of destruction. /// A wrapper for vulkan types which are owned by the device, taking care of destruction.
#[derive(Debug)]
pub struct DeviceObject< pub struct DeviceObject<
T: traits::ExternallyManagedObject<O>, T: traits::ExternallyManagedObject<O>,
O: AsRef<super::DeviceInner> = super::Device, O: AsRef<super::DeviceInner> = super::Device,

View file

@ -13,7 +13,6 @@ use crate::{
Error, PhysicalDeviceFeatures, PhysicalDeviceInfo, SurfaceCapabilities, Error, PhysicalDeviceFeatures, PhysicalDeviceInfo, SurfaceCapabilities,
device::{Extension, get_extensions, get_layers}, device::{Extension, get_extensions, get_layers},
get_physical_device_features, get_physical_device_properties, make_extension, get_physical_device_features, get_physical_device_properties, make_extension,
swapchain::Surface,
}; };
pub struct DebugUtilsCreateInfo { pub struct DebugUtilsCreateInfo {
@ -21,21 +20,21 @@ pub struct DebugUtilsCreateInfo {
pub message_type: vk::DebugUtilsMessageTypeFlagsEXT, pub message_type: vk::DebugUtilsMessageTypeFlagsEXT,
} }
pub(crate) struct InstanceExtensions { pub struct InstanceExtensions {
pub(crate) get_surface_capabilities2: Option<khr::get_surface_capabilities2::Instance>, pub get_surface_capabilities2: Option<khr::get_surface_capabilities2::Instance>,
pub(crate) surface_maintenance1: Option<()>, pub surface_maintenance1: Option<()>,
} }
pub struct InstanceInner { pub struct InstanceInner {
pub raw: ash::Instance, pub raw: ash::Instance,
pub(crate) extensions: InstanceExtensions, pub extensions: InstanceExtensions,
pub entry: Entry, pub entry: Entry,
pub(crate) _debug_utils: DebugUtils, pub _debug_utils: DebugUtils,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Instance { pub struct Instance {
pub(crate) inner: Arc<InstanceInner>, pub inner: Arc<InstanceInner>,
} }
impl Deref for Instance { impl Deref for Instance {
@ -255,28 +254,23 @@ impl Instance {
/// extensions are available, it will also query present mode compatibility /// extensions are available, it will also query present mode compatibility
/// information to filter the present modes based on the provided initial /// information to filter the present modes based on the provided initial
/// mode. /// mode.
pub(crate) fn get_adapter_surface_capabilities( pub fn get_adapter_surface_capabilities(
&self, &self,
pdev: vk::PhysicalDevice, pdev: vk::PhysicalDevice,
surface: &Surface, functor: &khr::surface::Instance,
raw: vk::SurfaceKHR,
present_mode: Option<vk::PresentModeKHR>, present_mode: Option<vk::PresentModeKHR>,
) -> crate::Result<SurfaceCapabilities> { ) -> crate::Result<SurfaceCapabilities> {
let formats = unsafe { let formats = unsafe {
surface functor.get_physical_device_surface_formats(pdev, raw)?
.functor
.get_physical_device_surface_formats(pdev, surface.raw)?
}; };
let present_modes = unsafe { let present_modes = unsafe {
surface functor.get_physical_device_surface_present_modes(pdev, raw)?
.functor
.get_physical_device_surface_present_modes(pdev, surface.raw)?
}; };
let generic_caps = unsafe { let generic_caps = unsafe {
surface functor.get_physical_device_surface_capabilities(pdev, raw)?
.functor
.get_physical_device_surface_capabilities(pdev, surface.raw)?
}; };
let (capabilities, present_modes) = if let Some(ref functor) = let (capabilities, present_modes) = if let Some(ref functor) =
@ -285,7 +279,7 @@ impl Instance {
{ {
let mut capabilities = vk::SurfaceCapabilities2KHR::default(); let mut capabilities = vk::SurfaceCapabilities2KHR::default();
let mut surface_info = 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 present_mode = present_mode.unwrap_or(present_modes[0]);
let mut surface_present_mode = let mut surface_present_mode =
@ -324,7 +318,7 @@ impl Instance {
}) })
} }
pub(crate) fn expose_adapter( pub fn expose_adapter(
&self, &self,
pdev: vk::PhysicalDevice, pdev: vk::PhysicalDevice,
) -> crate::Result<PhysicalDeviceInfo> { ) -> crate::Result<PhysicalDeviceInfo> {
@ -338,9 +332,9 @@ impl Instance {
}) })
} }
pub(crate) fn choose_adapter_default( pub fn choose_adapter_default(
&self, &self,
surface: Option<&Surface>, surface: Option<vk::SurfaceKHR>,
required_extensions: &[Extension], required_extensions: &[Extension],
required_features: Option<&PhysicalDeviceFeatures>, required_features: Option<&PhysicalDeviceFeatures>,
) -> crate::Result<PhysicalDeviceInfo> { ) -> crate::Result<PhysicalDeviceInfo> {
@ -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... /// 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, &self,
pdev: vk::PhysicalDevice, pdev: vk::PhysicalDevice,
queue_family: u32, queue_family: u32,
@ -497,7 +491,7 @@ impl Instance {
} }
} }
pub(crate) struct DebugUtils { pub struct DebugUtils {
pub instance: ext::debug_utils::Instance, pub instance: ext::debug_utils::Instance,
pub messenger: vk::DebugUtilsMessengerEXT, pub messenger: vk::DebugUtilsMessengerEXT,
} }

View file

@ -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<Device>,
}
#[derive(renderer_macros::DeviceRef)]
struct GenericWrapper<D> {
#[device]
device: D,
tag: &'static str,
}
#[test]
fn derives_impl_device_ref() {
fn assert_impl<T: DeviceRef>() {}
assert_impl::<Wrapper>();
assert_impl::<ArcWrapper>();
assert_impl::<GenericWrapper<Device>>();
assert_impl::<GenericWrapper<Arc<Device>>>();
}
}
#[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<T> = core::result::Result<T, Error>;
#[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<T: vk::ExtendsPhysicalDeviceFeatures2 + Debug + Send + Sync> ExtendsDeviceFeatures2Debug
for T
{
}
impl<T: vk::ExtendsPhysicalDeviceProperties2 + Debug + DynClone + Send + Sync>
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<vk::SurfaceFormatKHR>,
pub present_modes: Vec<vk::PresentModeKHR>,
}
#[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<vk::PhysicalDeviceMeshShaderFeaturesEXT<'static>>,
}
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<vk::ExtensionProperties>,
/// 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<vk::PhysicalDeviceMeshShaderPropertiesEXT<'static>>,
/// 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::<u64>()
}
}
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<PhysicalDeviceProperties> {
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<vk::ExtensionProperties> {
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)
}
}

View file

@ -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<Arc<DeviceInner>> for PipelineCache {
unsafe fn destroy(self, owner: &Arc<DeviceInner>) {
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<u8>)> {
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<Self> {
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<Vec<u8>> {
let data = unsafe { device.get_pipeline_cache_data(self.raw)? };
Ok(data)
}
}

View file

@ -42,9 +42,9 @@ impl Queue {
#[derive(Debug)] #[derive(Debug)]
pub struct QueueInner { pub struct QueueInner {
pub(crate) raw: vk::Queue, pub raw: vk::Queue,
pub(crate) family: QueueFamily, pub family: QueueFamily,
pub(crate) lock: Mutex<()>, pub lock: Mutex<()>,
} }
impl QueueInner { impl QueueInner {

View file

@ -345,9 +345,9 @@ pub enum Semaphore {
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct BinarySemaphore(pub(crate) vk::Semaphore); pub struct BinarySemaphore(pub vk::Semaphore);
#[derive(Debug, Clone, Copy)] #[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 // This is just so that ash can name these semaphore newtypes
impl vk::Handle for BinarySemaphore { impl vk::Handle for BinarySemaphore {
@ -433,12 +433,12 @@ impl Semaphore {
let semaphore = match semaphore_type { let semaphore = match semaphore_type {
SemaphoreType::Binary => { SemaphoreType::Binary => {
let semaphore: SemaphoreInner = let semaphore: SemaphoreInner =
device.pools.binary_semaphores.get_debug_named(name)?.into(); device.pools().binary_semaphores.get_debug_named(name)?.into();
PoolObject::new(semaphore, device.pools.clone()) PoolObject::new(semaphore, device.pools().clone())
} }
SemaphoreType::Timeline(value) => { SemaphoreType::Timeline(value) => {
let semaphore: SemaphoreInner = device let semaphore: SemaphoreInner = device
.pools .pools()
.timeline_semaphores .timeline_semaphores
.get_debug_named(name)? .get_debug_named(name)?
.into(); .into();
@ -450,7 +450,7 @@ impl Semaphore {
device.raw.signal_semaphore(&info)?; device.raw.signal_semaphore(&info)?;
} }
PoolObject::new(semaphore, device.pools.clone()) PoolObject::new(semaphore, device.pools().clone())
} }
}; };