Allow enabling additional vulkan extensions in Adapter::open and Instance::init (#7829)

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
Vecvec 2025-06-27 08:39:41 +12:00 committed by GitHub
parent 208fe4c23d
commit 0d61648eeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 151 additions and 34 deletions

View File

@ -59,6 +59,10 @@ Bottom level categories:
- Add acceleration structure limits. By @Vecvec in [#7845](https://github.com/gfx-rs/wgpu/pull/7845).
- Add support for clip-distances feature for Vulkan and GL backends. By @dzamkov in [#7730](https://github.com/gfx-rs/wgpu/pull/7730)
#### Vulkan
- Add ways to initialise the instance and open the adapter with callbacks to add extensions. By @Vecvec in [#7829](https://github.com/gfx-rs/wgpu/pull/7829).
### Bug Fixes
#### General

View File

@ -1,5 +1,5 @@
use alloc::{borrow::ToOwned as _, collections::BTreeMap, sync::Arc, vec::Vec};
use core::ffi::CStr;
use alloc::{borrow::ToOwned as _, boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
use core::{ffi::CStr, marker::PhantomData};
use ash::{ext, google, khr, vk};
use parking_lot::Mutex;
@ -2287,25 +2287,37 @@ impl super::Adapter {
pub fn texture_format_as_raw(&self, texture_format: wgt::TextureFormat) -> vk::Format {
self.private_caps.map_texture_format(texture_format)
}
}
impl crate::Adapter for super::Adapter {
type A = super::Api;
unsafe fn open(
/// # Safety:
/// - Same as `open` plus
/// - The callback may not change anything that the device does not support.
/// - The callback may not remove features.
pub unsafe fn open_with_callback<'a>(
&self,
features: wgt::Features,
_limits: &wgt::Limits,
memory_hints: &wgt::MemoryHints,
callback: Option<Box<super::CreateDeviceCallback<'a>>>,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
let enabled_extensions = self.required_device_extensions(features);
let mut enabled_extensions = self.required_device_extensions(features);
let mut enabled_phd_features = self.physical_device_features(&enabled_extensions, features);
let family_index = 0; //TODO
let family_info = vk::DeviceQueueCreateInfo::default()
.queue_family_index(family_index)
.queue_priorities(&[1.0]);
let family_infos = [family_info];
let mut family_infos = Vec::from([family_info]);
let mut pre_info = vk::DeviceCreateInfo::default();
if let Some(callback) = callback {
callback(super::CreateDeviceCallbackArgs {
extensions: &mut enabled_extensions,
device_features: &mut enabled_phd_features,
queue_create_infos: &mut family_infos,
create_info: &mut pre_info,
_phantom: PhantomData,
})
}
let str_pointers = enabled_extensions
.iter()
@ -2315,7 +2327,7 @@ impl crate::Adapter for super::Adapter {
})
.collect::<Vec<_>>();
let pre_info = vk::DeviceCreateInfo::default()
let pre_info = pre_info
.queue_create_infos(&family_infos)
.enabled_extension_names(&str_pointers);
let info = enabled_phd_features.add_to_device_create(pre_info);
@ -2351,6 +2363,19 @@ impl crate::Adapter for super::Adapter {
)
}
}
}
impl crate::Adapter for super::Adapter {
type A = super::Api;
unsafe fn open(
&self,
features: wgt::Features,
_limits: &wgt::Limits,
memory_hints: &wgt::MemoryHints,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
unsafe { self.open_with_callback(features, memory_hints, None) }
}
unsafe fn texture_format_capabilities(
&self,

View File

@ -1,6 +1,7 @@
use alloc::{borrow::ToOwned as _, boxed::Box, ffi::CString, string::String, sync::Arc, vec::Vec};
use core::{
ffi::{c_void, CStr},
marker::PhantomData,
slice,
str::FromStr,
};
@ -585,27 +586,19 @@ impl super::Instance {
swapchain: RwLock::new(None),
}
}
}
impl Drop for super::InstanceShared {
fn drop(&mut self) {
unsafe {
// Keep du alive since destroy_instance may also log
let _du = self.debug_utils.take().inspect(|du| {
du.extension
.destroy_debug_utils_messenger(du.messenger, None);
});
if self.drop_guard.is_none() {
self.raw.destroy_instance(None);
}
}
}
}
impl crate::Instance for super::Instance {
type A = super::Api;
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
/// `Instance::init` but with a callback.
/// If you want to add extensions, add the to the `Vec<'static CStr>` not the create info, otherwise
/// it will be overwritten
///
/// # Safety:
/// Same as `init` but additionally
/// - Callback must not remove features.
/// - Callback must not change anything to what the instance does not support.
pub unsafe fn init_with_callback(
desc: &crate::InstanceDescriptor,
callback: Option<Box<super::CreateInstanceCallback>>,
) -> Result<Self, crate::InstanceError> {
profiling::scope!("Init Vulkan Backend");
let entry = unsafe {
@ -654,7 +647,17 @@ impl crate::Instance for super::Instance {
},
);
let extensions = Self::desired_extensions(&entry, instance_api_version, desc.flags)?;
let mut extensions = Self::desired_extensions(&entry, instance_api_version, desc.flags)?;
let mut create_info = vk::InstanceCreateInfo::default();
if let Some(callback) = callback {
callback(super::CreateInstanceCallbackArgs {
extensions: &mut extensions,
create_info: &mut create_info,
entry: &entry,
_phantom: PhantomData,
});
}
let instance_layers = {
profiling::scope!("vkEnumerateInstanceLayerProperties");
@ -814,7 +817,7 @@ impl crate::Instance for super::Instance {
})
.collect::<Vec<_>>();
let mut create_info = vk::InstanceCreateInfo::default()
create_info = create_info
.flags(flags)
.application_info(&app_info)
.enabled_layer_names(&str_pointers[..layers.len()])
@ -876,6 +879,29 @@ impl crate::Instance for super::Instance {
)
}
}
}
impl Drop for super::InstanceShared {
fn drop(&mut self) {
unsafe {
// Keep du alive since destroy_instance may also log
let _du = self.debug_utils.take().inspect(|du| {
du.extension
.destroy_debug_utils_messenger(du.messenger, None);
});
if self.drop_guard.is_none() {
self.raw.destroy_instance(None);
}
}
}
}
impl crate::Instance for super::Instance {
type A = super::Api;
unsafe fn init(desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
unsafe { Self::init_with_callback(desc, None) }
}
unsafe fn create_surface(
&self,

View File

@ -36,7 +36,7 @@ mod semaphore_list;
pub use adapter::PhysicalDeviceFeatures;
use alloc::{boxed::Box, ffi::CString, sync::Arc, vec::Vec};
use core::{borrow::Borrow, ffi::CStr, fmt, mem, num::NonZeroU32};
use core::{borrow::Borrow, ffi::CStr, fmt, marker::PhantomData, mem, num::NonZeroU32};
use arrayvec::ArrayVec;
use ash::{ext, khr, vk};
@ -1500,3 +1500,65 @@ struct RawTlasInstance {
shader_binding_table_record_offset_and_flags: u32,
acceleration_structure_reference: u64,
}
/// Arguments to the [`CreateDeviceCallback`].
pub struct CreateDeviceCallbackArgs<'arg, 'pnext, 'this>
where
'this: 'pnext,
{
/// The extensions to enable for the device. You must not remove anything from this list,
/// but you may add to it.
pub extensions: &'arg mut Vec<&'static CStr>,
/// The physical device features to enable. You may enable features, but must not disable any.
pub device_features: &'arg mut PhysicalDeviceFeatures,
/// The queue create infos for the device. You may add or modify queue create infos as needed.
pub queue_create_infos: &'arg mut Vec<vk::DeviceQueueCreateInfo<'pnext>>,
/// The create info for the device. You may add or modify things in the pnext chain, but
/// do not turn features off. Additionally, do not add things to the list of extensions,
/// or to the feature set, as all changes to that member will be overwritten.
pub create_info: &'arg mut vk::DeviceCreateInfo<'pnext>,
/// We need to have `'this` in the struct, so we can declare that all lifetimes coming from
/// captures in the closure will live longer (and hence satisfy) `'pnext`. However, we
/// don't actually directly use `'this`
_phantom: PhantomData<&'this ()>,
}
/// Callback to allow changing the vulkan device creation parameters.
///
/// # Safety:
/// - If you want to add extensions, add the to the `Vec<'static CStr>` not the create info,
/// as the create info value will be overwritten.
/// - Callback must not remove features.
/// - Callback must not change anything to what the instance does not support.
pub type CreateDeviceCallback<'this> =
dyn for<'arg, 'pnext> FnOnce(CreateDeviceCallbackArgs<'arg, 'pnext, 'this>) + 'this;
/// Arguments to the [`CreateInstanceCallback`].
pub struct CreateInstanceCallbackArgs<'arg, 'pnext, 'this>
where
'this: 'pnext,
{
/// The extensions to enable for the instance. You must not remove anything from this list,
/// but you may add to it.
pub extensions: &'arg mut Vec<&'static CStr>,
/// The create info for the instance. You may add or modify things in the pnext chain, but
/// do not turn features off. Additionally, do not add things to the list of extensions,
/// all changes to that member will be overwritten.
pub create_info: &'arg mut vk::InstanceCreateInfo<'pnext>,
/// Vulkan entry point.
pub entry: &'arg ash::Entry,
/// We need to have `'this` in the struct, so we can declare that all lifetimes coming from
/// captures in the closure will live longer (and hence satisfy) `'pnext`. However, we
/// don't actually directly use `'this`
_phantom: PhantomData<&'this ()>,
}
/// Callback to allow changing the vulkan instance creation parameters.
///
/// # Safety:
/// - If you want to add extensions, add the to the `Vec<'static CStr>` not the create info,
/// as the create info value will be overwritten.
/// - Callback must not remove features.
/// - Callback must not change anything to what the instance does not support.
pub type CreateInstanceCallback<'this> =
dyn for<'arg, 'pnext> FnOnce(CreateInstanceCallbackArgs<'arg, 'pnext, 'this>) + 'this;