From 88c2eaff9e772387c2002e8a30b742120aaf0682 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 7 Oct 2023 16:59:19 +0200 Subject: [PATCH] Add support for `ext_private_data` (#2350) --- vulkano/src/device/mod.rs | 47 +++++ vulkano/src/device/private_data.rs | 276 +++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+) create mode 100644 vulkano/src/device/private_data.rs diff --git a/vulkano/src/device/mod.rs b/vulkano/src/device/mod.rs index 5bc225055f..66c24c75d5 100644 --- a/vulkano/src/device/mod.rs +++ b/vulkano/src/device/mod.rs @@ -140,6 +140,7 @@ use std::{ }; pub mod physical; +pub mod private_data; pub(crate) mod properties; mod queue; @@ -198,6 +199,7 @@ impl Device { enabled_extensions: _, enabled_features: _, ref physical_devices, + private_data_slot_request_count: _, _ne: _, } = create_info; @@ -283,6 +285,7 @@ impl Device { ref enabled_extensions, ref enabled_features, ref physical_devices, + private_data_slot_request_count, _ne: _, } = &create_info; @@ -361,6 +364,18 @@ impl Device { create_info_vk.p_next = next as *mut _ as *mut _; } + let mut private_data_create_info_vk = None; + + if private_data_slot_request_count != 0 { + let next = private_data_create_info_vk.insert(ash::vk::DevicePrivateDataCreateInfo { + private_data_slot_request_count, + ..Default::default() + }); + + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *mut _ as *mut _; + } + // VUID-VkDeviceCreateInfo-pNext-00373 if has_khr_get_physical_device_properties2 { create_info_vk.p_next = features_ffi.head_as_ref() as *const _ as _; @@ -401,6 +416,7 @@ impl Device { enabled_features, enabled_extensions, physical_devices, + private_data_slot_request_count: _, _ne: _, } = create_info; @@ -1208,6 +1224,20 @@ pub struct DeviceCreateInfo { /// [`khr_device_group`]: crate::device::DeviceExtensions::khr_device_group pub physical_devices: SmallVec<[Arc; 2]>, + /// The number of [private data slots] to reserve when creating the device. + /// + /// This is purely an optimization, and it is not necessary to do this in order to use private + /// data slots, but it may improve performance. + /// + /// If not zero, the physical device API version must be at least 1.3, or `enabled_extensions` + /// must contain [`ext_private_data`]. + /// + /// The default value is `0`. + /// + /// [private data slots]: self::private_data + /// [`ext_private_data`]: DeviceExtensions::ext_private_data + pub private_data_slot_request_count: u32, + pub _ne: crate::NonExhaustive, } @@ -1219,6 +1249,7 @@ impl Default for DeviceCreateInfo { enabled_extensions: DeviceExtensions::empty(), enabled_features: Features::empty(), physical_devices: SmallVec::new(), + private_data_slot_request_count: 0, _ne: crate::NonExhaustive(()), } } @@ -1234,6 +1265,7 @@ impl DeviceCreateInfo { ref enabled_extensions, ref enabled_features, ref physical_devices, + private_data_slot_request_count, _ne: _, } = self; @@ -1525,6 +1557,21 @@ impl DeviceCreateInfo { } } + if private_data_slot_request_count != 0 + && !(physical_device.api_version() >= Version::V1_3 + || enabled_extensions.ext_private_data) + { + return Err(Box::new(ValidationError { + context: "private_data_slot_request_count".into(), + problem: "is not zero".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_3)]), + RequiresAllOf(&[Requires::DeviceExtension("ext_private_data")]), + ]), + ..Default::default() + })); + } + Ok(()) } } diff --git a/vulkano/src/device/private_data.rs b/vulkano/src/device/private_data.rs new file mode 100644 index 0000000000..b29e6d90ba --- /dev/null +++ b/vulkano/src/device/private_data.rs @@ -0,0 +1,276 @@ +// Copyright (c) 2023 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// or the MIT +// license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +//! Store user-defined data associated with Vulkan objects. +//! +//! A private data slot allows you to associate an arbitrary `u64` value with any device-owned +//! Vulkan object. Each private data slot can store one value per object, but you can use the +//! value to look up a larger amount of data in a collection such as a `HashMap`. +//! +//! The intended usage is to create one private data slot for every subsystem in your program +//! that needs to assign data to objects independently of the others. That way, different parts +//! of a program manage their own private data and don't interfere with each other's data. +//! +//! When creating a device, it is possible to reserve private data slots ahead of time using +//! [`DeviceCreateInfo::private_data_slot_request_count`]. This is not necessary, but it can +//! speed up the use of data slots later. +//! +//! [`DeviceCreateInfo::private_data_slot_request_count`]: super::DeviceCreateInfo::private_data_slot_request_count + +use super::{Device, DeviceOwned}; +use crate::{ + instance::InstanceOwnedDebugWrapper, Requires, RequiresAllOf, RequiresOneOf, Validated, + ValidationError, Version, VulkanError, VulkanObject, +}; +use ash::vk::Handle; +use std::{mem::MaybeUninit, ptr, sync::Arc}; + +/// An object that stores one `u64` value per Vulkan object. +#[derive(Debug)] +pub struct PrivateDataSlot { + device: InstanceOwnedDebugWrapper>, + handle: ash::vk::PrivateDataSlot, +} + +impl PrivateDataSlot { + /// Creates a new `PrivateDataSlot`. + /// + /// The `private_data` feature must be enabled on the device. + #[inline] + pub fn new( + device: Arc, + create_info: PrivateDataSlotCreateInfo, + ) -> Result> { + Self::validate_new(&device, &create_info)?; + + unsafe { Ok(Self::new_unchecked(device, create_info)?) } + } + + fn validate_new( + device: &Device, + create_info: &PrivateDataSlotCreateInfo, + ) -> Result<(), Box> { + if !device.enabled_features().private_data { + return Err(Box::new(ValidationError { + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "private_data", + )])]), + vuids: &["VUID-vkCreatePrivateDataSlot-privateData-04564"], + ..Default::default() + })); + } + + create_info + .validate(device) + .map_err(|err| err.add_context("create_info"))?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn new_unchecked( + device: Arc, + create_info: PrivateDataSlotCreateInfo, + ) -> Result { + let &PrivateDataSlotCreateInfo { _ne: _ } = &create_info; + + let create_info_vk = ash::vk::PrivateDataSlotCreateInfo { + flags: ash::vk::PrivateDataSlotCreateFlags::empty(), + ..Default::default() + }; + + let handle = { + let fns = device.fns(); + let mut output = MaybeUninit::uninit(); + + if device.api_version() >= Version::V1_3 { + (fns.v1_3.create_private_data_slot)( + device.handle(), + &create_info_vk, + ptr::null(), + output.as_mut_ptr(), + ) + } else { + (fns.ext_private_data.create_private_data_slot_ext)( + device.handle(), + &create_info_vk, + ptr::null(), + output.as_mut_ptr(), + ) + } + .result() + .map_err(VulkanError::from)?; + + output.assume_init() + }; + + Ok(Self::from_handle(device, handle, create_info)) + } + + /// Creates a new `PrivateDataSlot` from a raw object handle. + /// + /// # Safety + /// + /// - `handle` must be a valid Vulkan object handle created from `device`. + /// - `create_info` must match the info used to create the object. + #[inline] + pub unsafe fn from_handle( + device: Arc, + handle: ash::vk::PrivateDataSlot, + _create_info: PrivateDataSlotCreateInfo, + ) -> Self { + Self { + device: InstanceOwnedDebugWrapper(device), + handle, + } + } + + /// Sets the private data that is associated with `object` to `data`. + /// + /// If `self` already has data for `object`, that data is replaced with the new value. + #[inline] + pub fn set_private_data( + &self, + object: &T, + data: u64, + ) -> Result<(), Validated> { + self.validate_set_private_data(object)?; + + unsafe { Ok(self.set_private_data_unchecked(object, data)?) } + } + + fn validate_set_private_data( + &self, + object: &T, + ) -> Result<(), Box> { + assert_eq!(self.device(), object.device()); + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_private_data_unchecked( + &self, + object: &T, + data: u64, + ) -> Result<(), VulkanError> { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.set_private_data)( + self.device.handle(), + T::Handle::TYPE, + object.handle().as_raw(), + self.handle, + data, + ) + } else { + (fns.ext_private_data.set_private_data_ext)( + self.device.handle(), + T::Handle::TYPE, + object.handle().as_raw(), + self.handle, + data, + ) + } + .result() + .map_err(VulkanError::from) + } + + /// Returns the private data in `self` that is associated with `object`. + /// + /// If no private data was previously set, 0 is returned. + pub fn get_private_data(&self, object: &T) -> u64 { + let fns = self.device.fns(); + + unsafe { + let mut output = MaybeUninit::uninit(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.get_private_data)( + self.device.handle(), + T::Handle::TYPE, + object.handle().as_raw(), + self.handle, + output.as_mut_ptr(), + ) + } else { + (fns.ext_private_data.get_private_data_ext)( + self.device.handle(), + T::Handle::TYPE, + object.handle().as_raw(), + self.handle, + output.as_mut_ptr(), + ) + } + + output.assume_init() + } + } +} + +impl Drop for PrivateDataSlot { + #[inline] + fn drop(&mut self) { + unsafe { + let fns = self.device.fns(); + + if self.device.api_version() >= Version::V1_3 { + (fns.v1_3.destroy_private_data_slot)( + self.device.handle(), + self.handle, + ptr::null(), + ); + } else { + (fns.ext_private_data.destroy_private_data_slot_ext)( + self.device.handle(), + self.handle, + ptr::null(), + ); + } + } + } +} + +unsafe impl VulkanObject for PrivateDataSlot { + type Handle = ash::vk::PrivateDataSlot; + + #[inline] + fn handle(&self) -> Self::Handle { + self.handle + } +} + +unsafe impl DeviceOwned for PrivateDataSlot { + #[inline] + fn device(&self) -> &Arc { + &self.device + } +} + +/// Parameters to create a new `PrivateDataSlot`. +#[derive(Clone, Debug)] +pub struct PrivateDataSlotCreateInfo { + pub _ne: crate::NonExhaustive, +} + +impl Default for PrivateDataSlotCreateInfo { + #[inline] + fn default() -> Self { + Self { + _ne: crate::NonExhaustive(()), + } + } +} + +impl PrivateDataSlotCreateInfo { + pub(crate) fn validate(&self, _device: &Device) -> Result<(), Box> { + Ok(()) + } +}