extract device into renderer_device
add deviceref macro
This commit is contained in:
parent
c20cee38de
commit
ce46ca1801
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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"] }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"] }
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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())?;
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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) => {
|
||||||
|
|
|
||||||
22
crates/renderer_device/Cargo.toml
Normal file
22
crates/renderer_device/Cargo.toml
Normal 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" }
|
||||||
|
|
@ -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,
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
626
crates/renderer_device/src/lib.rs
Normal file
626
crates/renderer_device/src/lib.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
116
crates/renderer_device/src/pipeline_cache.rs
Normal file
116
crates/renderer_device/src/pipeline_cache.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 {
|
||||||
|
|
@ -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())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Loading…
Reference in a new issue