513 lines
17 KiB
Rust
513 lines
17 KiB
Rust
use std::{
|
|
cmp::Ordering,
|
|
ffi::{CStr, CString},
|
|
mem::MaybeUninit,
|
|
ops::Deref,
|
|
sync::Arc,
|
|
};
|
|
|
|
use ash::{Entry, ext, khr, vk};
|
|
use raw_window_handle::RawDisplayHandle;
|
|
|
|
use crate::{
|
|
Error, PhysicalDeviceFeatures, PhysicalDeviceInfo, SurfaceCapabilities,
|
|
device::{Extension, get_extensions, get_layers},
|
|
get_physical_device_features, get_physical_device_properties, make_extension,
|
|
swapchain::Surface,
|
|
};
|
|
|
|
pub struct DebugUtilsCreateInfo {
|
|
pub severity: vk::DebugUtilsMessageSeverityFlagsEXT,
|
|
pub message_type: vk::DebugUtilsMessageTypeFlagsEXT,
|
|
}
|
|
|
|
pub(crate) struct InstanceExtensions {
|
|
pub(crate) get_surface_capabilities2: Option<khr::get_surface_capabilities2::Instance>,
|
|
pub(crate) surface_maintenance1: Option<()>,
|
|
}
|
|
|
|
pub struct InstanceInner {
|
|
pub raw: ash::Instance,
|
|
pub(crate) extensions: InstanceExtensions,
|
|
pub entry: Entry,
|
|
pub(crate) _debug_utils: DebugUtils,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Instance {
|
|
pub(crate) inner: Arc<InstanceInner>,
|
|
}
|
|
|
|
impl Deref for Instance {
|
|
type Target = InstanceInner;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.inner
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Debug for Instance {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("Instance")
|
|
.field("raw", &self.inner.raw.handle())
|
|
.finish_non_exhaustive()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct InstanceDesc<'a> {
|
|
pub app_name: Option<&'a str>,
|
|
pub app_version: u32,
|
|
pub instance_extensions: &'a [Extension<'a>],
|
|
pub layer_settings: &'a [vk::LayerSettingEXT<'a>],
|
|
pub layers: &'a [&'a CStr],
|
|
pub display_handle: Option<RawDisplayHandle>,
|
|
}
|
|
|
|
pub const VALIDATION_LAYER: &CStr = c"VK_LAYER_KHRONOS_validation";
|
|
|
|
#[cfg(not(debug_assertions))]
|
|
const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[];
|
|
#[cfg(debug_assertions)]
|
|
const DEBUG_LAYER_SETTINGS: &[vk::LayerSettingEXT] = &[
|
|
vk::LayerSettingEXT {
|
|
p_layer_name: VALIDATION_LAYER.as_ptr(),
|
|
p_setting_name: c"validate_best_practices".as_ptr(),
|
|
value_count: 1,
|
|
p_values: &[1u8; 1] as *const u8 as _,
|
|
ty: vk::LayerSettingTypeEXT::BOOL32,
|
|
_marker: core::marker::PhantomData,
|
|
},
|
|
vk::LayerSettingEXT {
|
|
p_layer_name: VALIDATION_LAYER.as_ptr(),
|
|
p_setting_name: c"validate_best_practices_amd".as_ptr(),
|
|
value_count: 1,
|
|
p_values: &[1u8; 1] as *const u8 as _,
|
|
ty: vk::LayerSettingTypeEXT::BOOL32,
|
|
_marker: core::marker::PhantomData,
|
|
},
|
|
vk::LayerSettingEXT {
|
|
p_layer_name: VALIDATION_LAYER.as_ptr(),
|
|
p_setting_name: c"validate_sync".as_ptr(),
|
|
value_count: 1,
|
|
p_values: &[1u8; 1] as *const u8 as _,
|
|
ty: vk::LayerSettingTypeEXT::BOOL32,
|
|
_marker: core::marker::PhantomData,
|
|
},
|
|
];
|
|
|
|
impl Default for InstanceDesc<'_> {
|
|
fn default() -> Self {
|
|
Self {
|
|
app_name: None,
|
|
app_version: vk::make_api_version(0, 1, 0, 0),
|
|
instance_extensions: &[make_extension!(khr::surface)],
|
|
layer_settings: DEBUG_LAYER_SETTINGS,
|
|
layers: &[VALIDATION_LAYER],
|
|
display_handle: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Instance {
|
|
pub fn new<'a>(desc: &InstanceDesc<'a>) -> crate::Result<Self> {
|
|
let entry = unsafe { ash::Entry::load()? };
|
|
|
|
let app_name = desc
|
|
.app_name
|
|
.and_then(|name| CString::new(name).ok())
|
|
.unwrap_or(c"ShooterGame".to_owned());
|
|
|
|
let version =
|
|
unsafe { entry.try_enumerate_instance_version()? }.unwrap_or(vk::API_VERSION_1_0);
|
|
|
|
let app_info = vk::ApplicationInfo::default()
|
|
.api_version(if version < vk::API_VERSION_1_1 {
|
|
vk::API_VERSION_1_0
|
|
} else {
|
|
// ash doesn't support 1.4 yet
|
|
vk::API_VERSION_1_3
|
|
})
|
|
.application_name(&app_name)
|
|
.application_version(desc.app_version)
|
|
.engine_name(c"Bevy Engine")
|
|
.engine_version(vk::make_api_version(0, 0, 1, 0));
|
|
|
|
let mut validation_info =
|
|
vk::LayerSettingsCreateInfoEXT::default().settings(desc.layer_settings);
|
|
|
|
let layers = match get_layers(&entry, desc.layers.to_vec()) {
|
|
Ok(layers) => layers,
|
|
Err((supported, unsupported)) => {
|
|
tracing::error!(
|
|
"Some requested layers were not supported by instance:\nSupported: {:?}\nUnsupported: {:?}",
|
|
supported,
|
|
unsupported
|
|
);
|
|
supported
|
|
}
|
|
};
|
|
|
|
let mut requested_extensions = desc.instance_extensions.to_vec();
|
|
|
|
requested_extensions.push(make_extension!(ext::debug_utils as 1));
|
|
#[cfg(debug_assertions)]
|
|
requested_extensions.push(make_extension!(ext::layer_settings));
|
|
|
|
// These are wanted for surface capabilities querying, but not strictly
|
|
// required, and are only supported on newer devices, especially on
|
|
// NVIDIA cards.
|
|
requested_extensions.push(make_extension!(khr::get_surface_capabilities2));
|
|
requested_extensions.push(make_extension!(ext::surface_maintenance1));
|
|
|
|
let (extensions, unsupported_extensions) =
|
|
get_extensions(&entry, &layers, requested_extensions, desc.display_handle)?;
|
|
|
|
if !unsupported_extensions.is_empty() {
|
|
tracing::error!(
|
|
"extensions were requested but not supported by instance: {:?}",
|
|
unsupported_extensions
|
|
);
|
|
}
|
|
|
|
let layers = layers
|
|
.iter()
|
|
.map(|layer| layer.as_ptr())
|
|
.collect::<Vec<_>>();
|
|
let extension_names = extensions
|
|
.iter()
|
|
.map(|ext| ext.name.as_ptr())
|
|
.collect::<Vec<_>>();
|
|
|
|
let create_info = vk::InstanceCreateInfo::default()
|
|
.application_info(&app_info)
|
|
.enabled_extension_names(&extension_names)
|
|
.enabled_layer_names(&layers)
|
|
.push_next(&mut validation_info);
|
|
|
|
tracing::debug!(
|
|
"Creating instance:\napp_info: {app_info:#?}\ncreate_info: {create_info:#?}"
|
|
);
|
|
|
|
let instance = unsafe { entry.create_instance(&create_info, None)? };
|
|
|
|
let debug_utils = {
|
|
let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
|
|
.message_severity(
|
|
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR
|
|
| vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
|
|
| vk::DebugUtilsMessageSeverityFlagsEXT::INFO,
|
|
)
|
|
.message_type(
|
|
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
|
|
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
|
|
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
|
|
)
|
|
.pfn_user_callback(Some(crate::debug::debug_callback));
|
|
|
|
let instance = ext::debug_utils::Instance::new(&entry, &instance);
|
|
let messenger = unsafe { instance.create_debug_utils_messenger(&debug_info, None)? };
|
|
|
|
crate::DebugUtils {
|
|
instance,
|
|
messenger,
|
|
}
|
|
};
|
|
|
|
let extensions = InstanceExtensions {
|
|
get_surface_capabilities2: extensions
|
|
.contains(&make_extension!(khr::get_surface_capabilities2))
|
|
.then(|| khr::get_surface_capabilities2::Instance::new(&entry, &instance)),
|
|
surface_maintenance1: extensions
|
|
.contains(&make_extension!(ext::surface_maintenance1))
|
|
.then_some(()),
|
|
};
|
|
|
|
let instance = Arc::new(InstanceInner {
|
|
raw: instance,
|
|
extensions,
|
|
_debug_utils: debug_utils,
|
|
entry,
|
|
});
|
|
|
|
Ok(Self { inner: instance })
|
|
}
|
|
|
|
fn choose_adapter(
|
|
&self,
|
|
mut discriminator: impl FnMut(&PhysicalDeviceInfo, &PhysicalDeviceInfo) -> Ordering,
|
|
) -> crate::Result<PhysicalDeviceInfo> {
|
|
let pdevs = unsafe { self.inner.raw.enumerate_physical_devices()? };
|
|
let mut pdevs = pdevs
|
|
.into_iter()
|
|
.map(|pdev| -> crate::Result<PhysicalDeviceInfo> { self.expose_adapter(pdev) })
|
|
.filter_map(Result::ok)
|
|
.collect::<Vec<_>>();
|
|
|
|
pdevs.sort_unstable_by(&mut discriminator);
|
|
pdevs.pop().ok_or(Error::NoAdapter)
|
|
}
|
|
|
|
/// Queries the surface capabilities of the given physical device and
|
|
/// surface, including supported formats and present modes.
|
|
/// If a present mode is provided, and the
|
|
/// `VK_KHR_get_surface_capabilities2` and `VK_EXT_surface_maintenance1`
|
|
/// extensions are available, it will also query present mode compatibility
|
|
/// information to filter the present modes based on the provided initial
|
|
/// mode.
|
|
pub(crate) fn get_adapter_surface_capabilities(
|
|
&self,
|
|
pdev: vk::PhysicalDevice,
|
|
surface: &Surface,
|
|
present_mode: Option<vk::PresentModeKHR>,
|
|
) -> crate::Result<SurfaceCapabilities> {
|
|
let formats = unsafe {
|
|
surface
|
|
.functor
|
|
.get_physical_device_surface_formats(pdev, surface.raw)?
|
|
};
|
|
|
|
let present_modes = unsafe {
|
|
surface
|
|
.functor
|
|
.get_physical_device_surface_present_modes(pdev, surface.raw)?
|
|
};
|
|
|
|
let generic_caps = unsafe {
|
|
surface
|
|
.functor
|
|
.get_physical_device_surface_capabilities(pdev, surface.raw)?
|
|
};
|
|
|
|
let (capabilities, present_modes) = if let Some(ref functor) =
|
|
self.extensions.get_surface_capabilities2
|
|
&& self.extensions.surface_maintenance1.is_some()
|
|
{
|
|
let mut capabilities = vk::SurfaceCapabilities2KHR::default();
|
|
let mut surface_info =
|
|
vk::PhysicalDeviceSurfaceInfo2KHR::default().surface(surface.raw());
|
|
|
|
let present_mode = present_mode.unwrap_or(present_modes[0]);
|
|
let mut surface_present_mode =
|
|
vk::SurfacePresentModeEXT::default().present_mode(present_mode);
|
|
surface_info = surface_info.push_next(&mut surface_present_mode);
|
|
|
|
let mut present_modes = MaybeUninit::<[vk::PresentModeKHR; 8]>::uninit();
|
|
let mut present_mode_info = vk::SurfacePresentModeCompatibilityEXT::default()
|
|
.present_modes(unsafe { present_modes.assume_init_mut() });
|
|
capabilities = capabilities.push_next(&mut present_mode_info);
|
|
|
|
unsafe {
|
|
functor.get_physical_device_surface_capabilities2(
|
|
pdev,
|
|
&surface_info,
|
|
&mut capabilities,
|
|
)?;
|
|
}
|
|
|
|
(capabilities.surface_capabilities, unsafe {
|
|
let num_present_modes = present_mode_info.present_mode_count as usize;
|
|
present_modes.assume_init()[..num_present_modes].to_vec()
|
|
})
|
|
} else {
|
|
(generic_caps, present_modes)
|
|
};
|
|
|
|
// surface.functor
|
|
// .get_physical_device_surface_capabilities2(pdev, surface.raw, present_mode)?;
|
|
|
|
Ok(SurfaceCapabilities {
|
|
capabilities,
|
|
min_image_count: generic_caps.min_image_count,
|
|
formats,
|
|
present_modes,
|
|
})
|
|
}
|
|
|
|
pub(crate) fn expose_adapter(
|
|
&self,
|
|
pdev: vk::PhysicalDevice,
|
|
) -> crate::Result<PhysicalDeviceInfo> {
|
|
let properties = get_physical_device_properties(&self.inner, pdev)?;
|
|
let features = get_physical_device_features(&self.inner, pdev, &properties);
|
|
|
|
Ok(PhysicalDeviceInfo {
|
|
pdev,
|
|
properties,
|
|
features,
|
|
})
|
|
}
|
|
|
|
pub(crate) fn choose_adapter_default(
|
|
&self,
|
|
surface: Option<&Surface>,
|
|
required_extensions: &[Extension],
|
|
required_features: Option<&PhysicalDeviceFeatures>,
|
|
) -> crate::Result<PhysicalDeviceInfo> {
|
|
self.choose_adapter(|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
|
|
_ = surface;
|
|
|
|
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 {
|
|
pub instance: ext::debug_utils::Instance,
|
|
pub messenger: vk::DebugUtilsMessengerEXT,
|
|
}
|
|
|
|
impl Drop for DebugUtils {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
self.instance
|
|
.destroy_debug_utils_messenger(self.messenger, None);
|
|
}
|
|
}
|
|
}
|