diff --git a/crates/renderer/src/device.rs b/crates/renderer/src/device.rs index 7c0bced..713f9c1 100644 --- a/crates/renderer/src/device.rs +++ b/crates/renderer/src/device.rs @@ -16,8 +16,7 @@ use tinyvec::{ArrayVec, array_vec}; use crate::{ Error, ExtendsDeviceProperties2Debug, Instance, PhysicalDevice, PhysicalDeviceFeatures, - PhysicalDeviceProperties, Queue, Result, VkNameList, instance::InstanceInner, - make_extention_properties, sync, + PhysicalDeviceInfo, PhysicalDeviceProperties, Queue, Result, instance::InstanceInner, sync, }; #[derive(Debug, Default)] @@ -127,6 +126,7 @@ pub struct DeviceInner { present_queue: Queue, sync_threadpool: sync::SyncThreadpool, features: crate::PhysicalDeviceFeatures, + properties: crate::PhysicalDeviceProperties, } impl core::fmt::Debug for DeviceInner { @@ -175,7 +175,6 @@ pub struct DeviceDesc<'a> { pub layer_settings: &'a [vk::LayerSettingEXT<'a>], pub instance_extensions: &'a [Extension<'a>], pub display_handle: Option, - pub features: crate::PhysicalDeviceFeatures, } const VALIDATION_LAYER_NAME: &core::ffi::CStr = c"VK_LAYER_KHRONOS_validation"; @@ -221,7 +220,6 @@ impl<'a> Default for DeviceDesc<'a> { layer_settings: &[], instance_extensions: Default::default(), display_handle: Default::default(), - features: Default::default(), } } } @@ -695,58 +693,6 @@ pub struct Device(Arc); pub type WeakDevice = std::sync::Weak; impl Device { - pub fn new_from_default_desc( - display_handle: Option, - with_instance_extensions: &[Extension<'_>], - ) -> crate::Result { - Self::new_from_desc(DeviceDesc { - app_name: Some("Vidya"), - app_version: vk::make_api_version(0, 0, 1, 0), - display_handle, - features: crate::PhysicalDeviceFeatures::default() - .version(vk::make_api_version(0, 1, 3, 0)) - .features10( - vk::PhysicalDeviceFeatures::default() - .sampler_anisotropy(true) - .fill_mode_non_solid(true) - .multi_draw_indirect(true), - ) - .features11( - vk::PhysicalDeviceVulkan11Features::default().shader_draw_parameters(true), - ) - .features12( - vk::PhysicalDeviceVulkan12Features::default() - .shader_int8(true) - .runtime_descriptor_array(true) - .descriptor_binding_partially_bound(true) - .shader_sampled_image_array_non_uniform_indexing(true) - .descriptor_binding_sampled_image_update_after_bind(true) - .storage_buffer8_bit_access(true), - ) - .with_extension( - make_extention_properties( - ash::ext::mesh_shader::NAME, - ash::ext::mesh_shader::SPEC_VERSION, - ), - vk::PhysicalDeviceMeshShaderFeaturesEXT::default() - .mesh_shader(true) - .task_shader(true), - ) - .with_extension( - make_extention_properties( - ash::ext::index_type_uint8::NAME, - ash::ext::index_type_uint8::SPEC_VERSION, - ), - vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default().index_type_uint8(true), - ) - .with_extensions2([make_extention_properties( - khr::spirv_1_4::NAME, - khr::spirv_1_4::SPEC_VERSION, - )]), - instance_extensions: with_instance_extensions, - ..Default::default() - }) - } pub fn new_from_desc(desc: DeviceDesc) -> crate::Result { let instance = Instance::new(&crate::instance::InstanceDesc { app_name: desc.app_name, @@ -761,28 +707,29 @@ impl Device { display_handle: desc.display_handle, })?; - let mut features = desc.features.with_extension2(make_extention_properties( - khr::swapchain::NAME, - khr::swapchain::SPEC_VERSION, - )); - - //these are required for the renderpass - let features13 = features.physical_features_13.get_or_insert_default(); - features13.synchronization2 = vk::TRUE; - features13.dynamic_rendering = vk::TRUE; - features13.maintenance4 = vk::TRUE; + // //these are required for the renderpass + // let features13 = features.physical_features_13.get_or_insert_default(); + // features13.synchronization2 = vk::TRUE; + // features13.dynamic_rendering = vk::TRUE; + // features13.maintenance4 = vk::TRUE; + let features = PhysicalDeviceFeatures { + core13: vk::PhysicalDeviceVulkan13Features { + synchronization2: vk::TRUE, + dynamic_rendering: vk::TRUE, + maintenance4: vk::TRUE, + ..Default::default() + }, + ..Default::default() + }; // Consider this: switching physical device in game? // anything above this point is device agnostic, everything below would have to be recreated // additionally, pdev would have to be derived from a device and not a scoring function. - let pdev = DeviceBuilder::choose_physical_device( - &instance.inner, - desc.display_handle, - &features, - vec![Box::new( - vk::PhysicalDeviceMeshShaderPropertiesEXT::default(), - )], + let pdev = instance.choose_adapter_default( + None, + &[make_extension!(ext::mesh_shader)], + Some(&features), )?; tracing::trace!("pdev: {pdev:?}"); @@ -793,7 +740,7 @@ impl Device { pub fn new( instance: Arc, - physical: PhysicalDevice, + physical: PhysicalDeviceInfo, mut features: crate::PhysicalDeviceFeatures, ) -> VkResult { // we have 4 queues at most: graphics, compute, transfer, present @@ -912,6 +859,9 @@ impl Device { pub fn features(&self) -> &crate::PhysicalDeviceFeatures { &self.0.features } + pub fn properties(&self) -> &crate::PhysicalDeviceProperties { + &self.0.properties + } pub fn physical_device(&self) -> &PhysicalDevice { &self.0.physical } diff --git a/crates/renderer/src/instance.rs b/crates/renderer/src/instance.rs index c1b6509..4834a8d 100644 --- a/crates/renderer/src/instance.rs +++ b/crates/renderer/src/instance.rs @@ -1,14 +1,17 @@ use std::{ + cmp::Ordering, ffi::{CStr, CString}, sync::Arc, }; -use ash::{Entry, ext, vk}; +use ash::{Entry, ext, khr, vk}; use raw_window_handle::RawDisplayHandle; use crate::{ + Error, PhysicalDeviceFeatures, PhysicalDeviceInfo, device::{Extension, get_extensions, get_layers}, - make_extension, + get_physical_device_features, get_physical_device_properties, make_extension, + swapchain::Surface, }; pub struct DebugUtilsCreateInfo { @@ -155,6 +158,207 @@ impl Instance { Ok(Self { inner: instance }) } + + fn choose_adapter( + &self, + surface: Option<&Surface>, + mut discriminator: impl FnMut(&PhysicalDeviceInfo, &PhysicalDeviceInfo) -> Ordering, + ) -> crate::Result { + let pdevs = unsafe { self.inner.raw.enumerate_physical_devices()? }; + let mut pdevs = pdevs + .into_iter() + .map(|pdev| -> crate::Result { + let properties = get_physical_device_properties(&self.inner, pdev)?; + let features = get_physical_device_features(&self.inner, pdev, &properties)?; + + let surface_capabilities = if let Some(surface) = surface { + let capabilities = unsafe { + surface + .functor + .get_physical_device_surface_capabilities(pdev, surface.raw)? + }; + + let formats = unsafe { + surface + .functor + .get_physical_device_surface_formats(pdev, surface.raw)? + }; + + Some((capabilities, formats)) + } else { + None + }; + + Ok(PhysicalDeviceInfo { + pdev, + properties, + features, + surface_capabilities, + }) + }) + .filter_map(Result::ok) + .collect::>(); + + pdevs.sort_unstable_by(&mut discriminator); + pdevs.pop().ok_or(Error::NoAdapter) + } + + pub(crate) fn choose_adapter_default( + &self, + surface: Option<&Surface>, + required_extensions: &[Extension], + required_features: Option<&PhysicalDeviceFeatures>, + ) -> crate::Result { + self.choose_adapter(surface, |a, b| { + // Extensions: we definitely need swapchain. + match a + .properties + .supports_extension(make_extension!(khr::swapchain)) + .cmp( + &b.properties + .supports_extension(make_extension!(khr::swapchain)), + ) { + Ordering::Equal => {} + other => return other, + } + + for ext in required_extensions { + match a.properties.supports_extension(*ext) { + true => {} + false => return Ordering::Less, + } + } + + // Check surface compatibility + // TODO + + if let Some(ref required_features) = required_features { + if b.features.superset_of(&required_features) + && !a.features.superset_of(&required_features) + { + return Ordering::Less; + } + } + + // check specific features that we need + match a + .features + .core13 + .synchronization2 + .cmp(&b.features.core13.synchronization2) + { + Ordering::Equal => {} + other => return other, + } + match a + .features + .core13 + .dynamic_rendering + .cmp(&b.features.core13.dynamic_rendering) + { + Ordering::Equal => {} + other => return other, + } + match a + .features + .core13 + .maintenance4 + .cmp(&b.features.core13.maintenance4) + { + Ordering::Equal => {} + other => return other, + } + + // Prefer discrete GPUs + let a_discrete = a.properties.core.device_type == vk::PhysicalDeviceType::DISCRETE_GPU; + let b_discrete = b.properties.core.device_type == vk::PhysicalDeviceType::DISCRETE_GPU; + + match (a_discrete, b_discrete) { + (true, false) => return Ordering::Greater, + (false, true) => return Ordering::Less, + _ => {} + } + + // Then prefer newer APIs + match a + .properties + .core + .api_version + .cmp(&b.properties.core.api_version) + { + Ordering::Equal => {} + other => return other, + } + + // Prefer larger memory + match a + .properties + .get_device_local_memory_count() + .cmp(&b.properties.get_device_local_memory_count()) + { + Ordering::Equal => {} + other => return other, + } + + // Prefer larger texture size + match a + .properties + .core + .limits + .max_image_dimension2_d + .cmp(&b.properties.core.limits.max_image_dimension2_d) + { + Ordering::Equal => {} + other => return other, + } + + Ordering::Equal + }) + } + + /// 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( + &self, + pdev: vk::PhysicalDevice, + queue_family: u32, + display_handle: RawDisplayHandle, + ) -> bool { + unsafe { + match display_handle { + RawDisplayHandle::Xlib(display) => { + let surface = + ash::khr::xlib_surface::Instance::new(&self.inner.entry, &self.inner.raw); + surface.get_physical_device_xlib_presentation_support( + pdev, + queue_family, + display.display.unwrap().as_ptr() as _, + display.screen as _, + ) + } + RawDisplayHandle::Xcb(_xcb_display_handle) => todo!("xcb"), + RawDisplayHandle::Wayland(wayland_display_handle) => { + let surface = ash::khr::wayland_surface::Instance::new( + &self.inner.entry, + &self.inner.raw, + ); + surface.get_physical_device_wayland_presentation_support( + pdev, + queue_family, + wayland_display_handle.display.cast().as_mut(), + ) + } + RawDisplayHandle::Drm(_) => { + // idk ? + true + } + RawDisplayHandle::Windows(_) => { + ash::khr::win32_surface::Instance::new(&self.inner.entry, &self.inner.raw) + .get_physical_device_win32_presentation_support(pdev, queue_family) + } + _ => panic!("unsupported platform"), + } + } + } } pub(crate) struct DebugUtils { diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index 279e0aa..79263f9 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -8,14 +8,15 @@ use std::{collections::HashMap, ffi::CStr, fmt::Debug, marker::PhantomData, sync::Arc}; +use bitflags::bitflags; use raw_window_handle::{RawDisplayHandle, RawWindowHandle}; use parking_lot::{Mutex, MutexGuard}; use ash::{ - Entry, ext, + Entry, ext, khr, prelude::VkResult, - vk::{self}, + vk::{self, SurfaceCapabilitiesKHR}, }; use dyn_clone::DynClone; @@ -84,6 +85,8 @@ 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)] @@ -155,6 +158,9 @@ fn compatible_extension_properties( } } +mod queue; + +// Queues must be externally synchronised for calls to `vkQueueSubmit` and `vkQueuePresentKHR`. #[derive(Clone, Debug)] pub struct Queue(Arc>, u32); @@ -200,230 +206,434 @@ impl { } +#[derive(Debug)] +pub struct PhysicalDeviceInfo { + pub pdev: vk::PhysicalDevice, + pub properties: PhysicalDeviceProperties, + pub features: PhysicalDeviceFeatures, + pub surface_capabilities: Option<(SurfaceCapabilitiesKHR, Vec)>, +} + #[derive(Default, Debug)] pub struct PhysicalDeviceFeatures { - pub version: u32, - pub physical_features_10: vk::PhysicalDeviceFeatures, - pub physical_features_11: Option>, - pub physical_features_12: Option>, - pub physical_features_13: Option>, - pub extra_features: Vec>, - pub device_extensions: Vec, + pub core: vk::PhysicalDeviceFeatures, + pub core11: vk::PhysicalDeviceVulkan11Features<'static>, + pub core12: vk::PhysicalDeviceVulkan12Features<'static>, + pub core13: vk::PhysicalDeviceVulkan13Features<'static>, + pub mesh_shader: Option>, } impl PhysicalDeviceFeatures { - fn version(self, version: u32) -> Self { - Self { version, ..self } - } - fn all_default() -> Self { - Self::default() - .features11(Default::default()) - .features12(Default::default()) - .features13(Default::default()) - } - - fn query(instance: &ash::Instance, pdev: vk::PhysicalDevice) -> Result { - let mut this = Self::all_default(); - let mut features2 = this.features2(); - let features = unsafe { - instance.get_physical_device_features2(pdev, &mut features2); - // allocate and query again - features2.features - }; - this = this.features10(features); - - let extensions = unsafe { instance.enumerate_device_extension_properties(pdev)? }; - this = this.device_extensions(extensions); - - Ok(this) - } - - fn features10(self, physical_features_10: vk::PhysicalDeviceFeatures) -> Self { - Self { - physical_features_10, - ..self - } - } - fn features11(self, physical_features_11: vk::PhysicalDeviceVulkan11Features<'static>) -> Self { - Self { - physical_features_11: Some(physical_features_11), - ..self - } - } - fn features12(self, physical_features_12: vk::PhysicalDeviceVulkan12Features<'static>) -> Self { - Self { - physical_features_12: Some(physical_features_12), - ..self - } - } - fn features13(self, physical_features_13: vk::PhysicalDeviceVulkan13Features<'static>) -> Self { - Self { - physical_features_13: Some(physical_features_13), - ..self - } - } - fn device_extensions(self, device_extensions: Vec) -> Self { - Self { - device_extensions, - ..self - } - } - - #[allow(dead_code)] - fn with_extension2(mut self, ext: vk::ExtensionProperties) -> Self { - self.device_extensions.push(ext); - - self - } - - fn with_extensions2>( - mut self, - exts: I, - ) -> Self { - self.device_extensions.extend(exts); - - self - } - - fn with_extension(mut self, ext: vk::ExtensionProperties, features: F) -> Self - where - F: ExtendsDeviceFeatures2Debug + 'static, - { - self.extra_features.push(Box::new(features)); - self.device_extensions.push(ext); - - self - } - - fn features2(&mut self) -> vk::PhysicalDeviceFeatures2<'_> { - let mut features2 = - vk::PhysicalDeviceFeatures2::default().features(self.physical_features_10); - - if let Some(ref mut features11) = self.physical_features_11 { - features2 = features2.push_next(features11); - } - if let Some(ref mut features12) = self.physical_features_12 { - features2 = features2.push_next(features12); - } - if let Some(ref mut features13) = self.physical_features_13 { - features2 = features2.push_next(features13); + 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) } - for features in self.extra_features.iter_mut() { - features2 = features2.push_next(Box::as_mut(features)); + 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) } - features2 - } + 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 supports_extension(&self, e: &vk::ExtensionProperties) -> bool { - self.device_extensions.iter().any(|ext| { - ext.extension_name_as_c_str() == e.extension_name_as_c_str() - && ext.spec_version >= e.spec_version - }) - } + 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 compatible_with(&self, device: &Self) -> bool { - let sort_exts = |a: &vk::ExtensionProperties, b: &vk::ExtensionProperties| { - (a.extension_name_as_c_str().unwrap(), a.spec_version) - .cmp(&(b.extension_name_as_c_str().unwrap(), b.spec_version)) - }; - let mut device_extensions = device.device_extensions.clone(); - device_extensions.sort_by(sort_exts); + 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) + } - let unsupported_extensions = self - .device_extensions - .iter() - .filter(|ext| { - device_extensions - .binary_search_by(|t| sort_exts(t, ext)) - .is_err() - }) - .cloned() - .collect::>(); - - let supports_extensions = unsupported_extensions.is_empty(); - - supports_extensions - && utils::eq_device_features10( - &utils::bitand_device_features10( - &self.physical_features_10, - &device.physical_features_10, - ), - &self.physical_features_10, - ) - && self - .physical_features_11 - .zip(device.physical_features_11) - .map(|(a, b)| { - utils::eq_device_features11(&utils::bitand_device_features11(&a, &b), &a) - }) - .unwrap_or(true) - && self - .physical_features_12 - .zip(device.physical_features_12) - .map(|(a, b)| { - utils::eq_device_features12(&utils::bitand_device_features12(&a, &b), &a) - }) - .unwrap_or(true) - && self - .physical_features_13 - .zip(device.physical_features_13) - .map(|(a, b)| { - utils::eq_device_features13(&utils::bitand_device_features13(&a, &b), &a) - }) - .unwrap_or(true) + 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)] -struct PhysicalDeviceProperties { - base: vk::PhysicalDeviceProperties, - vk11: vk::PhysicalDeviceVulkan11Properties<'static>, - vk12: vk::PhysicalDeviceVulkan12Properties<'static>, - vk13: vk::PhysicalDeviceVulkan13Properties<'static>, - extra_properties: Vec>, +pub(crate) struct PhysicalDeviceProperties { + /// Extensions supported by the device, as reported by `enumerate_device_extension_properties`. + supported_extensions: Vec, + /// Vulkan 1.0 properties. + core: vk::PhysicalDeviceProperties, + /// Vulkan 1.1 properties. + core11: vk::PhysicalDeviceVulkan11Properties<'static>, + /// Vulkan 1.2 properties. + core12: vk::PhysicalDeviceVulkan12Properties<'static>, + /// Vulkan 1.3 properties. + core13: vk::PhysicalDeviceVulkan13Properties<'static>, + /// Mesh shader properties from the `VK_EXT_mesh_shader` extension, if supported. + mesh_shader: Option>, + /// Memory properties of the device, as reported by `get_physical_device_memory_properties`. + memory_properties: vk::PhysicalDeviceMemoryProperties, + /// The API version supported by the device. + device_api_version: u32, } impl PhysicalDeviceProperties { - fn query(&mut self, instance: &ash::Instance, pdev: vk::PhysicalDevice) { - let mut props2 = self.properties2(); - unsafe { - instance.get_physical_device_properties2(pdev, &mut props2); - self.base = props2.properties; - } + pub(crate) fn get_device_local_memory_count(&self) -> u64 { + self.memory_properties + .memory_heaps + .iter() + .filter(|heap| heap.flags.contains(vk::MemoryHeapFlags::DEVICE_LOCAL)) + .map(|heap| heap.size) + .sum::() + } +} + +fn get_physical_device_features( + instance: &InstanceInner, + pdev: vk::PhysicalDevice, + properties: &PhysicalDeviceProperties, +) -> Result { + let mut features = PhysicalDeviceFeatures::default(); + let mut features2 = vk::PhysicalDeviceFeatures2::default(); + + if properties.supports_extension(make_extension!(ext::mesh_shader)) { + features2 = features2.push_next( + features + .mesh_shader + .insert(vk::PhysicalDeviceMeshShaderFeaturesEXT::default()), + ); } - fn extra_properties( - mut self, - extra_properties: Vec>, - ) -> Self { - self.extra_properties = extra_properties; - self + unsafe { + instance + .raw + .get_physical_device_features2(pdev, &mut features2); } - #[allow(dead_code)] - fn with_properties(mut self, properties: F) -> Self - where - F: ExtendsDeviceProperties2Debug + 'static, - { - self.extra_properties.push(Box::new(properties)); + todo!() +} - self +fn get_physical_device_properties( + instance: &InstanceInner, + pdev: vk::PhysicalDevice, +) -> Result { + let mut props = PhysicalDeviceProperties::default(); + props.core = unsafe { instance.raw.get_physical_device_properties(pdev) }; + props.device_api_version = props.core.api_version; + props.supported_extensions = + unsafe { instance.raw.enumerate_device_extension_properties(pdev)? }; + props.memory_properties = unsafe { instance.raw.get_physical_device_memory_properties(pdev) }; + + let supports_mesh_shader = props.supports_extension(make_extension!(ext::mesh_shader)); + + // TODO: fallibly check anything beyond 1.0 + let mut props2 = vk::PhysicalDeviceProperties2::default() + .push_next(&mut props.core11) + .push_next(&mut props.core12) + .push_next(&mut props.core13); + + if supports_mesh_shader { + let next = props + .mesh_shader + .insert(vk::PhysicalDeviceMeshShaderPropertiesEXT::default()); + props2 = props2.push_next(next); } - fn properties2(&mut self) -> vk::PhysicalDeviceProperties2<'_> { - let mut props2 = vk::PhysicalDeviceProperties2::default() - .properties(self.base) - .push_next(&mut self.vk11) - .push_next(&mut self.vk12) - .push_next(&mut self.vk13); + unsafe { + instance + .raw + .get_physical_device_properties2(pdev, &mut props2); + } - for props in &mut self.extra_properties { - props2 = props2.push_next(Box::as_mut(props)); - } + Ok(props) +} - props2 +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) } } @@ -788,6 +998,8 @@ impl Renderer2 { pub use vk::Extent2D; +use crate::{device::Extension, instance::InstanceInner}; + pub mod utils { #![allow(dead_code)] diff --git a/crates/renderer/src/pipeline.rs b/crates/renderer/src/pipeline.rs index d23f7a2..0deade2 100644 --- a/crates/renderer/src/pipeline.rs +++ b/crates/renderer/src/pipeline.rs @@ -1,10 +1,11 @@ use std::{borrow::Cow, path::Path, sync::Arc}; -use ash::{prelude::*, vk}; +use ash::{ext, prelude::*, vk}; use crate::{ define_device_owned_handle, device::{Device, DeviceOwnedDebugObject}, + make_extension, }; #[derive(Debug)] @@ -303,13 +304,10 @@ impl DescriptorSetLayout { .bindings(&bindings) .flags(desc.flags); - if device.features().version >= vk::API_VERSION_1_2 + if device.properties().device_api_version >= vk::API_VERSION_1_2 || device - .features() - .supports_extension(&crate::make_extention_properties( - ash::ext::descriptor_indexing::NAME, - ash::ext::descriptor_indexing::SPEC_VERSION, - )) + .properties() + .supports_extension(make_extension!(ext::descriptor_indexing)) { info = info.push_next(flags); } diff --git a/crates/renderer/src/queue.rs b/crates/renderer/src/queue.rs new file mode 100644 index 0000000..358074c --- /dev/null +++ b/crates/renderer/src/queue.rs @@ -0,0 +1,189 @@ +use bitflags::bitflags; +use raw_window_handle::RawDisplayHandle; +use std::sync::{Arc, Mutex}; + +use ash::vk; + +use crate::{Instance, PhysicalDeviceInfo, Result, instance::InstanceInner}; + +#[derive(Debug)] +pub struct Queue { + inner: Arc, +} + +#[derive(Debug)] +pub struct QueueInner { + queue: vk::Queue, + family: QueueFamily, + lock: Mutex<()>, +} + +#[derive(Debug, Clone, Copy)] +pub struct QueueFamily { + pub index: u32, + pub count: u32, + pub flags: QueueFlags, + pub granularity: TransferGranuality, + pub timestamp_valid_bits: u32, +} + +bitflags! { +#[derive(Debug, Clone, Copy)] + pub struct QueueFlags: u16 { + const GRAPHICS = 0b1; + const COMPUTE = 0b10; + const TRANSFER = 0b100; + const PRESENT = 0b1000; + const ENCODE = 0b1_0000; + const DECODE = 0b10_0000; + } +} + +impl QueueFlags { + pub fn from_vk_flags(vk_flags: vk::QueueFlags) -> Self { + let mut flags = QueueFlags::empty(); + if vk_flags.contains(vk::QueueFlags::GRAPHICS) { + flags |= QueueFlags::GRAPHICS; + } + if vk_flags.contains(vk::QueueFlags::COMPUTE) { + flags |= QueueFlags::COMPUTE; + } + if vk_flags.contains(vk::QueueFlags::TRANSFER) { + flags |= QueueFlags::TRANSFER; + } + if vk_flags.contains(vk::QueueFlags::VIDEO_ENCODE_KHR) { + flags |= QueueFlags::ENCODE; + } + if vk_flags.contains(vk::QueueFlags::VIDEO_DECODE_KHR) { + flags |= QueueFlags::DECODE; + } + flags + } +} + +#[derive(Debug, Clone, Copy)] +pub enum TransferGranuality { + FullImage, + Unrestricted, + Tiled(u32, u32, u32), +} + +impl TransferGranuality { + pub fn from_vk_granularity(vk_granularity: vk::Extent3D) -> Self { + if vk_granularity.width == 0 || vk_granularity.height == 0 || vk_granularity.depth == 0 { + TransferGranuality::FullImage + } else if vk_granularity.width == 1 + && vk_granularity.height == 1 + && vk_granularity.depth == 1 + { + TransferGranuality::Unrestricted + } else { + TransferGranuality::Tiled( + vk_granularity.width, + vk_granularity.height, + vk_granularity.depth, + ) + } + } +} + +#[derive(Debug)] +pub struct DeviceQueues { + graphics: Queue, + compute: Queue, + transfer: Queue, +} + +#[derive(Debug)] +pub struct DeviceQueueInfos { + graphics: QueueFamily, + compute: Option, + transfer: Option, +} + +impl DeviceQueueInfos { + pub fn select_queue_families( + instance: &Instance, + pdev: &PhysicalDeviceInfo, + display_handle: Option, + ) -> Result { + let queue_families = unsafe { + instance + .inner + .raw + .get_physical_device_queue_family_properties(pdev.pdev) + }; + + let queue_families = queue_families + .into_iter() + .enumerate() + .map(|(index, props)| { + let mut flags = QueueFlags::from_vk_flags(props.queue_flags); + if let Some(display_handle) = display_handle { + if instance.query_presentation_support(pdev.pdev, index as u32, display_handle) + { + flags |= QueueFlags::PRESENT; + } + } + + QueueFamily { + index: index as u32, + count: props.queue_count, + flags, + granularity: TransferGranuality::from_vk_granularity( + props.min_image_transfer_granularity, + ), + timestamp_valid_bits: props.timestamp_valid_bits, + } + }) + .collect::>(); + + let mut queue_counts = queue_families + .iter() + .map(|family| family.count) + .collect::>(); + + let graphics = queue_families + .iter() + .enumerate() + .find(|(_, family)| { + family + .flags + .contains(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) + }) + .map(|(i, family)| { + queue_counts[i] -= 1; + *family + }) + .expect("No graphics queue family found"); + + assert!( + graphics.flags.contains(QueueFlags::PRESENT), + "Selected graphics queue family does not support presentation" + ); + + let compute = queue_families + .iter() + .enumerate() + .find(|(i, family)| queue_counts[*i] > 0 && family.flags.contains(QueueFlags::COMPUTE)) + .map(|(i, family)| { + queue_counts[i] -= 1; + *family + }); + + let transfer = queue_families + .iter() + .enumerate() + .find(|(i, family)| queue_counts[*i] > 0 && family.flags.contains(QueueFlags::TRANSFER)) + .map(|(i, family)| { + queue_counts[i] -= 1; + *family + }); + + Ok(Self { + graphics, + compute, + transfer, + }) + } +} diff --git a/crates/renderer/src/swapchain.rs b/crates/renderer/src/swapchain.rs index df900c5..f4fedfc 100644 --- a/crates/renderer/src/swapchain.rs +++ b/crates/renderer/src/swapchain.rs @@ -27,9 +27,9 @@ use derive_more::Debug; #[derive(Debug)] pub struct Surface { - raw: vk::SurfaceKHR, + pub(crate) raw: vk::SurfaceKHR, #[debug(skip)] - functor: khr::surface::Instance, + pub(crate) functor: khr::surface::Instance, } impl Drop for Surface {