mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 06:45:23 +00:00
Add support for the Physical Storage Buffer Access feature. (#1359)
This feature was recently promoted from an extension to the core spec in Vulkan 1.2. It is already supported on many recent drivers: NVidia, Mesa RADV, and AMDVLK. Other than supporting enabling the feature and the basic buffer address query, I made no attempt to create safe interfaces for its use. Such will come later, after I've experimented with its use in Geobacter.
This commit is contained in:
parent
68cc93932d
commit
57c545d7ab
@ -56,6 +56,7 @@ pub type SwapchainKHR = u64;
|
||||
pub type DisplayKHR = u64;
|
||||
pub type DisplayModeKHR = u64;
|
||||
pub type DescriptorUpdateTemplateKHR = u64;
|
||||
pub type DeviceAddress = u64;
|
||||
|
||||
pub const LOD_CLAMP_NONE: f32 = 1000.0;
|
||||
pub const REMAINING_MIP_LEVELS: u32 = 0xffffffff;
|
||||
@ -195,6 +196,8 @@ pub const STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR: u32 = 1000
|
||||
pub const STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR: u32 = 1000146003;
|
||||
pub const STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR: u32 = 1000146004;
|
||||
pub const STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT: u32 = 1000255000;
|
||||
pub const STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT: u32 = 1000244000;
|
||||
pub const STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO: u32 = 1000244001;
|
||||
|
||||
pub type SystemAllocationScope = u32;
|
||||
pub const SYSTEM_ALLOCATION_SCOPE_COMMAND: u32 = 0;
|
||||
@ -765,6 +768,7 @@ pub const BUFFER_USAGE_STORAGE_BUFFER_BIT: u32 = 0x00000020;
|
||||
pub const BUFFER_USAGE_INDEX_BUFFER_BIT: u32 = 0x00000040;
|
||||
pub const BUFFER_USAGE_VERTEX_BUFFER_BIT: u32 = 0x00000080;
|
||||
pub const BUFFER_USAGE_INDIRECT_BUFFER_BIT: u32 = 0x00000100;
|
||||
pub const BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT: u32 = 0x00020000;
|
||||
pub type BufferUsageFlags = Flags;
|
||||
pub type BufferViewCreateFlags = Flags;
|
||||
pub type ImageViewCreateFlags = Flags;
|
||||
@ -1563,6 +1567,13 @@ pub struct ImageCreateInfo {
|
||||
pub initialLayout: ImageLayout,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BufferDeviceAddressInfo {
|
||||
pub sType: StructureType,
|
||||
pub pNext: *const c_void,
|
||||
pub buffer: Buffer,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SubresourceLayout {
|
||||
pub offset: DeviceSize,
|
||||
@ -2547,6 +2558,15 @@ pub struct PhysicalDeviceSparseImageFormatInfo2KHR {
|
||||
pub tiling: ImageTiling,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PhysicalDeviceBufferAddressFeaturesEXT {
|
||||
pub sType: StructureType,
|
||||
pub pNext: *const c_void,
|
||||
pub bufferDeviceAddress: Bool32,
|
||||
pub bufferDeviceAddressCaptureReplay: Bool32,
|
||||
pub bufferDeviceAddressMultiDevice: Bool32,
|
||||
}
|
||||
|
||||
pub type ViSurfaceCreateFlagsNN = Flags;
|
||||
|
||||
#[repr(C)]
|
||||
@ -2958,4 +2978,5 @@ ptrs!(DevicePointers, {
|
||||
CmdInsertDebugUtilsLabelEXT => (commandBuffer: CommandBuffer, pLabelInfo: *const DebugUtilsLabelEXT) -> Result,
|
||||
AcquireFullScreenExclusiveModeEXT => (device: Device, swapchain: SwapchainKHR) -> Result,
|
||||
ReleaseFullScreenExclusiveModeEXT => (device: Device, swapchain: SwapchainKHR) -> Result,
|
||||
GetBufferDeviceAddressEXT => (device: Device, pInfo: *const BufferDeviceAddressInfo) -> DeviceAddress,
|
||||
});
|
||||
|
@ -69,7 +69,7 @@ impl UnsafeBuffer {
|
||||
pub unsafe fn new<'a, I>(
|
||||
device: Arc<Device>,
|
||||
size: usize,
|
||||
usage: BufferUsage,
|
||||
mut usage: BufferUsage,
|
||||
sharing: Sharing<I>,
|
||||
sparse: SparseLevel,
|
||||
) -> Result<(UnsafeBuffer, MemoryRequirements), BufferCreationError>
|
||||
@ -86,14 +86,6 @@ impl UnsafeBuffer {
|
||||
size
|
||||
};
|
||||
|
||||
let usage_bits = usage.to_vulkan_bits();
|
||||
|
||||
// Checking for empty BufferUsage.
|
||||
assert!(
|
||||
usage_bits != 0,
|
||||
"Can't create buffer with empty BufferUsage"
|
||||
);
|
||||
|
||||
// Checking sparse features.
|
||||
assert!(
|
||||
sparse.sparse || !sparse.sparse_residency,
|
||||
@ -112,6 +104,21 @@ impl UnsafeBuffer {
|
||||
if sparse.sparse_aliased && !device.enabled_features().sparse_residency_aliased {
|
||||
return Err(BufferCreationError::SparseResidencyAliasedFeatureNotEnabled);
|
||||
}
|
||||
if usage.device_address && !device.enabled_features().buffer_device_address {
|
||||
usage.device_address = false;
|
||||
if usage.to_vulkan_bits() == 0 {
|
||||
// return an error iff device_address was the only requested usage and the
|
||||
// feature isn't enabled. Otherwise we'll hit that assert below.
|
||||
return Err(BufferCreationError::DeviceAddressFeatureNotEnabled);
|
||||
}
|
||||
}
|
||||
let usage_bits = usage.to_vulkan_bits();
|
||||
|
||||
// Checking for empty BufferUsage.
|
||||
assert!(
|
||||
usage_bits != 0,
|
||||
"Can't create buffer with empty BufferUsage"
|
||||
);
|
||||
|
||||
let buffer = {
|
||||
let (sh_mode, sh_indices) = match sharing {
|
||||
@ -324,6 +331,11 @@ impl UnsafeBuffer {
|
||||
(self.usage & vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT) != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn usage_device_address(&self) -> bool {
|
||||
(self.usage & vk::BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) != 0
|
||||
}
|
||||
|
||||
/// Returns a key unique to each `UnsafeBuffer`. Can be used for the `conflicts_key` method.
|
||||
#[inline]
|
||||
pub fn key(&self) -> u64 {
|
||||
@ -416,6 +428,17 @@ impl SparseLevel {
|
||||
}
|
||||
}
|
||||
|
||||
/// The device address usage flag was not set.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct DeviceAddressUsageNotEnabledError;
|
||||
impl error::Error for DeviceAddressUsageNotEnabledError { }
|
||||
impl fmt::Display for DeviceAddressUsageNotEnabledError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.write_str("the device address usage flag was not set on this buffer")
|
||||
}
|
||||
}
|
||||
|
||||
/// Error that can happen when creating a buffer.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum BufferCreationError {
|
||||
@ -427,6 +450,8 @@ pub enum BufferCreationError {
|
||||
SparseResidencyBufferFeatureNotEnabled,
|
||||
/// Sparse aliasing was requested but the corresponding feature wasn't enabled.
|
||||
SparseResidencyAliasedFeatureNotEnabled,
|
||||
/// Device address was requested but the corresponding feature wasn't enabled.
|
||||
DeviceAddressFeatureNotEnabled,
|
||||
}
|
||||
|
||||
impl error::Error for BufferCreationError {
|
||||
@ -453,6 +478,9 @@ impl fmt::Display for BufferCreationError {
|
||||
BufferCreationError::SparseResidencyAliasedFeatureNotEnabled => {
|
||||
"sparse aliasing was requested but the corresponding feature wasn't enabled"
|
||||
}
|
||||
BufferCreationError::DeviceAddressFeatureNotEnabled => {
|
||||
"device address was requested but the corresponding feature wasn't enabled"
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,11 @@
|
||||
|
||||
use std::hash::Hash;
|
||||
use std::hash::Hasher;
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::Range;
|
||||
use std::ptr;
|
||||
|
||||
use buffer::sys::UnsafeBuffer;
|
||||
use buffer::sys::{UnsafeBuffer, DeviceAddressUsageNotEnabledError};
|
||||
use buffer::BufferSlice;
|
||||
use device::DeviceOwned;
|
||||
use device::Queue;
|
||||
@ -19,7 +21,7 @@ use image::ImageAccess;
|
||||
use memory::Content;
|
||||
use sync::AccessError;
|
||||
|
||||
use SafeDeref;
|
||||
use ::{SafeDeref, VulkanObject, vk};
|
||||
|
||||
/// Trait for objects that represent a way for the GPU to have access to a buffer or a slice of a
|
||||
/// buffer.
|
||||
@ -142,6 +144,39 @@ pub unsafe trait BufferAccess: DeviceOwned {
|
||||
///
|
||||
/// Must only be called once per previous lock.
|
||||
unsafe fn unlock(&self);
|
||||
|
||||
/// Gets the device address for this buffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// No lock checking or waiting is performed. This is nevertheless still safe because the
|
||||
/// returned value isn't directly dereferencable. Unsafe code is required to dereference the
|
||||
/// value in a shader.
|
||||
fn raw_device_address(&self) -> Result<NonZeroU64, DeviceAddressUsageNotEnabledError> {
|
||||
let inner = self.inner();
|
||||
|
||||
if !inner.buffer.usage_device_address() {
|
||||
return Err(DeviceAddressUsageNotEnabledError);
|
||||
}
|
||||
|
||||
let dev = self.device();
|
||||
unsafe {
|
||||
let info = vk::BufferDeviceAddressInfo {
|
||||
sType: vk::STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
|
||||
pNext: ptr::null(),
|
||||
buffer: inner.buffer.internal_object(),
|
||||
};
|
||||
let ptr = dev.pointers()
|
||||
.GetBufferDeviceAddressEXT(dev.internal_object(),
|
||||
&info);
|
||||
|
||||
if ptr == 0 {
|
||||
panic!("got null ptr from a valid GetBufferDeviceAddressEXT call");
|
||||
}
|
||||
|
||||
Ok(NonZeroU64::new_unchecked(ptr + inner.offset as u64))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner information about a buffer.
|
||||
|
@ -27,6 +27,9 @@ pub struct BufferUsage {
|
||||
pub index_buffer: bool,
|
||||
pub vertex_buffer: bool,
|
||||
pub indirect_buffer: bool,
|
||||
/// Requires the `buffer_device_address` feature. If that feature is not enabled, this will
|
||||
/// be silently ignored.
|
||||
pub device_address: bool,
|
||||
}
|
||||
|
||||
impl BufferUsage {
|
||||
@ -60,6 +63,9 @@ impl BufferUsage {
|
||||
if self.indirect_buffer {
|
||||
result |= vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT;
|
||||
}
|
||||
if self.device_address {
|
||||
result |= vk::BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@ -76,6 +82,7 @@ impl BufferUsage {
|
||||
index_buffer: false,
|
||||
vertex_buffer: false,
|
||||
indirect_buffer: false,
|
||||
device_address: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,6 +99,7 @@ impl BufferUsage {
|
||||
index_buffer: true,
|
||||
vertex_buffer: true,
|
||||
indirect_buffer: true,
|
||||
device_address: true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,6 +199,15 @@ impl BufferUsage {
|
||||
..BufferUsage::none()
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BufferUsage` with `device_address` set to true and the rest to false.
|
||||
#[inline]
|
||||
pub fn device_address() -> BufferUsage {
|
||||
BufferUsage {
|
||||
device_address: true,
|
||||
..BufferUsage::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for BufferUsage {
|
||||
@ -208,6 +225,7 @@ impl BitOr for BufferUsage {
|
||||
index_buffer: self.index_buffer || rhs.index_buffer,
|
||||
vertex_buffer: self.vertex_buffer || rhs.vertex_buffer,
|
||||
indirect_buffer: self.indirect_buffer || rhs.indirect_buffer,
|
||||
device_address: self.device_address || rhs.device_address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,6 +221,11 @@ impl Device {
|
||||
.map(|extension| extension.as_ptr())
|
||||
.collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
let mut requested_features = requested_features.clone();
|
||||
// Always enabled; see below.
|
||||
requested_features.robust_buffer_access = true;
|
||||
let requested_features = requested_features;
|
||||
|
||||
// device creation
|
||||
let device = unsafe {
|
||||
// each element of `queues` is a `(queue_family, priorities)`
|
||||
@ -281,15 +286,12 @@ impl Device {
|
||||
//
|
||||
// Note that if we ever remove this, don't forget to adjust the change in
|
||||
// `Device`'s construction below.
|
||||
let features = {
|
||||
let mut features = requested_features.clone().into_vulkan_features();
|
||||
features.robustBufferAccess = vk::TRUE;
|
||||
features
|
||||
};
|
||||
|
||||
let features = requested_features.into_vulkan_features_v2();
|
||||
|
||||
let infos = vk::DeviceCreateInfo {
|
||||
sType: vk::STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
pNext: ptr::null(),
|
||||
pNext: features.base_ptr() as *const _,
|
||||
flags: 0, // reserved
|
||||
queueCreateInfoCount: queues.len() as u32,
|
||||
pQueueCreateInfos: queues.as_ptr(),
|
||||
@ -297,7 +299,7 @@ impl Device {
|
||||
ppEnabledLayerNames: layers_ptr.as_ptr(),
|
||||
enabledExtensionCount: extensions_list.len() as u32,
|
||||
ppEnabledExtensionNames: extensions_list.as_ptr(),
|
||||
pEnabledFeatures: &features,
|
||||
pEnabledFeatures: ptr::null(),
|
||||
};
|
||||
|
||||
let mut output = MaybeUninit::uninit();
|
||||
|
@ -7,56 +7,277 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::{mem, ptr};
|
||||
use std::marker::PhantomPinned;
|
||||
use std::pin::Pin;
|
||||
|
||||
use vk;
|
||||
|
||||
macro_rules! features {
|
||||
($($name:ident => $vk:ident,)+) => (
|
||||
/// Represents all the features that are available on a physical device or enabled on
|
||||
/// a logical device.
|
||||
///
|
||||
/// Note that the `robust_buffer_access` is guaranteed to be supported by all Vulkan
|
||||
/// implementations.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use vulkano::device::Features;
|
||||
/// # let physical_device: vulkano::instance::PhysicalDevice = return;
|
||||
/// let minimal_features = Features {
|
||||
/// geometry_shader: true,
|
||||
/// .. Features::none()
|
||||
/// };
|
||||
///
|
||||
/// let optimal_features = vulkano::device::Features {
|
||||
/// geometry_shader: true,
|
||||
/// tessellation_shader: true,
|
||||
/// .. Features::none()
|
||||
/// };
|
||||
///
|
||||
/// if !physical_device.supported_features().superset_of(&minimal_features) {
|
||||
/// panic!("The physical device is not good enough for this application.");
|
||||
/// }
|
||||
///
|
||||
/// assert!(optimal_features.superset_of(&minimal_features));
|
||||
/// let features_to_request = optimal_features.intersection(physical_device.supported_features());
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Features {
|
||||
$(
|
||||
pub $name: bool,
|
||||
)+
|
||||
}
|
||||
macro_rules! features_init {
|
||||
(core { $name:ident => $vk:ident }, $out:expr, $val:expr) => {
|
||||
$out.$name = $val;
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)+
|
||||
],
|
||||
}, $out:expr, $val:expr) => {
|
||||
$($out.$name = $val;)+
|
||||
};
|
||||
}
|
||||
macro_rules! features_superset_of {
|
||||
(core { $name:ident => $vk:ident }, $self:expr, $other:expr) => {
|
||||
($self.$name == true || $other.$name == false)
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)+
|
||||
],
|
||||
}, $self:expr, $other:expr) => {
|
||||
$(($self.$name == true || $other.$name == false))&&+
|
||||
};
|
||||
}
|
||||
macro_rules! features_intersection {
|
||||
(core { $name:ident => $vk:ident }, $out:expr, $self:expr, $other:expr) => {
|
||||
$out.$name = $self.$name && $other.$name;
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)+
|
||||
],
|
||||
}, $out:expr, $self:expr, $other:expr) => {
|
||||
$($out.$name = $self.$name && $other.$name;)+
|
||||
};
|
||||
}
|
||||
macro_rules! features_difference {
|
||||
(core { $name:ident => $vk:ident }, $out:expr, $self:expr, $other:expr) => {
|
||||
$out.$name = $self.$name && !$other.$name;
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)+
|
||||
],
|
||||
}, $out:expr, $self:expr, $other:expr) => {
|
||||
$($out.$name = $self.$name && !$other.$name;)+
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! from_feature_v1 {
|
||||
(core { $name:ident => $vk:ident }, $out:expr, $features:expr) => {
|
||||
$out.$name = $features.$vk != vk::FALSE;
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)+
|
||||
],
|
||||
}, $out:expr, $features:expr) => {
|
||||
// nothing.
|
||||
};
|
||||
}
|
||||
macro_rules! into_feature_v1 {
|
||||
(core { $name:ident => $vk:ident }, $out:expr, $self:expr) => {
|
||||
$out.$vk = if $self.$name { vk::TRUE } else { vk::FALSE };
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)+
|
||||
],
|
||||
}, $out:expr, $self:expr) => {
|
||||
// nothing.
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! from_ext_features_match {
|
||||
(core { $name:ident => $vk:ident }, $output:expr, $base_ref:expr, $base_ptr:expr) => {
|
||||
// nothing.
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)+
|
||||
],
|
||||
}, $output:expr, $base_ref:expr, $base_ptr:expr) => {
|
||||
if $base_ref.sType == $stype {
|
||||
let ptr = $base_ptr as *const $ty;
|
||||
let r = ptr.as_ref().unwrap();
|
||||
$($output.$name = r.$vk != vk::FALSE;)+
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! into_ext_features_match {
|
||||
(core { $name:ident => $vk:ident }, $output:expr, $base_ref:expr, $base_ptr:expr) => {
|
||||
// nothing.
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)+
|
||||
],
|
||||
}, $this:expr, $base_ref:expr, $base_ptr:expr) => {
|
||||
if $base_ref.sType == $stype {
|
||||
let ptr = $base_ptr as *mut $ty;
|
||||
let r = ptr.as_mut().unwrap();
|
||||
$(r.$vk = if $this.$name { vk::TRUE } else { vk::FALSE };)+
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! features_ffi_init_pinned {
|
||||
(core { $name:ident => $vk:ident }, $this:expr, $prev:expr) => {
|
||||
// nothing.
|
||||
};
|
||||
(extension {
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_name:ident,
|
||||
sType: $stype:expr,
|
||||
fields: [
|
||||
$($name:ident => $vk:ident,)*
|
||||
],
|
||||
}, $this:expr, $prev:expr) => {{
|
||||
$this.$ffi_name.sType = $stype;
|
||||
let next = &mut $this.$ffi_name as *mut _ as *mut Base;
|
||||
(&mut *$prev).pNext = next;
|
||||
$prev = next;
|
||||
}};
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Base {
|
||||
sType: vk::StructureType,
|
||||
pNext: *mut Base,
|
||||
}
|
||||
|
||||
// Can't define this structure with macros :(
|
||||
/// Represents all the features that are available on a physical device or enabled on
|
||||
/// a logical device.
|
||||
///
|
||||
/// Note that the `robust_buffer_access` is guaranteed to be supported by all Vulkan
|
||||
/// implementations.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use vulkano::device::Features;
|
||||
/// # let physical_device: vulkano::instance::PhysicalDevice = return;
|
||||
/// let minimal_features = Features {
|
||||
/// geometry_shader: true,
|
||||
/// .. Features::none()
|
||||
/// };
|
||||
///
|
||||
/// let optimal_features = vulkano::device::Features {
|
||||
/// geometry_shader: true,
|
||||
/// tessellation_shader: true,
|
||||
/// .. Features::none()
|
||||
/// };
|
||||
///
|
||||
/// if !physical_device.supported_features().superset_of(&minimal_features) {
|
||||
/// panic!("The physical device is not good enough for this application.");
|
||||
/// }
|
||||
///
|
||||
/// assert!(optimal_features.superset_of(&minimal_features));
|
||||
/// let features_to_request = optimal_features.intersection(physical_device.supported_features());
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Features {
|
||||
pub robust_buffer_access: bool,
|
||||
pub full_draw_index_uint32: bool,
|
||||
pub image_cube_array: bool,
|
||||
pub independent_blend: bool,
|
||||
pub geometry_shader: bool,
|
||||
pub tessellation_shader: bool,
|
||||
pub sample_rate_shading: bool,
|
||||
pub dual_src_blend: bool,
|
||||
pub logic_op: bool,
|
||||
pub multi_draw_indirect: bool,
|
||||
pub draw_indirect_first_instance: bool,
|
||||
pub depth_clamp: bool,
|
||||
pub depth_bias_clamp: bool,
|
||||
pub fill_mode_non_solid: bool,
|
||||
pub depth_bounds: bool,
|
||||
pub wide_lines: bool,
|
||||
pub large_points: bool,
|
||||
pub alpha_to_one: bool,
|
||||
pub multi_viewport: bool,
|
||||
pub sampler_anisotropy: bool,
|
||||
pub texture_compression_etc2: bool,
|
||||
pub texture_compression_astc_ldr: bool,
|
||||
pub texture_compression_bc: bool,
|
||||
pub occlusion_query_precise: bool,
|
||||
pub pipeline_statistics_query: bool,
|
||||
pub vertex_pipeline_stores_and_atomics: bool,
|
||||
pub fragment_stores_and_atomics: bool,
|
||||
pub shader_tessellation_and_geometry_point_size: bool,
|
||||
pub shader_image_gather_extended: bool,
|
||||
pub shader_storage_image_extended_formats: bool,
|
||||
pub shader_storage_image_multisample: bool,
|
||||
pub shader_storage_image_read_without_format: bool,
|
||||
pub shader_storage_image_write_without_format: bool,
|
||||
pub shader_uniform_buffer_array_dynamic_indexing: bool,
|
||||
pub shader_sampled_image_array_dynamic_indexing: bool,
|
||||
pub shader_storage_buffer_array_dynamic_indexing: bool,
|
||||
pub shader_storage_image_array_dynamic_indexing: bool,
|
||||
pub shader_clip_distance: bool,
|
||||
pub shader_cull_distance: bool,
|
||||
pub shader_f3264: bool,
|
||||
pub shader_int64: bool,
|
||||
pub shader_int16: bool,
|
||||
pub shader_resource_residency: bool,
|
||||
pub shader_resource_min_lod: bool,
|
||||
pub sparse_binding: bool,
|
||||
pub sparse_residency_buffer: bool,
|
||||
pub sparse_residency_image2d: bool,
|
||||
pub sparse_residency_image3d: bool,
|
||||
pub sparse_residency2_samples: bool,
|
||||
pub sparse_residency4_samples: bool,
|
||||
pub sparse_residency8_samples: bool,
|
||||
pub sparse_residency16_samples: bool,
|
||||
pub sparse_residency_aliased: bool,
|
||||
pub variable_multisample_rate: bool,
|
||||
pub inherited_queries: bool,
|
||||
|
||||
pub buffer_device_address: bool,
|
||||
pub buffer_device_address_capture_replay: bool,
|
||||
pub buffer_device_address_multi_device: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct FeaturesFfi {
|
||||
_pinned: PhantomPinned,
|
||||
pub(crate) main: vk::PhysicalDeviceFeatures2KHR,
|
||||
phys_dev_buf_addr: vk::PhysicalDeviceBufferAddressFeaturesEXT,
|
||||
}
|
||||
|
||||
macro_rules! features {
|
||||
($($kind:ident $args:tt,)+) => (
|
||||
impl Features {
|
||||
/// Builds a `Features` object with all values to false.
|
||||
pub fn none() -> Features {
|
||||
Features {
|
||||
$(
|
||||
$name: false,
|
||||
)+
|
||||
}
|
||||
let mut out = Features::default();
|
||||
$(features_init!($kind $args, out, false);)+
|
||||
out
|
||||
}
|
||||
|
||||
/// Builds a `Features` object with all values to true.
|
||||
@ -64,11 +285,9 @@ macro_rules! features {
|
||||
/// > **Note**: This function is used for testing purposes, and is probably useless in
|
||||
/// > a real code.
|
||||
pub fn all() -> Features {
|
||||
Features {
|
||||
$(
|
||||
$name: true,
|
||||
)+
|
||||
}
|
||||
let mut out = Features::default();
|
||||
$(features_init!($kind $args, out, true);)+
|
||||
out
|
||||
}
|
||||
|
||||
/// Returns true if `self` is a superset of the parameter.
|
||||
@ -76,7 +295,7 @@ macro_rules! features {
|
||||
/// That is, for each feature of the parameter that is true, the corresponding value
|
||||
/// in self is true as well.
|
||||
pub fn superset_of(&self, other: &Features) -> bool {
|
||||
$((self.$name == true || other.$name == false))&&+
|
||||
$(features_superset_of!($kind $args, self, other))&&+
|
||||
}
|
||||
|
||||
/// Builds a `Features` that is the intersection of `self` and another `Features`
|
||||
@ -84,97 +303,184 @@ macro_rules! features {
|
||||
///
|
||||
/// The result's field will be true if it is also true in both `self` and `other`.
|
||||
pub fn intersection(&self, other: &Features) -> Features {
|
||||
Features {
|
||||
$(
|
||||
$name: self.$name && other.$name,
|
||||
)+
|
||||
}
|
||||
let mut out = Self::none();
|
||||
$(features_intersection!($kind $args, out, self, other);)+
|
||||
out
|
||||
}
|
||||
|
||||
/// Builds a `Features` that is the difference of another `Features` object from `self`.
|
||||
///
|
||||
/// The result's field will be true if it is true in `self` but not `other`.
|
||||
pub fn difference(&self, other: &Features) -> Features {
|
||||
Features {
|
||||
$(
|
||||
$name: self.$name && !other.$name,
|
||||
)+
|
||||
}
|
||||
let mut out = Self::none();
|
||||
$(features_difference!($kind $args, out, self, other);)+
|
||||
out
|
||||
}
|
||||
|
||||
pub(crate) fn from_vulkan_features(features: vk::PhysicalDeviceFeatures) -> Features {
|
||||
Features {
|
||||
$(
|
||||
$name: features.$vk != 0,
|
||||
)+
|
||||
}
|
||||
let mut out = Self::none();
|
||||
$(from_feature_v1!($kind $args, out, features);)+
|
||||
out
|
||||
}
|
||||
|
||||
pub(crate) fn into_vulkan_features(self) -> vk::PhysicalDeviceFeatures {
|
||||
vk::PhysicalDeviceFeatures {
|
||||
$(
|
||||
$vk: if self.$name { vk::TRUE } else { vk::FALSE },
|
||||
)+
|
||||
}
|
||||
let mut out: vk::PhysicalDeviceFeatures = unsafe { mem::zeroed() };
|
||||
$(into_feature_v1!($kind $args, out, self);)+
|
||||
out
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn into_vulkan_features_v2(&self) -> Pin<Box<FeaturesFfi>> {
|
||||
let mut features = FeaturesFfi::new();
|
||||
unsafe {
|
||||
let mut next = FeaturesFfi::mut_base_ptr(&mut features);
|
||||
while let Some(next_mut) = next.as_mut() {
|
||||
match next_mut.sType {
|
||||
vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR => {
|
||||
let r = (next as *mut vk::PhysicalDeviceFeatures2KHR).as_mut().unwrap();
|
||||
$(into_feature_v1!($kind $args, r.features, self);)+
|
||||
},
|
||||
_ => {
|
||||
$(into_ext_features_match!($kind $args, self, next_mut, next);)+
|
||||
},
|
||||
}
|
||||
|
||||
next = next_mut.pNext as *mut Base;
|
||||
}
|
||||
}
|
||||
features
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn from_vulkan_features_v2(features: &vk::PhysicalDeviceFeatures2KHR) -> Self {
|
||||
let mut output = Self::none();
|
||||
|
||||
unsafe {
|
||||
let mut next: *const Base = features as *const vk::PhysicalDeviceFeatures2KHR as _;
|
||||
while let Some(next_ref) = next.as_ref() {
|
||||
match next_ref.sType {
|
||||
vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR => {
|
||||
let r = (next as *const vk::PhysicalDeviceFeatures2KHR).as_ref().unwrap();
|
||||
$(from_feature_v1!($kind $args, output, r.features);)+
|
||||
},
|
||||
_ => {
|
||||
$(from_ext_features_match!($kind $args, output, next_ref, next);)+
|
||||
},
|
||||
}
|
||||
|
||||
next = next_ref.pNext as *const Base;
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl FeaturesFfi {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new() -> Pin<Box<Self>> {
|
||||
#![allow(unused_assignments)]
|
||||
|
||||
let this = FeaturesFfi {
|
||||
_pinned: PhantomPinned,
|
||||
main: vk::PhysicalDeviceFeatures2KHR {
|
||||
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
|
||||
pNext: ptr::null(),
|
||||
features: unsafe { mem::zeroed() },
|
||||
},
|
||||
.. unsafe { mem::zeroed() }
|
||||
};
|
||||
|
||||
let mut this = Box::pin(this);
|
||||
unsafe {
|
||||
let this = this.as_mut();
|
||||
let this = this.get_unchecked_mut();
|
||||
let mut base = &mut this.main as *mut _ as *mut Base;
|
||||
$(features_ffi_init_pinned!($kind $args, this, base);)*
|
||||
}
|
||||
this
|
||||
}
|
||||
#[inline(always)]
|
||||
pub(crate) fn base_ptr(&self) -> *const Base {
|
||||
&self.main as *const _ as *const Base
|
||||
}
|
||||
#[inline(always)]
|
||||
pub(crate) fn mut_base_ptr(this: &mut Pin<Box<Self>>) -> *mut Base {
|
||||
unsafe {
|
||||
let this = this.as_mut();
|
||||
let this = this.get_unchecked_mut();
|
||||
&mut this.main as *mut _ as *mut Base
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
features! {
|
||||
robust_buffer_access => robustBufferAccess,
|
||||
full_draw_index_uint32 => fullDrawIndexUint32,
|
||||
image_cube_array => imageCubeArray,
|
||||
independent_blend => independentBlend,
|
||||
geometry_shader => geometryShader,
|
||||
tessellation_shader => tessellationShader,
|
||||
sample_rate_shading => sampleRateShading,
|
||||
dual_src_blend => dualSrcBlend,
|
||||
logic_op => logicOp,
|
||||
multi_draw_indirect => multiDrawIndirect,
|
||||
draw_indirect_first_instance => drawIndirectFirstInstance,
|
||||
depth_clamp => depthClamp,
|
||||
depth_bias_clamp => depthBiasClamp,
|
||||
fill_mode_non_solid => fillModeNonSolid,
|
||||
depth_bounds => depthBounds,
|
||||
wide_lines => wideLines,
|
||||
large_points => largePoints,
|
||||
alpha_to_one => alphaToOne,
|
||||
multi_viewport => multiViewport,
|
||||
sampler_anisotropy => samplerAnisotropy,
|
||||
texture_compression_etc2 => textureCompressionETC2,
|
||||
texture_compression_astc_ldr => textureCompressionASTC_LDR,
|
||||
texture_compression_bc => textureCompressionBC,
|
||||
occlusion_query_precise => occlusionQueryPrecise,
|
||||
pipeline_statistics_query => pipelineStatisticsQuery,
|
||||
vertex_pipeline_stores_and_atomics => vertexPipelineStoresAndAtomics,
|
||||
fragment_stores_and_atomics => fragmentStoresAndAtomics,
|
||||
shader_tessellation_and_geometry_point_size => shaderTessellationAndGeometryPointSize,
|
||||
shader_image_gather_extended => shaderImageGatherExtended,
|
||||
shader_storage_image_extended_formats => shaderStorageImageExtendedFormats,
|
||||
shader_storage_image_multisample => shaderStorageImageMultisample,
|
||||
shader_storage_image_read_without_format => shaderStorageImageReadWithoutFormat,
|
||||
shader_storage_image_write_without_format => shaderStorageImageWriteWithoutFormat,
|
||||
shader_uniform_buffer_array_dynamic_indexing => shaderUniformBufferArrayDynamicIndexing,
|
||||
shader_sampled_image_array_dynamic_indexing => shaderSampledImageArrayDynamicIndexing,
|
||||
shader_storage_buffer_array_dynamic_indexing => shaderStorageBufferArrayDynamicIndexing,
|
||||
shader_storage_image_array_dynamic_indexing => shaderStorageImageArrayDynamicIndexing,
|
||||
shader_clip_distance => shaderClipDistance,
|
||||
shader_cull_distance => shaderCullDistance,
|
||||
shader_f3264 => shaderf3264,
|
||||
shader_int64 => shaderInt64,
|
||||
shader_int16 => shaderInt16,
|
||||
shader_resource_residency => shaderResourceResidency,
|
||||
shader_resource_min_lod => shaderResourceMinLod,
|
||||
sparse_binding => sparseBinding,
|
||||
sparse_residency_buffer => sparseResidencyBuffer,
|
||||
sparse_residency_image2d => sparseResidencyImage2D,
|
||||
sparse_residency_image3d => sparseResidencyImage3D,
|
||||
sparse_residency2_samples => sparseResidency2Samples,
|
||||
sparse_residency4_samples => sparseResidency4Samples,
|
||||
sparse_residency8_samples => sparseResidency8Samples,
|
||||
sparse_residency16_samples => sparseResidency16Samples,
|
||||
sparse_residency_aliased => sparseResidencyAliased,
|
||||
variable_multisample_rate => variableMultisampleRate,
|
||||
inherited_queries => inheritedQueries,
|
||||
core { robust_buffer_access => robustBufferAccess },
|
||||
core { full_draw_index_uint32 => fullDrawIndexUint32 },
|
||||
core { image_cube_array => imageCubeArray },
|
||||
core { independent_blend => independentBlend },
|
||||
core { geometry_shader => geometryShader },
|
||||
core { tessellation_shader => tessellationShader },
|
||||
core { sample_rate_shading => sampleRateShading },
|
||||
core { dual_src_blend => dualSrcBlend },
|
||||
core { logic_op => logicOp },
|
||||
core { multi_draw_indirect => multiDrawIndirect },
|
||||
core { draw_indirect_first_instance => drawIndirectFirstInstance },
|
||||
core { depth_clamp => depthClamp },
|
||||
core { depth_bias_clamp => depthBiasClamp },
|
||||
core { fill_mode_non_solid => fillModeNonSolid },
|
||||
core { depth_bounds => depthBounds },
|
||||
core { wide_lines => wideLines },
|
||||
core { large_points => largePoints },
|
||||
core { alpha_to_one => alphaToOne },
|
||||
core { multi_viewport => multiViewport },
|
||||
core { sampler_anisotropy => samplerAnisotropy },
|
||||
core { texture_compression_etc2 => textureCompressionETC2 },
|
||||
core { texture_compression_astc_ldr => textureCompressionASTC_LDR },
|
||||
core { texture_compression_bc => textureCompressionBC },
|
||||
core { occlusion_query_precise => occlusionQueryPrecise },
|
||||
core { pipeline_statistics_query => pipelineStatisticsQuery },
|
||||
core { vertex_pipeline_stores_and_atomics => vertexPipelineStoresAndAtomics },
|
||||
core { fragment_stores_and_atomics => fragmentStoresAndAtomics },
|
||||
core { shader_tessellation_and_geometry_point_size => shaderTessellationAndGeometryPointSize },
|
||||
core { shader_image_gather_extended => shaderImageGatherExtended },
|
||||
core { shader_storage_image_extended_formats => shaderStorageImageExtendedFormats },
|
||||
core { shader_storage_image_multisample => shaderStorageImageMultisample },
|
||||
core { shader_storage_image_read_without_format => shaderStorageImageReadWithoutFormat },
|
||||
core { shader_storage_image_write_without_format => shaderStorageImageWriteWithoutFormat },
|
||||
core { shader_uniform_buffer_array_dynamic_indexing => shaderUniformBufferArrayDynamicIndexing },
|
||||
core { shader_sampled_image_array_dynamic_indexing => shaderSampledImageArrayDynamicIndexing },
|
||||
core { shader_storage_buffer_array_dynamic_indexing => shaderStorageBufferArrayDynamicIndexing },
|
||||
core { shader_storage_image_array_dynamic_indexing => shaderStorageImageArrayDynamicIndexing },
|
||||
core { shader_clip_distance => shaderClipDistance },
|
||||
core { shader_cull_distance => shaderCullDistance },
|
||||
core { shader_f3264 => shaderf3264 },
|
||||
core { shader_int64 => shaderInt64 },
|
||||
core { shader_int16 => shaderInt16 },
|
||||
core { shader_resource_residency => shaderResourceResidency },
|
||||
core { shader_resource_min_lod => shaderResourceMinLod },
|
||||
core { sparse_binding => sparseBinding },
|
||||
core { sparse_residency_buffer => sparseResidencyBuffer },
|
||||
core { sparse_residency_image2d => sparseResidencyImage2D },
|
||||
core { sparse_residency_image3d => sparseResidencyImage3D },
|
||||
core { sparse_residency2_samples => sparseResidency2Samples },
|
||||
core { sparse_residency4_samples => sparseResidency4Samples },
|
||||
core { sparse_residency8_samples => sparseResidency8Samples },
|
||||
core { sparse_residency16_samples => sparseResidency16Samples },
|
||||
core { sparse_residency_aliased => sparseResidencyAliased },
|
||||
core { variable_multisample_rate => variableMultisampleRate },
|
||||
core { inherited_queries => inheritedQueries },
|
||||
|
||||
extension {
|
||||
ty: vk::PhysicalDeviceBufferAddressFeaturesEXT,
|
||||
ffi_name: phys_dev_buf_addr,
|
||||
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT,
|
||||
fields: [
|
||||
buffer_device_address => bufferDeviceAddress,
|
||||
buffer_device_address_capture_replay => bufferDeviceAddressCaptureReplay,
|
||||
buffer_device_address_multi_device => bufferDeviceAddressMultiDevice,
|
||||
],
|
||||
},
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ use Error;
|
||||
use OomError;
|
||||
use VulkanObject;
|
||||
|
||||
use features::Features;
|
||||
use features::{Features, FeaturesFfi};
|
||||
use instance::{InstanceExtensions, RawInstanceExtensions};
|
||||
use version::Version;
|
||||
|
||||
@ -444,14 +444,11 @@ impl Instance {
|
||||
output.memoryProperties
|
||||
};
|
||||
|
||||
let available_features: vk::PhysicalDeviceFeatures = unsafe {
|
||||
let mut output = vk::PhysicalDeviceFeatures2KHR {
|
||||
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR,
|
||||
pNext: ptr::null_mut(),
|
||||
features: mem::zeroed(),
|
||||
};
|
||||
vk.GetPhysicalDeviceFeatures2KHR(device, &mut output);
|
||||
output.features
|
||||
let available_features: Features = unsafe {
|
||||
let mut output = FeaturesFfi::new();
|
||||
let ptr = FeaturesFfi::mut_base_ptr(&mut output) as *mut _;
|
||||
vk.GetPhysicalDeviceFeatures2KHR(device, ptr);
|
||||
Features::from_vulkan_features_v2(&output.main)
|
||||
};
|
||||
|
||||
output.push(PhysicalDeviceInfos {
|
||||
@ -460,7 +457,7 @@ impl Instance {
|
||||
extended_properties,
|
||||
memory,
|
||||
queue_families,
|
||||
available_features: Features::from_vulkan_features(available_features),
|
||||
available_features,
|
||||
});
|
||||
}
|
||||
output
|
||||
|
Loading…
Reference in New Issue
Block a user