instance refactor

This commit is contained in:
janis 2026-03-26 23:18:02 +01:00
parent 41e4a2ed9c
commit 20f743d74b
4 changed files with 380 additions and 294 deletions

View file

@ -16,7 +16,8 @@ use tinyvec::{ArrayVec, array_vec};
use crate::{
Error, ExtendsDeviceProperties2Debug, Instance, PhysicalDevice, PhysicalDeviceFeatures,
PhysicalDeviceProperties, Queue, Result, VkNameList, make_extention_properties, sync,
PhysicalDeviceProperties, Queue, Result, VkNameList, instance::InstanceInner,
make_extention_properties, sync,
};
#[derive(Debug, Default)]
@ -114,7 +115,7 @@ pub struct DeviceInner {
alloc: vk_mem::Allocator,
device: DeviceWrapper,
physical: PhysicalDevice,
instance: Arc<Instance>,
instance: Arc<InstanceInner>,
swapchain: khr::swapchain::Device,
debug_utils: ash::ext::debug_utils::Device,
allocated_queues: BTreeMap<(u32, u32), Queue>,
@ -137,18 +138,18 @@ impl core::fmt::Debug for DeviceInner {
}
#[macro_export]
macro_rules! make_extention {
macro_rules! make_extension {
($module:path) => {{
use $module::{NAME as EXTENSION_NAME, SPEC_VERSION as EXTENSION_VERSION};
Extension {
name: EXTENSION_NAME.to_str().unwrap(),
$crate::device::Extension {
name: EXTENSION_NAME,
version: EXTENSION_VERSION,
}
}};
($module:path as $version:expr) => {{
use $module::*;
Extension {
name: NAME.to_str().unwrap(),
$crate::device::Extension {
name: NAME,
version: $version,
}
}};
@ -156,7 +157,7 @@ macro_rules! make_extention {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Extension<'a> {
pub name: &'a str,
pub name: &'a CStr,
pub version: u32,
}
@ -229,7 +230,7 @@ struct DeviceBuilder;
impl DeviceBuilder {
fn queue_family_supports_presentation(
instance: &Instance,
instance: &Arc<InstanceInner>,
pdev: vk::PhysicalDevice,
queue_family: u32,
display_handle: RawDisplayHandle,
@ -270,7 +271,7 @@ impl DeviceBuilder {
}
fn select_pdev_queue_families(
instance: &Instance,
instance: &Arc<InstanceInner>,
display_handle: Option<RawDisplayHandle>,
pdev: vk::PhysicalDevice,
) -> DeviceQueueFamilies {
@ -454,7 +455,7 @@ impl DeviceBuilder {
}
fn choose_physical_device(
instance: &Instance,
instance: &Arc<InstanceInner>,
display_handle: Option<RawDisplayHandle>,
requirements: &PhysicalDeviceFeatures,
extra_properties: Vec<Box<dyn ExtendsDeviceProperties2Debug>>,
@ -505,8 +506,9 @@ impl DeviceBuilder {
properties,
})
}
}
fn get_available_extensions(
pub(crate) fn get_available_extensions(
entry: &ash::Entry,
layers: &[&CStr],
) -> Result<Vec<ash::vk::ExtensionProperties>> {
@ -526,14 +528,13 @@ impl DeviceBuilder {
}
/// returns a tuple of supported-or-enabled extensions and unsupported-and-requested extensions
fn get_extensions<'a>(
pub(crate) fn get_extensions<'a>(
entry: &ash::Entry,
layers: &[&'a CStr],
extensions: impl Iterator<Item = Extension<'a>> + 'a,
mut extensions: Vec<Extension<'a>>,
display_handle: Option<RawDisplayHandle>,
) -> Result<(HashSet<Extension<'a>>, HashSet<Extension<'a>>)> {
unsafe {
let available_extensions = Self::get_available_extensions(entry, layers)?;
let available_extensions = get_available_extensions(entry, layers)?;
let available_extension_names = available_extensions
.iter()
@ -550,102 +551,117 @@ impl DeviceBuilder {
available_extension_names.iter().collect::<Vec<_>>()
);
let mut out_extensions = HashSet::new();
let mut unsupported_extensions = HashSet::new();
let mut wsi_extensions = Vec::new();
wsi_extensions.push(make_extention!(khr::surface));
wsi_extensions.push(make_extension!(khr::surface));
// taken from wgpu-hal/src/vulkan/instance.rs:
//
// we want to enable all the wsi extensions that are applicable to the
// platform, even if the user didn't explicitly request them, or
// supplied a different/no display handle, because we might later want
// to create a surface for a different windowing system, and enabling
// all the wsi extensions doesn't have any real downsides.
// We don't notify the user if some of these extensions aren't available
// (e.g. because wayland isn't supported on some unix system)
if cfg!(all(
unix,
not(target_os = "android"),
not(target_os = "macos")
)) {
wsi_extensions.push(make_extention!(khr::xlib_surface));
wsi_extensions.push(make_extention!(khr::xcb_surface));
wsi_extensions.push(make_extention!(khr::wayland_surface));
wsi_extensions.push(make_extension!(khr::xlib_surface));
wsi_extensions.push(make_extension!(khr::xcb_surface));
wsi_extensions.push(make_extension!(khr::wayland_surface));
}
if cfg!(target_os = "windows") {
wsi_extensions.push(make_extention!(khr::win32_surface));
wsi_extensions.push(make_extension!(khr::win32_surface));
}
if cfg!(target_os = "android") {
wsi_extensions.push(make_extention!(khr::android_surface));
wsi_extensions.push(make_extension!(khr::android_surface));
}
if cfg!(target_os = "macos") {
wsi_extensions.push(make_extention!(ext::metal_surface));
wsi_extensions.push(make_extention!(khr::portability_enumeration));
wsi_extensions.push(make_extension!(ext::metal_surface));
wsi_extensions.push(make_extension!(khr::portability_enumeration));
}
if cfg!(all(
unix,
not(target_vendor = "apple"),
not(target_family = "wasm")
)) {
wsi_extensions.push(make_extention!(ext::acquire_drm_display));
wsi_extensions.push(make_extention!(ext::direct_mode_display));
wsi_extensions.push(make_extention!(khr::display));
wsi_extensions.push(make_extension!(ext::acquire_drm_display));
wsi_extensions.push(make_extension!(ext::direct_mode_display));
wsi_extensions.push(make_extension!(khr::display));
}
for extension in extensions {
if let Some(available_version) = available_extension_names.get(&extension.name)
&& *available_version >= extension.version
let is_extension_available = |ext: &mut Extension| -> bool {
if available_extensions
.iter()
.any(|inst_ext| inst_ext.extension_name_as_c_str() == Ok(ext.name))
{
out_extensions.insert(extension);
true
} else {
unsupported_extensions.insert(extension);
tracing::warn!(
"Extension {:?} v{} was requested but is not available",
ext.name,
ext.version
);
false
}
}
for extension in wsi_extensions {
if let Some(available_version) = available_extension_names.get(&extension.name)
&& *available_version >= extension.version
{
out_extensions.insert(extension);
}
// don't warn about missing WSI extensions, these might not all
// be needed and weren't requested by the user.
}
// if a display handle is provided, ensure the required WSI extensions are present
let required_extension_names = display_handle
.map(ash_window::enumerate_required_extensions)
.unwrap_or(Ok(&[]))?;
for &extension in required_extension_names {
let extension = core::ffi::CStr::from_ptr(extension);
let extension = Extension {
name: extension.to_str()?,
version: 0,
};
if let Some(available_version) = available_extension_names.get(&extension.name)
&& *available_version >= extension.version
{
out_extensions.insert(extension);
} else {
unsupported_extensions.insert(extension);
}
let mut enabled_extensions = extensions
.extract_if(.., is_extension_available)
.collect::<Vec<_>>();
enabled_extensions.extend(wsi_extensions.extract_if(.., is_extension_available));
// if a display handle is provided, ensure the required WSI extensions are present
if let Some(display_handle) = display_handle {
let mut required_extensions = ash_window::enumerate_required_extensions(display_handle)?
.iter()
.map(|&p| Extension {
name: unsafe { CStr::from_ptr(p) },
version: 0,
})
// filter out extensions that are already enabled
.filter(|ext| {
!enabled_extensions
.iter()
.any(|enabled| enabled.name == ext.name)
})
.collect::<Vec<_>>();
// filter out extensions that aren't available, and log a warning for them
let display_extensions = required_extensions.extract_if(.., is_extension_available);
enabled_extensions.extend(display_extensions);
extensions.extend(required_extensions);
}
// all extensions remaining in `extensions` at this point are unsupported,
// and were requested by the user or are required by the display handle
let unsupported_extensions = HashSet::from_iter(extensions);
let out_extensions = HashSet::from_iter(enabled_extensions);
Ok((out_extensions, unsupported_extensions))
}
}
fn get_layers<'a>(
/// returns a list of enabled, or a tuple of enabled and unsupported but requested layers.
pub(crate) fn get_layers<'a>(
entry: &ash::Entry,
wants_layers: impl Iterator<Item = &'a CStr> + 'a,
wants_layers: Vec<&'a CStr>,
) -> core::result::Result<Vec<&'a CStr>, (Vec<&'a CStr>, Vec<&'a CStr>)> {
unsafe {
let wants_layers = wants_layers.collect::<Vec<_>>();
let Ok(available_layers) = entry.enumerate_instance_layer_properties() else {
return Err((vec![], wants_layers));
};
let available_layers = entry
.enumerate_instance_layer_properties()
.map_err(|_| (Vec::<&'a CStr>::new(), wants_layers.clone()))?;
let available_layer_names = available_layers
let Ok(available_layer_names) = available_layers
.iter()
.map(|layer| layer.layer_name_as_c_str())
.collect::<core::result::Result<BTreeSet<_>, _>>()
.map_err(|_| (Vec::<&'a CStr>::new(), wants_layers.clone()))?;
else {
return Err((vec![], wants_layers));
};
tracing::debug!(
"Available layers: {:?}",
@ -655,22 +671,21 @@ impl DeviceBuilder {
.collect::<Vec<_>>()
);
let mut out_layers = Vec::new();
let mut enabled_layers = Vec::new();
let mut unsupported_layers = Vec::new();
for layer in wants_layers {
if available_layer_names.contains(&layer) {
out_layers.push(layer);
enabled_layers.push(layer);
} else {
unsupported_layers.push(layer);
}
}
if !unsupported_layers.is_empty() {
Err((out_layers, unsupported_layers))
Err((enabled_layers, unsupported_layers))
} else {
Ok(out_layers)
}
Ok(enabled_layers)
}
}
}
@ -733,92 +748,18 @@ impl Device {
})
}
pub fn new_from_desc(desc: DeviceDesc) -> crate::Result<Self> {
tracing::debug!("creating new device with: {desc:#?}");
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 app_info = vk::ApplicationInfo::default()
.api_version(desc.features.version)
.application_name(&app_name)
.application_version(desc.app_version)
.engine_name(c"VidyaEngine")
.engine_version(vk::make_api_version(0, 0, 1, 0));
let mut validation_info =
vk::LayerSettingsCreateInfoEXT::default().settings(desc.layer_settings);
let extra_instance_extensions = [
make_extention!(ext::debug_utils as 1),
#[cfg(debug_assertions)]
make_extention!(ext::layer_settings),
];
let layers = DeviceBuilder::get_layers(&entry, desc.layers.iter().cloned()).unwrap();
let (extensions, unsupported_extensions) = DeviceBuilder::get_extensions(
&entry,
&layers,
desc.instance_extensions
let instance = Instance::new(&crate::instance::InstanceDesc {
app_name: desc.app_name,
app_version: desc.app_version,
instance_extensions: &desc
.instance_extensions
.iter()
.cloned()
.chain(extra_instance_extensions),
desc.display_handle,
)?;
if !unsupported_extensions.is_empty() {
tracing::error!(
"extensions were requested but not supported by instance: {:?}",
unsupported_extensions
);
}
let layers = VkNameList::from_strs(&layers);
let extensions = crate::util::CStringList::from_iter(extensions.iter().map(|ext| ext.name));
let create_info = vk::InstanceCreateInfo::default()
.application_info(&app_info)
.enabled_extension_names(&extensions.strings)
.enabled_layer_names(&layers.names)
.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 instance = Arc::new(Instance {
raw: instance,
_debug_utils: debug_utils,
entry,
});
.map(|ext| ext.name)
.collect::<Vec<_>>(),
layers: desc.layers,
layer_settings: desc.layer_settings,
display_handle: desc.display_handle,
})?;
let mut features = desc.features.with_extension2(make_extention_properties(
khr::swapchain::NAME,
@ -836,7 +777,7 @@ impl Device {
// additionally, pdev would have to be derived from a device and not a scoring function.
let pdev = DeviceBuilder::choose_physical_device(
&instance,
&instance.inner,
desc.display_handle,
&features,
vec![Box::new(
@ -845,13 +786,13 @@ impl Device {
)?;
tracing::trace!("pdev: {pdev:?}");
let device = Device::new(instance.clone(), pdev, features)?;
let device = Device::new(instance.inner.clone(), pdev, features)?;
Ok(device)
}
pub fn new(
instance: Arc<Instance>,
instance: Arc<InstanceInner>,
physical: PhysicalDevice,
mut features: crate::PhysicalDeviceFeatures,
) -> VkResult<Self> {
@ -953,7 +894,7 @@ impl Device {
pub fn dev(&self) -> &ash::Device {
&self.0.device
}
pub fn instance(&self) -> &Arc<Instance> {
pub fn instance(&self) -> &Arc<InstanceInner> {
&self.0.instance
}
pub fn swapchain(&self) -> &khr::swapchain::Device {

View file

@ -1 +1,172 @@
use std::{
ffi::{CStr, CString},
sync::Arc,
};
use ash::{Entry, ext, vk};
use raw_window_handle::RawDisplayHandle;
use crate::{
device::{Extension, get_extensions, get_layers},
make_extension,
};
pub struct DebugUtilsCreateInfo {
pub severity: vk::DebugUtilsMessageSeverityFlagsEXT,
pub message_type: vk::DebugUtilsMessageTypeFlagsEXT,
}
pub struct InstanceInner {
pub raw: ash::Instance,
pub entry: Entry,
pub(crate) _debug_utils: DebugUtils,
}
#[derive(Clone)]
pub struct Instance {
pub(crate) inner: Arc<InstanceInner>,
}
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()
}
}
pub struct InstanceDesc<'a> {
pub app_name: Option<&'a str>,
pub app_version: u32,
pub instance_extensions: &'a [&'a CStr],
pub layer_settings: &'a [vk::LayerSettingEXT<'a>],
pub layers: &'a [&'a CStr],
pub display_handle: Option<RawDisplayHandle>,
}
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
.iter()
.map(|name| Extension { name, version: 0 })
.collect::<Vec<_>>();
requested_extensions.push(make_extension!(ext::debug_utils as 1));
#[cfg(debug_assertions)]
requested_extensions.push(make_extension!(ext::layer_settings));
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 extensions = extensions
.iter()
.map(|ext| ext.name.as_ptr())
.collect::<Vec<_>>();
let create_info = vk::InstanceCreateInfo::default()
.application_info(&app_info)
.enabled_extension_names(&extensions)
.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 instance = Arc::new(InstanceInner {
raw: instance,
_debug_utils: debug_utils,
entry,
});
Ok(Self { inner: instance })
}
}
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);
}
}
}

View file

@ -435,30 +435,8 @@ pub struct PhysicalDevice {
properties: PhysicalDeviceProperties,
}
struct DebugUtils {
instance: ext::debug_utils::Instance,
messenger: vk::DebugUtilsMessengerEXT,
}
impl Drop for DebugUtils {
fn drop(&mut self) {
unsafe {
self.instance
.destroy_debug_utils_messenger(self.messenger, None);
}
}
}
pub struct DebugUtilsCreateInfo {
pub severity: vk::DebugUtilsMessageSeverityFlagsEXT,
pub message_type: vk::DebugUtilsMessageTypeFlagsEXT,
}
pub struct Instance {
entry: Entry,
raw: ash::Instance,
_debug_utils: DebugUtils,
}
pub(crate) use instance::DebugUtils;
pub use instance::{DebugUtilsCreateInfo, Instance};
pub struct SamplerCache {
device: Device,

View file

@ -17,7 +17,9 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
use crate::{
Instance, Result, define_device_owned_handle,
device::{Device, DeviceOwned},
images, sync,
images,
instance::InstanceInner,
sync,
util::RawMutexGuard,
};
@ -40,7 +42,7 @@ impl Drop for Surface {
impl Surface {
#[allow(dead_code)]
pub fn headless(instance: &Arc<Instance>) -> Result<Self> {
pub fn headless(instance: &Arc<InstanceInner>) -> Result<Self> {
let headless_instance =
ash::ext::headless_surface::Instance::new(&instance.entry, &instance.raw);
let functor = khr::surface::Instance::new(&instance.entry, &instance.raw);
@ -67,7 +69,7 @@ impl Surface {
/// was created with the appropriate platform-specific surface extensions
/// enabled.
pub unsafe fn new_from_raw_window_handle(
instance: &Arc<Instance>,
instance: &Arc<InstanceInner>,
display_handle: RawDisplayHandle,
window_handle: RawWindowHandle,
) -> Result<Self> {
@ -704,7 +706,7 @@ impl WindowSurface {
#[cfg(test)]
mod tests {
use crate::device;
use crate::make_extension;
use super::*;
@ -712,14 +714,8 @@ mod tests {
let device = Device::new_from_default_desc(
None,
&[
device::Extension {
name: "VK_EXT_headless_surface",
version: ash::ext::headless_surface::SPEC_VERSION,
},
device::Extension {
name: "VK_KHR_surface",
version: ash::khr::surface::SPEC_VERSION,
},
make_extension!(ash::ext::headless_surface),
make_extension!(ash::khr::surface),
],
)?;