Skip to content

Commit

Permalink
extensions: Separate wrappers between instance and device functions
Browse files Browse the repository at this point in the history
Not only is it more efficient to load specialized device functions via
`get_device_proc_addr()` instead of `get_instance_proc_addr()` (saves a
device-based jump in the ICD), loading instance functions via
`get_device_proc_addr()` (which was the case in `VK_KHR_swapchain`,
`VK_KHR_device_group` and `VK_EXT_full_screen_exclusive`) always results
in a `NULL` causing their instance functions panic no matter what.

Low-level `*Fn` structs are separated out between instance and device
pointers for every extension that contains both, forcing users
(typically high-level extension writers) to explicitly account for this.
  • Loading branch information
MarijnS95 committed Apr 3, 2023
1 parent ed2e5e2 commit 97e731c
Show file tree
Hide file tree
Showing 11 changed files with 375 additions and 197 deletions.
2 changes: 1 addition & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Inlined struct setters (#602)
- Bumped MSRV from 1.59 to 1.60 (#709)
- Replaced `const fn name()` with associated `NAME` constants (#715)
- Separate low-level `*Fn` structs an high-level extension wrappers between instance and device functions, for the following extensions: (#TODO)
- Separate low-level `*Fn` structs an high-level extension wrappers between instance and device functions, for the following extensions: (#734)
- `VK_KHR_swapchain`
- `VK_KHR_video_queue`
- `VK_KHR_device_group`
Expand Down
83 changes: 54 additions & 29 deletions ash/src/extensions/ext/calibrated_timestamps.rs
Original file line number Diff line number Diff line change
@@ -1,52 +1,38 @@
use crate::prelude::*;
use crate::vk;
use crate::{Entry, Instance};
use crate::{Device, Entry, Instance};
use std::ffi::CStr;
use std::mem;

/// High-level device function wrapper for
/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_calibrated_timestamps.html>
#[derive(Clone)]
pub struct CalibratedTimestamps {
handle: vk::Instance,
fp: vk::ExtCalibratedTimestampsFn,
pub struct CalibratedTimestampsDevice {
handle: vk::Device,
fp: vk::ExtCalibratedTimestampsDeviceFn,
}

impl CalibratedTimestamps {
pub fn new(entry: &Entry, instance: &Instance) -> Self {
let handle = instance.handle();
let fp = vk::ExtCalibratedTimestampsFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(handle, name.as_ptr()))
impl CalibratedTimestampsDevice {
pub fn new(instance: &Instance, device: &Device) -> Self {
let handle = device.handle();
let fp = vk::ExtCalibratedTimestampsDeviceFn::load(|name| unsafe {
mem::transmute(instance.get_device_proc_addr(handle, name.as_ptr()))
});
Self { handle, fp }
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetPhysicalDeviceCalibrateableTimeDomainsEXT.html>
#[inline]
pub unsafe fn get_physical_device_calibrateable_time_domains(
&self,
physical_device: vk::PhysicalDevice,
) -> VkResult<Vec<vk::TimeDomainEXT>> {
read_into_uninitialized_vector(|count, data| {
(self.fp.get_physical_device_calibrateable_time_domains_ext)(
physical_device,
count,
data,
)
})
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetCalibratedTimestampsEXT.html>
///
/// Returns a tuple containing `(timestamps, max_deviation)`
#[inline]
pub unsafe fn get_calibrated_timestamps(
&self,
device: vk::Device,
info: &[vk::CalibratedTimestampInfoEXT],
) -> VkResult<(Vec<u64>, Vec<u64>)> {
let mut timestamps = vec![0u64; info.len()];
let mut max_deviation = vec![0u64; info.len()];
(self.fp.get_calibrated_timestamps_ext)(
device,
self.handle,
info.len() as u32,
info.as_ptr(),
timestamps.as_mut_ptr(),
Expand All @@ -55,15 +41,54 @@ impl CalibratedTimestamps {
.result_with_success((timestamps, max_deviation))
}

pub const NAME: &'static CStr = vk::ExtCalibratedTimestampsFn::NAME;
pub const NAME: &'static CStr = vk::ExtCalibratedTimestampsDeviceFn::NAME;

#[inline]
pub fn fp(&self) -> &vk::ExtCalibratedTimestampsFn {
pub fn fp(&self) -> &vk::ExtCalibratedTimestampsDeviceFn {
&self.fp
}

#[inline]
pub fn instance(&self) -> vk::Instance {
pub fn device(&self) -> vk::Device {
self.handle
}
}

/// High-level instance function wrapper for
/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_calibrated_timestamps.html>
#[derive(Clone)]
pub struct CalibratedTimestampsInstance {
fp: vk::ExtCalibratedTimestampsInstanceFn,
}

impl CalibratedTimestampsInstance {
pub fn new(entry: &Entry, instance: &Instance) -> Self {
let handle = instance.handle();
let fp = vk::ExtCalibratedTimestampsInstanceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(handle, name.as_ptr()))
});
Self { fp }
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetPhysicalDeviceCalibrateableTimeDomainsEXT.html>
#[inline]
pub unsafe fn get_physical_device_calibrateable_time_domains(
&self,
physical_device: vk::PhysicalDevice,
) -> VkResult<Vec<vk::TimeDomainEXT>> {
read_into_uninitialized_vector(|count, data| {
(self.fp.get_physical_device_calibrateable_time_domains_ext)(
physical_device,
count,
data,
)
})
}

pub const NAME: &'static CStr = vk::ExtCalibratedTimestampsInstanceFn::NAME;

#[inline]
pub fn fp(&self) -> &vk::ExtCalibratedTimestampsInstanceFn {
&self.fp
}
}
60 changes: 45 additions & 15 deletions ash/src/extensions/ext/debug_utils.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
use crate::prelude::*;
use crate::{vk, RawPtr};
use crate::{Entry, Instance};
use crate::{Device, Entry, Instance};
use std::ffi::CStr;
use std::mem;

/// High-level device function wrapper for
/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html>
#[derive(Clone)]
pub struct DebugUtils {
handle: vk::Instance,
fp: vk::ExtDebugUtilsFn,
pub struct DebugUtilsDevice {
handle: vk::Device,
fp: vk::ExtDebugUtilsDeviceFn,
}

impl DebugUtils {
pub fn new(entry: &Entry, instance: &Instance) -> Self {
let handle = instance.handle();
let fp = vk::ExtDebugUtilsFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(handle, name.as_ptr()))
impl DebugUtilsDevice {
pub fn new(instance: &Instance, device: &Device) -> Self {
let handle = device.handle();
let fp = vk::ExtDebugUtilsDeviceFn::load(|name| unsafe {
mem::transmute(instance.get_device_proc_addr(handle, name.as_ptr()))
});
Self { handle, fp }
}
Expand All @@ -23,20 +25,18 @@ impl DebugUtils {
#[inline]
pub unsafe fn set_debug_utils_object_name(
&self,
device: vk::Device,
name_info: &vk::DebugUtilsObjectNameInfoEXT,
) -> VkResult<()> {
(self.fp.set_debug_utils_object_name_ext)(device, name_info).result()
(self.fp.set_debug_utils_object_name_ext)(self.handle, name_info).result()
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkSetDebugUtilsObjectTagEXT.html>
#[inline]
pub unsafe fn set_debug_utils_object_tag(
&self,
device: vk::Device,
tag_info: &vk::DebugUtilsObjectTagInfoEXT,
) -> VkResult<()> {
(self.fp.set_debug_utils_object_tag_ext)(device, tag_info).result()
(self.fp.set_debug_utils_object_tag_ext)(self.handle, tag_info).result()
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCmdBeginDebugUtilsLabelEXT.html>
Expand Down Expand Up @@ -91,6 +91,36 @@ impl DebugUtils {
(self.fp.queue_insert_debug_utils_label_ext)(queue, label);
}

pub const NAME: &'static CStr = vk::ExtDebugUtilsDeviceFn::NAME;

#[inline]
pub fn fp(&self) -> &vk::ExtDebugUtilsDeviceFn {
&self.fp
}

#[inline]
pub fn device(&self) -> vk::Device {
self.handle
}
}

/// High-level instance function wrapper for
/// <https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html>
#[derive(Clone)]
pub struct DebugUtilsInstance {
handle: vk::Instance,
fp: vk::ExtDebugUtilsInstanceFn,
}

impl DebugUtilsInstance {
pub fn new(entry: &Entry, instance: &Instance) -> Self {
let handle = instance.handle();
let fp = vk::ExtDebugUtilsInstanceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(handle, name.as_ptr()))
});
Self { handle, fp }
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCreateDebugUtilsMessengerEXT.html>
#[inline]
pub unsafe fn create_debug_utils_messenger(
Expand Down Expand Up @@ -134,10 +164,10 @@ impl DebugUtils {
);
}

pub const NAME: &'static CStr = vk::ExtDebugUtilsFn::NAME;
pub const NAME: &'static CStr = vk::ExtDebugUtilsInstanceFn::NAME;

#[inline]
pub fn fp(&self) -> &vk::ExtDebugUtilsFn {
pub fn fp(&self) -> &vk::ExtDebugUtilsInstanceFn {
&self.fp
}

Expand Down
74 changes: 50 additions & 24 deletions ash/src/extensions/ext/full_screen_exclusive.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
use crate::prelude::*;
use crate::vk;
use crate::{Device, Instance};
use crate::{Device, Entry, Instance};
use std::ffi::CStr;
use std::mem;

/// High-level device function wrapper for
/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_EXT_full_screen_exclusive.html>
#[derive(Clone)]
pub struct FullScreenExclusive {
pub struct FullScreenExclusiveDevice {
handle: vk::Device,
fp: vk::ExtFullScreenExclusiveFn,
fp: vk::ExtFullScreenExclusiveDeviceFn,
}

impl FullScreenExclusive {
impl FullScreenExclusiveDevice {
pub fn new(instance: &Instance, device: &Device) -> Self {
let handle = device.handle();
let fp = vk::ExtFullScreenExclusiveFn::load(|name| unsafe {
let fp = vk::ExtFullScreenExclusiveDeviceFn::load(|name| unsafe {
mem::transmute(instance.get_device_proc_addr(handle, name.as_ptr()))
});
Self { handle, fp }
Expand All @@ -28,23 +30,6 @@ impl FullScreenExclusive {
(self.fp.acquire_full_screen_exclusive_mode_ext)(self.handle, swapchain).result()
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetPhysicalDeviceSurfacePresentModes2EXT.html>
#[inline]
pub unsafe fn get_physical_device_surface_present_modes2(
&self,
physical_device: vk::PhysicalDevice,
surface_info: &vk::PhysicalDeviceSurfaceInfo2KHR,
) -> VkResult<Vec<vk::PresentModeKHR>> {
read_into_uninitialized_vector(|count, data| {
(self.fp.get_physical_device_surface_present_modes2_ext)(
physical_device,
surface_info,
count,
data,
)
})
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkReleaseFullScreenExclusiveModeEXT.html>
#[inline]
pub unsafe fn release_full_screen_exclusive_mode(
Expand All @@ -69,10 +54,10 @@ impl FullScreenExclusive {
.result_with_success(present_modes)
}

pub const NAME: &'static CStr = vk::ExtFullScreenExclusiveFn::NAME;
pub const NAME: &'static CStr = vk::ExtFullScreenExclusiveDeviceFn::NAME;

#[inline]
pub fn fp(&self) -> &vk::ExtFullScreenExclusiveFn {
pub fn fp(&self) -> &vk::ExtFullScreenExclusiveDeviceFn {
&self.fp
}

Expand All @@ -81,3 +66,44 @@ impl FullScreenExclusive {
self.handle
}
}

/// High-level instance function wrapper for
/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_EXT_full_screen_exclusive.html>
#[derive(Clone)]
pub struct FullScreenExclusiveInstance {
fp: vk::ExtFullScreenExclusiveInstanceFn,
}

impl FullScreenExclusiveInstance {
pub fn new(entry: &Entry, instance: &Instance) -> Self {
let handle = instance.handle();
let fp = vk::ExtFullScreenExclusiveInstanceFn::load(|name| unsafe {
mem::transmute(entry.get_instance_proc_addr(handle, name.as_ptr()))
});
Self { fp }
}

/// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkGetPhysicalDeviceSurfacePresentModes2EXT.html>
#[inline]
pub unsafe fn get_physical_device_surface_present_modes2(
&self,
physical_device: vk::PhysicalDevice,
surface_info: &vk::PhysicalDeviceSurfaceInfo2KHR,
) -> VkResult<Vec<vk::PresentModeKHR>> {
read_into_uninitialized_vector(|count, data| {
(self.fp.get_physical_device_surface_present_modes2_ext)(
physical_device,
surface_info,
count,
data,
)
})
}

pub const NAME: &'static CStr = vk::ExtFullScreenExclusiveInstanceFn::NAME;

#[inline]
pub fn fp(&self) -> &vk::ExtFullScreenExclusiveInstanceFn {
&self.fp
}
}
8 changes: 4 additions & 4 deletions ash/src/extensions/ext/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
pub use self::acquire_drm_display::AcquireDrmDisplay;
pub use self::buffer_device_address::BufferDeviceAddress;
pub use self::calibrated_timestamps::CalibratedTimestamps;
pub use self::calibrated_timestamps::{CalibratedTimestampsDevice, CalibratedTimestampsInstance};
#[allow(deprecated)]
pub use self::debug_marker::DebugMarker;
#[allow(deprecated)]
pub use self::debug_report::DebugReport;
pub use self::debug_utils::DebugUtils;
pub use self::debug_utils::{DebugUtilsDevice, DebugUtilsInstance};
pub use self::descriptor_buffer::DescriptorBuffer;
pub use self::extended_dynamic_state::ExtendedDynamicState;
pub use self::extended_dynamic_state2::ExtendedDynamicState2;
pub use self::extended_dynamic_state3::ExtendedDynamicState3;
pub use self::full_screen_exclusive::FullScreenExclusive;
pub use self::full_screen_exclusive::{FullScreenExclusiveDevice, FullScreenExclusiveInstance};
pub use self::headless_surface::HeadlessSurface;
pub use self::image_compression_control::ImageCompressionControl;
pub use self::image_drm_format_modifier::ImageDrmFormatModifier;
pub use self::mesh_shader::MeshShader;
pub use self::metal_surface::MetalSurface;
pub use self::private_data::PrivateData;
pub use self::sample_locations::SampleLocations;
pub use self::sample_locations::{SampleLocationsDevice, SampleLocationsInstance};
pub use self::shader_object::ShaderObject;
pub use self::tooling_info::ToolingInfo;

Expand Down
Loading

0 comments on commit 97e731c

Please sign in to comment.