mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-25 16:25:31 +00:00
Auto-generate extensions and features, check requirements (#1599)
* Migrate to using Ash as a base * Small change to test, to make it applicable to Vulkan 1.1 * Auto-generate extensions, check extension requirements * Add documentation to extensions, use vk.xml from Ash 0.32.1 * Remove `RawDeviceExtensions` and `RawInstanceExtensions` * Add extension conflict checking * Small fix * Add auto-generated features, more requirements checks * Features documentation * Macrofy required_if_supported_extensions * Include vulkano-gen tool * Move autogen imports to macros * Change autogen into a build script * Delete leftover file
This commit is contained in:
parent
15fcbafaf9
commit
71191bd24d
13
Cargo.toml
13
Cargo.toml
@ -1,12 +1,3 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"examples",
|
||||
"vk-sys",
|
||||
"vulkano",
|
||||
"vulkano-shaders",
|
||||
"vulkano-win"
|
||||
]
|
||||
|
||||
exclude = [
|
||||
"www"
|
||||
]
|
||||
members = ["examples", "vk-sys", "vulkano", "vulkano-shaders", "vulkano-win"]
|
||||
exclude = ["www"]
|
||||
|
@ -14,7 +14,9 @@ readme = "../README.md"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
ash = "0.32"
|
||||
# When updating Ash, also update vk.xml to the same Vulkan patch version that Ash uses.
|
||||
# All versions of vk.xml can be found at https://github.com/KhronosGroup/Vulkan-Headers/commits/master/registry/vk.xml.
|
||||
ash = "0.32.1"
|
||||
crossbeam-queue = "0.3"
|
||||
fnv = "1.0"
|
||||
half = "1.7"
|
||||
@ -22,3 +24,9 @@ lazy_static = "1.4"
|
||||
parking_lot = { version = "0.11.1", features = ["send_guard"] }
|
||||
shared_library = "0.1"
|
||||
smallvec = "1.6"
|
||||
|
||||
[build-dependencies]
|
||||
heck = "0.3"
|
||||
indexmap = "1.6"
|
||||
regex = "1.5"
|
||||
vk-parse = "0.6"
|
||||
|
399
vulkano/autogen/extensions.rs
Normal file
399
vulkano/autogen/extensions.rs
Normal file
@ -0,0 +1,399 @@
|
||||
// Copyright (c) 2021 The Vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use heck::SnakeCase;
|
||||
use indexmap::IndexMap;
|
||||
use std::io::Write;
|
||||
use vk_parse::Extension;
|
||||
|
||||
// This is not included in vk.xml, so it's added here manually
|
||||
fn required_if_supported(name: &str) -> bool {
|
||||
match name {
|
||||
"VK_KHR_portability_subset" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn conflicts_extensions(name: &str) -> &'static [&'static str] {
|
||||
match name {
|
||||
"VK_KHR_buffer_device_address" => &["VK_EXT_buffer_device_address"],
|
||||
"VK_EXT_buffer_device_address" => &["VK_KHR_buffer_device_address"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(writer: &mut W, extensions: &IndexMap<&str, &Extension>) {
|
||||
write_device_extensions(writer, make_vulkano_extensions("device", &extensions));
|
||||
write!(writer, "\n\n").unwrap();
|
||||
write_instance_extensions(writer, make_vulkano_extensions("instance", &extensions));
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct VulkanoExtension {
|
||||
member: String,
|
||||
raw: String,
|
||||
requires_core: (u16, u16),
|
||||
requires_device_extensions: Vec<String>,
|
||||
requires_instance_extensions: Vec<String>,
|
||||
required_if_supported: bool,
|
||||
conflicts_device_extensions: Vec<String>,
|
||||
status: Option<ExtensionStatus>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Replacement {
|
||||
Core((u16, u16)),
|
||||
DeviceExtension(String),
|
||||
InstanceExtension(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum ExtensionStatus {
|
||||
Promoted(Replacement),
|
||||
Deprecated(Option<Replacement>),
|
||||
}
|
||||
|
||||
fn make_vulkano_extensions(
|
||||
ty: &str,
|
||||
extensions: &IndexMap<&str, &Extension>,
|
||||
) -> Vec<VulkanoExtension> {
|
||||
extensions
|
||||
.values()
|
||||
.filter(|ext| ext.ext_type.as_ref().unwrap() == ty)
|
||||
.map(|ext| {
|
||||
let raw = ext.name.to_owned();
|
||||
let member = raw.strip_prefix("VK_").unwrap().to_snake_case();
|
||||
let (major, minor) = ext
|
||||
.requires_core
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or("1.0")
|
||||
.split_once('.')
|
||||
.unwrap();
|
||||
let requires_extensions: Vec<_> = ext
|
||||
.requires
|
||||
.as_ref()
|
||||
.map(|s| s.split(',').collect())
|
||||
.unwrap_or_default();
|
||||
let conflicts_extensions = conflicts_extensions(&ext.name);
|
||||
|
||||
VulkanoExtension {
|
||||
member: member.clone(),
|
||||
raw,
|
||||
requires_core: (major.parse().unwrap(), minor.parse().unwrap()),
|
||||
requires_device_extensions: requires_extensions
|
||||
.iter()
|
||||
.filter(|&&vk_name| extensions[vk_name].ext_type.as_ref().unwrap() == "device")
|
||||
.map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case())
|
||||
.collect(),
|
||||
requires_instance_extensions: requires_extensions
|
||||
.iter()
|
||||
.filter(|&&vk_name| {
|
||||
extensions[vk_name].ext_type.as_ref().unwrap() == "instance"
|
||||
})
|
||||
.map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case())
|
||||
.collect(),
|
||||
required_if_supported: required_if_supported(ext.name.as_str()),
|
||||
conflicts_device_extensions: conflicts_extensions
|
||||
.iter()
|
||||
.filter(|&&vk_name| extensions[vk_name].ext_type.as_ref().unwrap() == "device")
|
||||
.map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case())
|
||||
.collect(),
|
||||
status: ext
|
||||
.promotedto
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.and_then(|pr| {
|
||||
if let Some(version) = pr.strip_prefix("VK_VERSION_") {
|
||||
let (major, minor) = version.split_once('_').unwrap();
|
||||
Some(ExtensionStatus::Promoted(Replacement::Core((
|
||||
major.parse().unwrap(),
|
||||
minor.parse().unwrap(),
|
||||
))))
|
||||
} else {
|
||||
let member = pr.strip_prefix("VK_").unwrap().to_snake_case();
|
||||
match extensions[pr].ext_type.as_ref().unwrap().as_str() {
|
||||
"device" => Some(ExtensionStatus::Promoted(
|
||||
Replacement::DeviceExtension(member),
|
||||
)),
|
||||
"instance" => Some(ExtensionStatus::Promoted(
|
||||
Replacement::InstanceExtension(member),
|
||||
)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
ext.deprecatedby
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.and_then(|depr| {
|
||||
if depr.is_empty() {
|
||||
Some(ExtensionStatus::Deprecated(None))
|
||||
} else if let Some(version) = depr.strip_prefix("VK_VERSION_") {
|
||||
let (major, minor) = version.split_once('_').unwrap();
|
||||
Some(ExtensionStatus::Deprecated(Some(Replacement::Core((
|
||||
major.parse().unwrap(),
|
||||
minor.parse().unwrap(),
|
||||
)))))
|
||||
} else {
|
||||
let member = depr.strip_prefix("VK_").unwrap().to_snake_case();
|
||||
match extensions[depr].ext_type.as_ref().unwrap().as_str() {
|
||||
"device" => Some(ExtensionStatus::Deprecated(Some(
|
||||
Replacement::DeviceExtension(member),
|
||||
))),
|
||||
"instance" => Some(ExtensionStatus::Deprecated(Some(
|
||||
Replacement::InstanceExtension(member),
|
||||
))),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
})
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn write_device_extensions<W, I>(writer: &mut W, extensions: I)
|
||||
where
|
||||
W: Write,
|
||||
I: IntoIterator<Item = VulkanoExtension>,
|
||||
{
|
||||
write!(writer, "crate::device::extensions::device_extensions! {{").unwrap();
|
||||
for ext in extensions {
|
||||
write!(writer, "\n\t{} => {{", ext.member).unwrap();
|
||||
write_doc(writer, &ext);
|
||||
write!(writer, "\n\t\traw: b\"{}\",", ext.raw).unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequires_core: crate::Version::V{}_{},",
|
||||
ext.requires_core.0, ext.requires_core.1
|
||||
)
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequires_device_extensions: [{}],",
|
||||
ext.requires_device_extensions.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequires_instance_extensions: [{}],",
|
||||
ext.requires_instance_extensions.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequired_if_supported: {},",
|
||||
ext.required_if_supported
|
||||
)
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\tconflicts_device_extensions: [{}],",
|
||||
ext.conflicts_device_extensions.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
/*if let Some(promoted_to_core) = ext.promoted_to_core {
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\tpromoted_to_core: Some(Version::V{}_{}),",
|
||||
promoted_to_core.0, promoted_to_core.1
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
write!(writer, "\n\t\tpromoted_to_core: None,",).unwrap();
|
||||
}*/
|
||||
|
||||
write!(writer, "\n\t}},").unwrap();
|
||||
}
|
||||
write!(writer, "\n}}").unwrap();
|
||||
}
|
||||
|
||||
fn write_instance_extensions<W, I>(writer: &mut W, extensions: I)
|
||||
where
|
||||
W: Write,
|
||||
I: IntoIterator<Item = VulkanoExtension>,
|
||||
{
|
||||
write!(
|
||||
writer,
|
||||
"crate::instance::extensions::instance_extensions! {{"
|
||||
)
|
||||
.unwrap();
|
||||
for ext in extensions {
|
||||
write!(writer, "\n\t{} => {{", ext.member).unwrap();
|
||||
write_doc(writer, &ext);
|
||||
write!(writer, "\n\t\traw: b\"{}\",", ext.raw).unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequires_core: crate::Version::V{}_{},",
|
||||
ext.requires_core.0, ext.requires_core.1
|
||||
)
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequires_extensions: [{}],",
|
||||
ext.requires_instance_extensions.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
/*if let Some(promoted_to_core) = ext.promoted_to_core {
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\tpromoted_to_core: Some(crate::Version::V{}_{}),",
|
||||
promoted_to_core.0, promoted_to_core.1
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
write!(writer, "\n\t\tpromoted_to_core: None,",).unwrap();
|
||||
}*/
|
||||
|
||||
write!(writer, "\n\t}},").unwrap();
|
||||
}
|
||||
write!(writer, "\n}}").unwrap();
|
||||
}
|
||||
|
||||
fn write_doc<W>(writer: &mut W, ext: &VulkanoExtension)
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
write!(writer, "\n\t\tdoc: \"\n\t\t\t- [Vulkan documentation](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/{}.html)", ext.raw).unwrap();
|
||||
|
||||
if ext.requires_core != (1, 0) {
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\t\t- Requires Vulkan {}.{}",
|
||||
ext.requires_core.0, ext.requires_core.1
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if !ext.requires_device_extensions.is_empty() {
|
||||
let links: Vec<_> = ext
|
||||
.requires_device_extensions
|
||||
.iter()
|
||||
.map(|ext| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext))
|
||||
.collect();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\t\t- Requires device extension{}: {}",
|
||||
if ext.requires_device_extensions.len() > 1 {
|
||||
"s"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
links.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if !ext.requires_instance_extensions.is_empty() {
|
||||
let links: Vec<_> = ext
|
||||
.requires_instance_extensions
|
||||
.iter()
|
||||
.map(|ext| format!("[`{}`](crate::instance::InstanceExtensions::{0})", ext))
|
||||
.collect();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\t\t- Requires instance extension{}: {}",
|
||||
if ext.requires_instance_extensions.len() > 1 {
|
||||
"s"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
links.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if ext.required_if_supported {
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\t\t- Must be enabled if it is supported by the physical device",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if !ext.conflicts_device_extensions.is_empty() {
|
||||
let links: Vec<_> = ext
|
||||
.conflicts_device_extensions
|
||||
.iter()
|
||||
.map(|ext| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext))
|
||||
.collect();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\t\t- Conflicts with device extension{}: {}",
|
||||
if ext.conflicts_device_extensions.len() > 1 {
|
||||
"s"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
links.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if let Some(status) = ext.status.as_ref() {
|
||||
match status {
|
||||
ExtensionStatus::Promoted(replacement) => {
|
||||
write!(writer, "\n\t\t\t- Promoted to ",).unwrap();
|
||||
|
||||
match replacement {
|
||||
Replacement::Core(version) => {
|
||||
write!(writer, "Vulkan {}.{}", version.0, version.1).unwrap();
|
||||
}
|
||||
Replacement::DeviceExtension(ext) => {
|
||||
write!(writer, "[`{}`](crate::device::DeviceExtensions::{0})", ext)
|
||||
.unwrap();
|
||||
}
|
||||
Replacement::InstanceExtension(ext) => {
|
||||
write!(
|
||||
writer,
|
||||
"[`{}`](crate::instance::InstanceExtensions::{0})",
|
||||
ext
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
ExtensionStatus::Deprecated(replacement) => {
|
||||
write!(writer, "\n\t\t\t- Deprecated ",).unwrap();
|
||||
|
||||
match replacement {
|
||||
Some(Replacement::Core(version)) => {
|
||||
write!(writer, "by Vulkan {}.{}", version.0, version.1).unwrap();
|
||||
}
|
||||
Some(Replacement::DeviceExtension(ext)) => {
|
||||
write!(
|
||||
writer,
|
||||
"by [`{}`](crate::device::DeviceExtensions::{0})",
|
||||
ext
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
Some(Replacement::InstanceExtension(ext)) => {
|
||||
write!(
|
||||
writer,
|
||||
"by [`{}`](crate::instance::InstanceExtensions::{0})",
|
||||
ext
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
None => {
|
||||
write!(writer, "without a replacement").unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write!(writer, "\n\t\t\",").unwrap();
|
||||
}
|
384
vulkano/autogen/features.rs
Normal file
384
vulkano/autogen/features.rs
Normal file
@ -0,0 +1,384 @@
|
||||
// Copyright (c) 2021 The Vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use heck::SnakeCase;
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
io::Write,
|
||||
};
|
||||
use vk_parse::{Type, TypeMember, TypeMemberMarkup, TypeSpec};
|
||||
|
||||
// This is not included in vk.xml, so it's added here manually
|
||||
fn requires_features(name: &str) -> &'static [&'static str] {
|
||||
match name {
|
||||
"sparseImageInt64Atomics" => &["shaderImageInt64Atomics"],
|
||||
"sparseImageFloat32Atomics" => &["shaderImageFloat32Atomics"],
|
||||
"sparseImageFloat32AtomicAdd" => &["shaderImageFloat32AtomicAdd"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
fn conflicts_features(name: &str) -> &'static [&'static str] {
|
||||
match name {
|
||||
"shadingRateImage" => &[
|
||||
"pipelineFragmentShadingRate",
|
||||
"primitiveFragmentShadingRate",
|
||||
"attachmentFragmentShadingRate",
|
||||
],
|
||||
"fragmentDensityMap" => &[
|
||||
"pipelineFragmentShadingRate",
|
||||
"primitiveFragmentShadingRate",
|
||||
"attachmentFragmentShadingRate",
|
||||
],
|
||||
"pipelineFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
|
||||
"primitiveFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
|
||||
"attachmentFragmentShadingRate" => &["shadingRateImage", "fragmentDensityMap"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
fn required_by_extensions(name: &str) -> &'static [&'static str] {
|
||||
match name {
|
||||
"shaderDrawParameters" => &["VK_KHR_shader_draw_parameters"],
|
||||
"drawIndirectCount" => &["VK_KHR_draw_indirect_count"],
|
||||
"samplerMirrorClampToEdge" => &["VK_KHR_sampler_mirror_clamp_to_edge"],
|
||||
"descriptorIndexing" => &["VK_EXT_descriptor_indexing"],
|
||||
"samplerFilterMinmax" => &["VK_EXT_sampler_filter_minmax"],
|
||||
"shaderOutputViewportIndex" => &["VK_EXT_shader_viewport_index_layer"],
|
||||
"shaderOutputLayer" => &["VK_EXT_shader_viewport_index_layer"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(writer: &mut W, types: &HashMap<&str, (&Type, Vec<&str>)>) {
|
||||
write!(writer, "crate::device::features::features! {{").unwrap();
|
||||
|
||||
for feat in make_vulkano_features(&types) {
|
||||
write!(writer, "\n\t{} => {{", feat.member).unwrap();
|
||||
write_doc(writer, &feat);
|
||||
write!(writer, "\n\t\tffi_name: {},", feat.ffi_name).unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\tffi_members: [{}],",
|
||||
feat.ffi_members.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequires_features: [{}],",
|
||||
feat.requires_features.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\tconflicts_features: [{}],",
|
||||
feat.conflicts_features.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequired_by_extensions: [{}],",
|
||||
feat.required_by_extensions.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
write!(writer, "\n\t}},").unwrap();
|
||||
}
|
||||
|
||||
write!(
|
||||
writer,
|
||||
"\n}}\n\ncrate::device::features::features_ffi! {{\n\tapi_version,\n\textensions,"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for ffi in make_vulkano_features_ffi(&types) {
|
||||
write!(writer, "\n\t{} => {{", ffi.member).unwrap();
|
||||
write!(writer, "\n\t\tty: {},", ffi.ty).unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\tprovided_by: [{}],",
|
||||
ffi.provided_by.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
write!(writer, "\n\t\tconflicts: [{}],", ffi.conflicts.join(", ")).unwrap();
|
||||
write!(writer, "\n\t}},").unwrap();
|
||||
}
|
||||
|
||||
write!(writer, "\n}}").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct VulkanoFeature {
|
||||
member: String,
|
||||
vulkan_doc: String,
|
||||
ffi_name: String,
|
||||
ffi_members: Vec<String>,
|
||||
requires_features: Vec<String>,
|
||||
conflicts_features: Vec<String>,
|
||||
required_by_extensions: Vec<String>,
|
||||
}
|
||||
|
||||
fn make_vulkano_features(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<VulkanoFeature> {
|
||||
let mut features = HashMap::new();
|
||||
std::iter::once(&types["VkPhysicalDeviceFeatures"])
|
||||
.chain(sorted_structs(types).into_iter())
|
||||
.filter(|(ty, _)| {
|
||||
ty.name.as_ref().map(|s| s.as_str()) == Some("VkPhysicalDeviceFeatures")
|
||||
|| ty.structextends.as_ref().map(|s| s.as_str())
|
||||
== Some("VkPhysicalDeviceFeatures2,VkDeviceCreateInfo")
|
||||
})
|
||||
.for_each(|(ty, _)| {
|
||||
let vulkan_ty_name = ty.name.as_ref().unwrap();
|
||||
|
||||
let ty_name = if vulkan_ty_name == "VkPhysicalDeviceFeatures" {
|
||||
"features_vulkan10.features".to_owned()
|
||||
} else {
|
||||
ffi_member(vulkan_ty_name)
|
||||
};
|
||||
|
||||
members(ty).into_iter().for_each(|name| {
|
||||
let member = name.to_snake_case();
|
||||
match features.entry(member.clone()) {
|
||||
Entry::Vacant(entry) => {
|
||||
let requires_features = requires_features(name);
|
||||
let conflicts_features = conflicts_features(name);
|
||||
let required_by_extensions = required_by_extensions(name);
|
||||
|
||||
entry.insert(VulkanoFeature {
|
||||
member: member.clone(),
|
||||
vulkan_doc: format!("{}.html#features-{}", vulkan_ty_name, name),
|
||||
ffi_name: member,
|
||||
ffi_members: vec![ty_name.to_owned()],
|
||||
requires_features: requires_features
|
||||
.into_iter()
|
||||
.map(|&s| s.to_snake_case())
|
||||
.collect(),
|
||||
conflicts_features: conflicts_features
|
||||
.into_iter()
|
||||
.map(|&s| s.to_snake_case())
|
||||
.collect(),
|
||||
required_by_extensions: required_by_extensions
|
||||
.iter()
|
||||
.map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case())
|
||||
.collect(),
|
||||
});
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
entry.into_mut().ffi_members.push(ty_name.to_owned());
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
let mut names: Vec<_> = features.values().map(|feat| feat.member.clone()).collect();
|
||||
names.sort_unstable();
|
||||
names
|
||||
.into_iter()
|
||||
.map(|name| features.remove(&name).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn write_doc<W>(writer: &mut W, feat: &VulkanoFeature)
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
write!(writer, "\n\t\tdoc: \"\n\t\t\t- [Vulkan documentation](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/{})", feat.vulkan_doc).unwrap();
|
||||
|
||||
if !feat.requires_features.is_empty() {
|
||||
let links: Vec<_> = feat
|
||||
.requires_features
|
||||
.iter()
|
||||
.map(|ext| format!("[`{}`](crate::device::Features::{0})", ext))
|
||||
.collect();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\t\t- Requires feature{}: {}",
|
||||
if feat.requires_features.len() > 1 {
|
||||
"s"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
links.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if !feat.required_by_extensions.is_empty() {
|
||||
let links: Vec<_> = feat
|
||||
.required_by_extensions
|
||||
.iter()
|
||||
.map(|ext| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext))
|
||||
.collect();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\t\t- Required by device extension{}: {}",
|
||||
if feat.required_by_extensions.len() > 1 {
|
||||
"s"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
links.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if !feat.conflicts_features.is_empty() {
|
||||
let links: Vec<_> = feat
|
||||
.conflicts_features
|
||||
.iter()
|
||||
.map(|ext| format!("[`{}`](crate::device::Features::{0})", ext))
|
||||
.collect();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\t\t- Conflicts with feature{}: {}",
|
||||
if feat.conflicts_features.len() > 1 {
|
||||
"s"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
links.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
write!(writer, "\n\t\t\",").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct VulkanoFeatureFfi {
|
||||
member: String,
|
||||
ty: String,
|
||||
provided_by: Vec<String>,
|
||||
conflicts: Vec<String>,
|
||||
}
|
||||
|
||||
fn make_vulkano_features_ffi(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<VulkanoFeatureFfi> {
|
||||
let mut feature_included_in: HashMap<&str, Vec<&str>> = HashMap::new();
|
||||
sorted_structs(types)
|
||||
.into_iter()
|
||||
.map(|(ty, provided_by)| {
|
||||
let name = ty.name.as_ref().unwrap();
|
||||
let provided_by = provided_by
|
||||
.iter()
|
||||
.map(|provided_by| {
|
||||
if let Some(version) = provided_by.strip_prefix("VK_VERSION_") {
|
||||
format!("api_version >= crate::Version::V{}", version)
|
||||
} else {
|
||||
format!(
|
||||
"extensions.{}",
|
||||
provided_by
|
||||
.strip_prefix("VK_")
|
||||
.unwrap()
|
||||
.to_ascii_lowercase()
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut conflicts = vec![];
|
||||
members(ty)
|
||||
.into_iter()
|
||||
.for_each(|member| match feature_included_in.entry(member) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(vec![name]);
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
let conflicters = entry.into_mut();
|
||||
conflicters.iter().for_each(|conflicter| {
|
||||
let conflicter = ffi_member(conflicter);
|
||||
if !conflicts.contains(&conflicter) {
|
||||
conflicts.push(conflicter);
|
||||
}
|
||||
});
|
||||
conflicters.push(name);
|
||||
}
|
||||
});
|
||||
|
||||
VulkanoFeatureFfi {
|
||||
member: ffi_member(name),
|
||||
ty: name.strip_prefix("Vk").unwrap().to_owned(),
|
||||
provided_by,
|
||||
conflicts,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn sorted_structs<'a>(
|
||||
types: &'a HashMap<&str, (&'a Type, Vec<&'a str>)>,
|
||||
) -> Vec<&'a (&'a Type, Vec<&'a str>)> {
|
||||
let mut structs: Vec<_> = types
|
||||
.values()
|
||||
.filter(|(ty, _)| {
|
||||
ty.structextends.as_ref().map(|s| s.as_str())
|
||||
== Some("VkPhysicalDeviceFeatures2,VkDeviceCreateInfo")
|
||||
})
|
||||
.collect();
|
||||
let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Features$").unwrap();
|
||||
structs.sort_unstable_by_key(|&(ty, provided_by)| {
|
||||
let name = ty.name.as_ref().unwrap();
|
||||
(
|
||||
!regex.is_match(name),
|
||||
if let Some(version) = provided_by
|
||||
.iter()
|
||||
.find_map(|s| s.strip_prefix("VK_VERSION_"))
|
||||
{
|
||||
let (major, minor) = version.split_once('_').unwrap();
|
||||
major.parse::<i32>().unwrap() << 22 | minor.parse::<i32>().unwrap() << 12
|
||||
} else if provided_by
|
||||
.iter()
|
||||
.find(|s| s.starts_with("VK_KHR_"))
|
||||
.is_some()
|
||||
{
|
||||
i32::MAX - 2
|
||||
} else if provided_by
|
||||
.iter()
|
||||
.find(|s| s.starts_with("VK_EXT_"))
|
||||
.is_some()
|
||||
{
|
||||
i32::MAX - 1
|
||||
} else {
|
||||
i32::MAX
|
||||
},
|
||||
name,
|
||||
)
|
||||
});
|
||||
|
||||
structs
|
||||
}
|
||||
|
||||
fn ffi_member(ty_name: &str) -> String {
|
||||
let ty_name = ty_name
|
||||
.strip_prefix("VkPhysicalDevice")
|
||||
.unwrap()
|
||||
.to_snake_case();
|
||||
let (base, suffix) = ty_name.rsplit_once("_features").unwrap();
|
||||
format!("features_{}{}", base, suffix)
|
||||
}
|
||||
|
||||
fn members(ty: &Type) -> Vec<&str> {
|
||||
if let TypeSpec::Members(members) = &ty.spec {
|
||||
members
|
||||
.iter()
|
||||
.filter_map(|member| {
|
||||
if let TypeMember::Definition(def) = member {
|
||||
let name = def.markup.iter().find_map(|markup| match markup {
|
||||
TypeMemberMarkup::Name(name) => Some(name.as_str()),
|
||||
_ => None,
|
||||
});
|
||||
if name != Some("sType") && name != Some("pNext") {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
79
vulkano/autogen/fns.rs
Normal file
79
vulkano/autogen/fns.rs
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2021 The Vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use heck::{CamelCase, SnakeCase};
|
||||
use indexmap::IndexMap;
|
||||
use std::io::Write;
|
||||
use vk_parse::{Extension, ExtensionChild, InterfaceItem};
|
||||
|
||||
pub fn write<W: Write>(writer: &mut W, extensions: &IndexMap<&str, &Extension>) {
|
||||
write_fns(writer, std::iter::empty(), "Entry");
|
||||
write!(writer, "\n\n").unwrap();
|
||||
write_fns(
|
||||
writer,
|
||||
make_vulkano_extension_fns("instance", &extensions),
|
||||
"Instance",
|
||||
);
|
||||
write!(writer, "\n\n").unwrap();
|
||||
write_fns(
|
||||
writer,
|
||||
make_vulkano_extension_fns("device", &extensions),
|
||||
"Device",
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct VulkanoFns {
|
||||
member: String,
|
||||
fn_struct: String,
|
||||
}
|
||||
|
||||
fn make_vulkano_extension_fns(
|
||||
ty: &str,
|
||||
extensions: &IndexMap<&str, &Extension>,
|
||||
) -> Vec<VulkanoFns> {
|
||||
extensions
|
||||
.values()
|
||||
.filter(|ext| ext.ext_type.as_ref().unwrap() == ty)
|
||||
// Filter only extensions that have functions
|
||||
.filter(|ext| {
|
||||
ext.children.iter().any(|ch| {
|
||||
if let ExtensionChild::Require { items, .. } = ch {
|
||||
items
|
||||
.iter()
|
||||
.any(|i| matches!(i, InterfaceItem::Command { .. }))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
})
|
||||
.map(|ext| {
|
||||
let member = ext.name.strip_prefix("VK_").unwrap().to_snake_case();
|
||||
let fn_struct = member.to_camel_case() + "Fn";
|
||||
VulkanoFns { member, fn_struct }
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn write_fns<W, I>(writer: &mut W, extension_fns: I, ty: &str)
|
||||
where
|
||||
W: Write,
|
||||
I: IntoIterator<Item = VulkanoFns>,
|
||||
{
|
||||
write!(writer, "crate::fns::fns!({}Functions, {{", ty).unwrap();
|
||||
|
||||
for version in std::array::IntoIter::new(["1_0", "1_1", "1_2"]) {
|
||||
write!(writer, "\n\tv{} => {}FnV{0},", version, ty).unwrap();
|
||||
}
|
||||
|
||||
for ext in extension_fns {
|
||||
write!(writer, "\n\t{} => {},", ext.member, ext.fn_struct).unwrap();
|
||||
}
|
||||
write!(writer, "\n}});").unwrap();
|
||||
}
|
212
vulkano/autogen/mod.rs
Normal file
212
vulkano/autogen/mod.rs
Normal file
@ -0,0 +1,212 @@
|
||||
// Copyright (c) 2021 The Vulkano developers
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or
|
||||
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
|
||||
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
|
||||
// at your option. All files in the project carrying such
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::{collections::HashMap, io::Write, path::Path};
|
||||
use vk_parse::{
|
||||
Extension, ExtensionChild, Feature, InterfaceItem, Registry, RegistryChild, Type,
|
||||
TypeCodeMarkup, TypeSpec, TypesChild,
|
||||
};
|
||||
|
||||
mod extensions;
|
||||
mod features;
|
||||
mod fns;
|
||||
|
||||
pub fn write<W: Write>(writer: &mut W) {
|
||||
let registry = get_registry("vk.xml");
|
||||
let aliases = get_aliases(®istry);
|
||||
let extensions = get_extensions(®istry);
|
||||
let features = get_features(®istry);
|
||||
let types = get_types(®istry, &aliases, &features, &extensions);
|
||||
let header_version = get_header_version(®istry);
|
||||
|
||||
write!(
|
||||
writer,
|
||||
"\
|
||||
// This file is auto-generated by vulkano-gen from vk.xml header version {}.\n\
|
||||
// It should not be edited manually. Changes should be made by editing vulkano-gen.\n\
|
||||
\n",
|
||||
header_version
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
extensions::write(writer, &extensions);
|
||||
write!(writer, "\n\n").unwrap();
|
||||
fns::write(writer, &extensions);
|
||||
write!(writer, "\n\n").unwrap();
|
||||
features::write(writer, &types);
|
||||
write!(writer, "\n").unwrap();
|
||||
}
|
||||
|
||||
fn get_registry<P: AsRef<Path> + ?Sized>(path: &P) -> Registry {
|
||||
let (registry, errors) = vk_parse::parse_file(path.as_ref()).unwrap();
|
||||
|
||||
if !errors.is_empty() {
|
||||
eprintln!("The following errors were found while parsing the file:");
|
||||
|
||||
for error in errors {
|
||||
eprintln!("{:?}", error);
|
||||
}
|
||||
}
|
||||
|
||||
registry
|
||||
}
|
||||
|
||||
fn get_aliases(registry: &Registry) -> HashMap<&str, &str> {
|
||||
registry
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|child| {
|
||||
if let RegistryChild::Types(types) = child {
|
||||
return Some(types.children.iter().filter_map(|ty| {
|
||||
if let TypesChild::Type(ty) = ty {
|
||||
if let Some(alias) = ty.alias.as_ref().map(|s| s.as_str()) {
|
||||
return Some((ty.name.as_ref().unwrap().as_str(), alias));
|
||||
}
|
||||
}
|
||||
None
|
||||
}));
|
||||
}
|
||||
None
|
||||
})
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_extensions(registry: &Registry) -> IndexMap<&str, &Extension> {
|
||||
let iter = registry
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|child| {
|
||||
if let RegistryChild::Extensions(ext) = child {
|
||||
return Some(ext.children.iter().filter_map(|ext| {
|
||||
if ext.supported.as_ref().map(|s| s.as_str()) == Some("vulkan")
|
||||
&& ext.obsoletedby.is_none()
|
||||
{
|
||||
return Some(ext);
|
||||
}
|
||||
None
|
||||
}));
|
||||
}
|
||||
None
|
||||
})
|
||||
.flatten();
|
||||
|
||||
let extensions: HashMap<&str, &Extension> =
|
||||
iter.clone().map(|ext| (ext.name.as_str(), ext)).collect();
|
||||
let mut names: Vec<_> = iter.map(|ext| ext.name.as_str()).collect();
|
||||
names.sort_unstable_by_key(|name| {
|
||||
if name.starts_with("VK_KHR_") {
|
||||
(0, name.to_owned())
|
||||
} else if name.starts_with("VK_EXT_") {
|
||||
(1, name.to_owned())
|
||||
} else {
|
||||
(2, name.to_owned())
|
||||
}
|
||||
});
|
||||
|
||||
names.iter().map(|&name| (name, extensions[name])).collect()
|
||||
}
|
||||
|
||||
fn get_features(registry: &Registry) -> IndexMap<&str, &Feature> {
|
||||
registry
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|child| {
|
||||
if let RegistryChild::Feature(feat) = child {
|
||||
return Some((feat.name.as_str(), feat));
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_types<'a>(
|
||||
registry: &'a Registry,
|
||||
aliases: &'a HashMap<&str, &str>,
|
||||
features: &'a IndexMap<&str, &Feature>,
|
||||
extensions: &'a IndexMap<&str, &Extension>,
|
||||
) -> HashMap<&'a str, (&'a Type, Vec<&'a str>)> {
|
||||
let mut types: HashMap<&str, (&Type, Vec<&str>)> = registry
|
||||
.0
|
||||
.iter()
|
||||
.filter_map(|child| {
|
||||
if let RegistryChild::Types(types) = child {
|
||||
return Some(types.children.iter().filter_map(|ty| {
|
||||
if let TypesChild::Type(ty) = ty {
|
||||
if ty.alias.is_none() {
|
||||
return ty.name.as_ref().map(|name| (name.as_str(), (ty, vec![])));
|
||||
}
|
||||
}
|
||||
None
|
||||
}));
|
||||
}
|
||||
None
|
||||
})
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
features
|
||||
.iter()
|
||||
.map(|(name, feature)| (name, &feature.children))
|
||||
.chain(extensions.iter().map(|(name, ext)| (name, &ext.children)))
|
||||
.for_each(|(provided_by, children)| {
|
||||
children
|
||||
.iter()
|
||||
.filter_map(|child| {
|
||||
if let ExtensionChild::Require { items, .. } = child {
|
||||
return Some(items.iter());
|
||||
}
|
||||
None
|
||||
})
|
||||
.flatten()
|
||||
.filter_map(|item| {
|
||||
if let InterfaceItem::Type { name, .. } = item {
|
||||
return Some(name.as_str());
|
||||
}
|
||||
None
|
||||
})
|
||||
.for_each(|item_name| {
|
||||
let item_name = aliases.get(item_name).unwrap_or(&item_name);
|
||||
if let Some(ty) = types.get_mut(item_name) {
|
||||
if !ty.1.contains(provided_by) {
|
||||
ty.1.push(provided_by);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
types
|
||||
.into_iter()
|
||||
.filter(|(_key, val)| !val.1.is_empty())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn get_header_version(registry: &Registry) -> u16 {
|
||||
registry.0.iter()
|
||||
.find_map(|child| -> Option<u16> {
|
||||
if let RegistryChild::Types(types) = child {
|
||||
return types.children.iter().find_map(|ty| -> Option<u16> {
|
||||
if let TypesChild::Type(ty) = ty {
|
||||
if let TypeSpec::Code(code) = &ty.spec {
|
||||
if code.markup.iter().any(|mkup| matches!(mkup, TypeCodeMarkup::Name(name) if name == "VK_HEADER_VERSION")) {
|
||||
return Some(code.code.rsplit_once(' ').unwrap().1.parse().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
});
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.unwrap()
|
||||
}
|
@ -7,7 +7,9 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::env;
|
||||
use std::{env, fs::File, io::BufWriter, path::Path};
|
||||
|
||||
mod autogen;
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
@ -21,4 +23,9 @@ fn main() {
|
||||
println!("cargo:rustc-link-lib=framework=UIKit");
|
||||
println!("cargo:rustc-link-lib=framework=Foundation");
|
||||
}
|
||||
|
||||
// Write autogen.rs
|
||||
let path = Path::new(&env::var_os("OUT_DIR").unwrap()).join("autogen.rs");
|
||||
let mut writer = BufWriter::new(File::create(path).unwrap());
|
||||
autogen::write(&mut writer);
|
||||
}
|
||||
|
@ -102,10 +102,7 @@ impl UnsafeBuffer {
|
||||
ash::vk::BufferCreateFlags::empty()
|
||||
};
|
||||
|
||||
if usage.device_address
|
||||
&& !(device.enabled_features().buffer_device_address
|
||||
|| device.enabled_features().ext_buffer_device_address)
|
||||
{
|
||||
if usage.device_address && !device.enabled_features().buffer_device_address {
|
||||
usage.device_address = false;
|
||||
if ash::vk::BufferUsageFlags::from(usage).is_empty() {
|
||||
// return an error iff device_address was the only requested usage and the
|
||||
|
@ -7,99 +7,157 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::iter::FromIterator;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
|
||||
use crate::check_errors;
|
||||
use crate::extensions::SupportedExtensionsError;
|
||||
pub use crate::extensions::{
|
||||
ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError,
|
||||
};
|
||||
use crate::instance::PhysicalDevice;
|
||||
use crate::VulkanObject;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
|
||||
macro_rules! device_extensions {
|
||||
($sname:ident, $rawname:ident, [$($ext_req_if_supported:ident,)*], $($ext:ident => $s:expr,)*) => (
|
||||
(
|
||||
$($member:ident => {
|
||||
doc: $doc:expr,
|
||||
raw: $raw:expr,
|
||||
requires_core: $requires_core:expr,
|
||||
requires_device_extensions: [$($requires_device_extension:ident),*],
|
||||
requires_instance_extensions: [$($requires_instance_extension:ident),*],
|
||||
required_if_supported: $required_if_supported:expr,
|
||||
conflicts_device_extensions: [$($conflicts_device_extension:ident),*],
|
||||
},)*
|
||||
) => (
|
||||
extensions! {
|
||||
$sname, $rawname,
|
||||
$( $ext => $s,)*
|
||||
DeviceExtensions,
|
||||
$( $member => {
|
||||
doc: $doc,
|
||||
raw: $raw,
|
||||
requires_core: $requires_core,
|
||||
requires_device_extensions: [$($requires_device_extension),*],
|
||||
requires_instance_extensions: [$($requires_instance_extension),*],
|
||||
},)*
|
||||
}
|
||||
|
||||
impl $rawname {
|
||||
/// See the docs of supported_by_device().
|
||||
pub fn supported_by_device_raw(physical_device: PhysicalDevice) -> Result<Self, SupportedExtensionsError> {
|
||||
let fns = physical_device.instance().fns();
|
||||
|
||||
let properties: Vec<ash::vk::ExtensionProperties> = unsafe {
|
||||
let mut num = 0;
|
||||
check_errors(fns.v1_0.enumerate_device_extension_properties(
|
||||
physical_device.internal_object(), ptr::null(), &mut num, ptr::null_mut()
|
||||
))?;
|
||||
|
||||
let mut properties = Vec::with_capacity(num as usize);
|
||||
check_errors(fns.v1_0.enumerate_device_extension_properties(
|
||||
physical_device.internal_object(), ptr::null(), &mut num, properties.as_mut_ptr()
|
||||
))?;
|
||||
properties.set_len(num as usize);
|
||||
properties
|
||||
};
|
||||
Ok($rawname(properties.iter().map(|x| unsafe { CStr::from_ptr(x.extension_name.as_ptr()) }.to_owned()).collect()))
|
||||
}
|
||||
|
||||
/// Returns an `Extensions` object with extensions supported by the `PhysicalDevice`.
|
||||
pub fn supported_by_device(physical_device: PhysicalDevice) -> Self {
|
||||
match $rawname::supported_by_device_raw(physical_device) {
|
||||
Ok(l) => l,
|
||||
Err(SupportedExtensionsError::LoadingError(_)) => unreachable!(),
|
||||
Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $sname {
|
||||
/// See the docs of supported_by_device().
|
||||
pub fn supported_by_device_raw(physical_device: PhysicalDevice) -> Result<Self, SupportedExtensionsError> {
|
||||
let fns = physical_device.instance().fns();
|
||||
|
||||
let properties: Vec<ash::vk::ExtensionProperties> = unsafe {
|
||||
let mut num = 0;
|
||||
check_errors(fns.v1_0.enumerate_device_extension_properties(
|
||||
physical_device.internal_object(), ptr::null(), &mut num, ptr::null_mut()
|
||||
))?;
|
||||
|
||||
let mut properties = Vec::with_capacity(num as usize);
|
||||
check_errors(fns.v1_0.enumerate_device_extension_properties(
|
||||
physical_device.internal_object(), ptr::null(), &mut num, properties.as_mut_ptr()
|
||||
))?;
|
||||
properties.set_len(num as usize);
|
||||
properties
|
||||
};
|
||||
|
||||
let mut extensions = $sname::none();
|
||||
for property in properties {
|
||||
let name = unsafe { CStr::from_ptr(property.extension_name.as_ptr()) };
|
||||
impl DeviceExtensions {
|
||||
/// Checks enabled extensions against the device version, instance extensions and each other.
|
||||
pub(super) fn check_requirements(
|
||||
&self,
|
||||
supported: &DeviceExtensions,
|
||||
api_version: crate::Version,
|
||||
instance_extensions: &InstanceExtensions,
|
||||
) -> Result<(), crate::extensions::ExtensionRestrictionError> {
|
||||
$(
|
||||
// TODO: Check specVersion?
|
||||
if name.to_bytes() == &$s[..] {
|
||||
extensions.$ext = true;
|
||||
if self.$member {
|
||||
if !supported.$member {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::NotSupported,
|
||||
});
|
||||
}
|
||||
|
||||
if api_version < $requires_core {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::RequiresCore($requires_core),
|
||||
});
|
||||
}
|
||||
|
||||
$(
|
||||
if !self.$requires_device_extension {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::RequiresDeviceExtension(stringify!($requires_device_extension)),
|
||||
});
|
||||
}
|
||||
)*
|
||||
|
||||
$(
|
||||
if !instance_extensions.$requires_instance_extension {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::RequiresInstanceExtension(stringify!($requires_instance_extension)),
|
||||
});
|
||||
}
|
||||
)*
|
||||
|
||||
$(
|
||||
if self.$conflicts_device_extension {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::ConflictsDeviceExtension(stringify!($conflicts_device_extension)),
|
||||
});
|
||||
}
|
||||
)*
|
||||
} else {
|
||||
if $required_if_supported && supported.$member {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::RequiredIfSupported,
|
||||
});
|
||||
}
|
||||
}
|
||||
)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(extensions)
|
||||
pub(crate) fn required_if_supported_extensions() -> Self {
|
||||
Self {
|
||||
$(
|
||||
$member: $required_if_supported,
|
||||
)*
|
||||
_unbuildable: crate::extensions::Unbuildable(())
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
pub use crate::autogen::DeviceExtensions;
|
||||
pub(crate) use device_extensions;
|
||||
|
||||
impl DeviceExtensions {
|
||||
/// See the docs of supported_by_device().
|
||||
pub fn supported_by_device_raw(
|
||||
physical_device: PhysicalDevice,
|
||||
) -> Result<Self, SupportedExtensionsError> {
|
||||
let fns = physical_device.instance().fns();
|
||||
|
||||
let properties: Vec<ash::vk::ExtensionProperties> = unsafe {
|
||||
let mut num = 0;
|
||||
check_errors(fns.v1_0.enumerate_device_extension_properties(
|
||||
physical_device.internal_object(),
|
||||
ptr::null(),
|
||||
&mut num,
|
||||
ptr::null_mut(),
|
||||
))?;
|
||||
|
||||
let mut properties = Vec::with_capacity(num as usize);
|
||||
check_errors(fns.v1_0.enumerate_device_extension_properties(
|
||||
physical_device.internal_object(),
|
||||
ptr::null(),
|
||||
&mut num,
|
||||
properties.as_mut_ptr(),
|
||||
))?;
|
||||
properties.set_len(num as usize);
|
||||
properties
|
||||
};
|
||||
|
||||
Ok(Self::from(properties.iter().map(|property| unsafe {
|
||||
CStr::from_ptr(property.extension_name.as_ptr())
|
||||
})))
|
||||
}
|
||||
|
||||
/// Returns an `Extensions` object with extensions supported by the `PhysicalDevice`.
|
||||
/// Returns a `DeviceExtensions` object with extensions supported by the `PhysicalDevice`.
|
||||
pub fn supported_by_device(physical_device: PhysicalDevice) -> Self {
|
||||
match $sname::supported_by_device_raw(physical_device) {
|
||||
match DeviceExtensions::supported_by_device_raw(physical_device) {
|
||||
Ok(l) => l,
|
||||
Err(SupportedExtensionsError::LoadingError(_)) => unreachable!(),
|
||||
Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an `Extensions` object with extensions required as well as supported by the `PhysicalDevice`.
|
||||
/// Returns a `DeviceExtensions` object with extensions required as well as supported by the `PhysicalDevice`.
|
||||
/// They are needed to be passed to `Device::new(...)`.
|
||||
pub fn required_extensions(physical_device: PhysicalDevice) -> Self {
|
||||
let supported = Self::supported_by_device(physical_device);
|
||||
@ -107,46 +165,6 @@ macro_rules! device_extensions {
|
||||
|
||||
required_if_supported.intersection(&supported)
|
||||
}
|
||||
|
||||
fn required_if_supported_extensions() -> Self {
|
||||
Self {
|
||||
$(
|
||||
$ext_req_if_supported: true,
|
||||
)*
|
||||
..Self::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
device_extensions! {
|
||||
DeviceExtensions,
|
||||
RawDeviceExtensions,
|
||||
[ // required if supported extensions
|
||||
// https://vulkan.lunarg.com/doc/view/1.2.162.1/mac/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pProperties-04451
|
||||
khr_portability_subset,
|
||||
],
|
||||
|
||||
// List in order: khr, ext, then alphabetical
|
||||
khr_16bit_storage => b"VK_KHR_16bit_storage",
|
||||
khr_8bit_storage => b"VK_KHR_8bit_storage",
|
||||
khr_dedicated_allocation => b"VK_KHR_dedicated_allocation",
|
||||
khr_display_swapchain => b"VK_KHR_display_swapchain",
|
||||
khr_external_memory => b"VK_KHR_external_memory",
|
||||
khr_external_memory_fd => b"VK_KHR_external_memory_fd",
|
||||
khr_get_memory_requirements2 => b"VK_KHR_get_memory_requirements2",
|
||||
khr_incremental_present => b"VK_KHR_incremental_present",
|
||||
khr_maintenance1 => b"VK_KHR_maintenance1",
|
||||
khr_multiview => b"VK_KHR_multiview",
|
||||
khr_portability_subset => b"VK_KHR_portability_subset",
|
||||
khr_sampler_mirror_clamp_to_edge => b"VK_KHR_sampler_mirror_clamp_to_edge",
|
||||
khr_spirv_1_4 => b"VK_KHR_spirv_1_4",
|
||||
khr_storage_buffer_storage_class => b"VK_KHR_storage_buffer_storage_class",
|
||||
khr_swapchain => b"VK_KHR_swapchain",
|
||||
ext_debug_utils => b"VK_EXT_debug_utils",
|
||||
ext_external_memory_dmabuf => b"VK_EXT_external_memory_dma_buf",
|
||||
ext_full_screen_exclusive => b"VK_EXT_full_screen_exclusive",
|
||||
}
|
||||
|
||||
/// This helper type can only be instantiated inside this module.
|
||||
@ -157,11 +175,12 @@ pub struct Unbuildable(());
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::device::{DeviceExtensions, RawDeviceExtensions};
|
||||
use crate::device::DeviceExtensions;
|
||||
use std::ffi::CString;
|
||||
|
||||
#[test]
|
||||
fn empty_extensions() {
|
||||
let d: RawDeviceExtensions = (&DeviceExtensions::none()).into();
|
||||
let d: Vec<CString> = (&DeviceExtensions::none()).into();
|
||||
assert!(d.iter().next().is_none());
|
||||
}
|
||||
|
||||
|
@ -7,13 +7,19 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use crate::Version;
|
||||
use std::marker::PhantomPinned;
|
||||
use std::ptr::addr_of_mut;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
macro_rules! features {
|
||||
{
|
||||
$({ $member:ident => $($ffi_struct:ident.$($ffi_field:ident).+)|+ },)*
|
||||
$($member:ident => {
|
||||
doc: $doc:expr,
|
||||
ffi_name: $ffi_field:ident,
|
||||
ffi_members: [$($ffi_struct:ident $(.$ffi_struct_field:ident)?),+],
|
||||
requires_features: [$($requires_feature:ident),*],
|
||||
conflicts_features: [$($conflicts_feature:ident),*],
|
||||
required_by_extensions: [$($required_by_extension:ident),*],
|
||||
},)*
|
||||
} => {
|
||||
/// Represents all the features that are available on a physical device or enabled on
|
||||
/// a logical device.
|
||||
@ -48,10 +54,61 @@ macro_rules! features {
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Features {
|
||||
$(pub $member: bool,)*
|
||||
$(
|
||||
#[doc = $doc]
|
||||
pub $member: bool,
|
||||
)*
|
||||
}
|
||||
|
||||
impl Features {
|
||||
/// Checks enabled features against the device version, device extensions and each other.
|
||||
pub(super) fn check_requirements(
|
||||
&self,
|
||||
supported: &Features,
|
||||
api_version:
|
||||
crate::Version,
|
||||
extensions: &crate::device::DeviceExtensions,
|
||||
) -> Result<(), crate::device::features::FeatureRestrictionError> {
|
||||
$(
|
||||
if self.$member {
|
||||
if !supported.$member {
|
||||
return Err(crate::device::features::FeatureRestrictionError {
|
||||
feature: stringify!($member),
|
||||
restriction: crate::device::features::FeatureRestriction::NotSupported,
|
||||
});
|
||||
}
|
||||
|
||||
$(
|
||||
if !self.$requires_feature {
|
||||
return Err(crate::device::features::FeatureRestrictionError {
|
||||
feature: stringify!($member),
|
||||
restriction: crate::device::features::FeatureRestriction::RequiresFeature(stringify!($requires_feature)),
|
||||
});
|
||||
}
|
||||
)*
|
||||
|
||||
$(
|
||||
if self.$conflicts_feature {
|
||||
return Err(crate::device::features::FeatureRestrictionError {
|
||||
feature: stringify!($member),
|
||||
restriction: crate::device::features::FeatureRestriction::ConflictsFeature(stringify!($conflicts_feature)),
|
||||
});
|
||||
}
|
||||
)*
|
||||
} else {
|
||||
$(
|
||||
if extensions.$required_by_extension {
|
||||
return Err(crate::device::features::FeatureRestrictionError {
|
||||
feature: stringify!($member),
|
||||
restriction: crate::device::features::FeatureRestriction::RequiredByExtension(stringify!($required_by_extension)),
|
||||
});
|
||||
}
|
||||
)*
|
||||
}
|
||||
)*
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Builds a `Features` object with all values to false.
|
||||
pub fn none() -> Features {
|
||||
Features {
|
||||
@ -97,235 +154,128 @@ macro_rules! features {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Features> for FeaturesFfi {
|
||||
#[inline(always)]
|
||||
fn from(features: &Features) -> Self {
|
||||
let mut features_ffi = FeaturesFfi::default();
|
||||
impl FeaturesFfi {
|
||||
pub(crate) fn write(&mut self, features: &Features) {
|
||||
$(
|
||||
$(features_ffi.$ffi_struct.$($ffi_field).+ |= features.$member as ash::vk::Bool32;)*
|
||||
)+
|
||||
features_ffi
|
||||
std::array::IntoIter::new([
|
||||
$(self.$ffi_struct.as_mut().map(|s| &mut s$(.$ffi_struct_field)?.$ffi_field)),+
|
||||
]).flatten().next().map(|f| *f = features.$member as ash::vk::Bool32);
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&FeaturesFfi> for Features {
|
||||
#[inline(always)]
|
||||
fn from(features_ffi: &FeaturesFfi) -> Self {
|
||||
Features {
|
||||
$($member: $(features_ffi.$ffi_struct.$($ffi_field).+ != 0)||+,)*
|
||||
$(
|
||||
$member: std::array::IntoIter::new([
|
||||
$(features_ffi.$ffi_struct.map(|s| s$(.$ffi_struct_field)?.$ffi_field)),+
|
||||
]).flatten().next().unwrap_or(0) != 0,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
features! {
|
||||
// Vulkan 1.0
|
||||
{robust_buffer_access => vulkan_1_0.features.robust_buffer_access},
|
||||
{full_draw_index_uint32 => vulkan_1_0.features.full_draw_index_uint32},
|
||||
{image_cube_array => vulkan_1_0.features.image_cube_array},
|
||||
{independent_blend => vulkan_1_0.features.independent_blend},
|
||||
{geometry_shader => vulkan_1_0.features.geometry_shader},
|
||||
{tessellation_shader => vulkan_1_0.features.tessellation_shader},
|
||||
{sample_rate_shading => vulkan_1_0.features.sample_rate_shading},
|
||||
{dual_src_blend => vulkan_1_0.features.dual_src_blend},
|
||||
{logic_op => vulkan_1_0.features.logic_op},
|
||||
{multi_draw_indirect => vulkan_1_0.features.multi_draw_indirect},
|
||||
{draw_indirect_first_instance => vulkan_1_0.features.draw_indirect_first_instance},
|
||||
{depth_clamp => vulkan_1_0.features.depth_clamp},
|
||||
{depth_bias_clamp => vulkan_1_0.features.depth_bias_clamp},
|
||||
{fill_mode_non_solid => vulkan_1_0.features.fill_mode_non_solid},
|
||||
{depth_bounds => vulkan_1_0.features.depth_bounds},
|
||||
{wide_lines => vulkan_1_0.features.wide_lines},
|
||||
{large_points => vulkan_1_0.features.large_points},
|
||||
{alpha_to_one => vulkan_1_0.features.alpha_to_one},
|
||||
{multi_viewport => vulkan_1_0.features.multi_viewport},
|
||||
{sampler_anisotropy => vulkan_1_0.features.sampler_anisotropy},
|
||||
{texture_compression_etc2 => vulkan_1_0.features.texture_compression_etc2},
|
||||
{texture_compression_astc_ldr => vulkan_1_0.features.texture_compression_astc_ldr},
|
||||
{texture_compression_bc => vulkan_1_0.features.texture_compression_bc},
|
||||
{occlusion_query_precise => vulkan_1_0.features.occlusion_query_precise},
|
||||
{pipeline_statistics_query => vulkan_1_0.features.pipeline_statistics_query},
|
||||
{vertex_pipeline_stores_and_atomics => vulkan_1_0.features.vertex_pipeline_stores_and_atomics},
|
||||
{fragment_stores_and_atomics => vulkan_1_0.features.fragment_stores_and_atomics},
|
||||
{shader_tessellation_and_geometry_point_size => vulkan_1_0.features.shader_tessellation_and_geometry_point_size},
|
||||
{shader_image_gather_extended => vulkan_1_0.features.shader_image_gather_extended},
|
||||
{shader_storage_image_extended_formats => vulkan_1_0.features.shader_storage_image_extended_formats},
|
||||
{shader_storage_image_multisample => vulkan_1_0.features.shader_storage_image_multisample},
|
||||
{shader_storage_image_read_without_format => vulkan_1_0.features.shader_storage_image_read_without_format},
|
||||
{shader_storage_image_write_without_format => vulkan_1_0.features.shader_storage_image_write_without_format},
|
||||
{shader_uniform_buffer_array_dynamic_indexing => vulkan_1_0.features.shader_uniform_buffer_array_dynamic_indexing},
|
||||
{shader_sampled_image_array_dynamic_indexing => vulkan_1_0.features.shader_sampled_image_array_dynamic_indexing},
|
||||
{shader_storage_buffer_array_dynamic_indexing => vulkan_1_0.features.shader_storage_buffer_array_dynamic_indexing},
|
||||
{shader_storage_image_array_dynamic_indexing => vulkan_1_0.features.shader_storage_image_array_dynamic_indexing},
|
||||
{shader_clip_distance => vulkan_1_0.features.shader_clip_distance},
|
||||
{shader_cull_distance => vulkan_1_0.features.shader_cull_distance},
|
||||
{shader_float64 => vulkan_1_0.features.shader_float64},
|
||||
{shader_int64 => vulkan_1_0.features.shader_int64},
|
||||
{shader_int16 => vulkan_1_0.features.shader_int16},
|
||||
{shader_resource_residency => vulkan_1_0.features.shader_resource_residency},
|
||||
{shader_resource_min_lod => vulkan_1_0.features.shader_resource_min_lod},
|
||||
{sparse_binding => vulkan_1_0.features.sparse_binding},
|
||||
{sparse_residency_buffer => vulkan_1_0.features.sparse_residency_buffer},
|
||||
{sparse_residency_image2d => vulkan_1_0.features.sparse_residency_image2_d},
|
||||
{sparse_residency_image3d => vulkan_1_0.features.sparse_residency_image3_d},
|
||||
{sparse_residency2_samples => vulkan_1_0.features.sparse_residency2_samples},
|
||||
{sparse_residency4_samples => vulkan_1_0.features.sparse_residency4_samples},
|
||||
{sparse_residency8_samples => vulkan_1_0.features.sparse_residency8_samples},
|
||||
{sparse_residency16_samples => vulkan_1_0.features.sparse_residency16_samples},
|
||||
{sparse_residency_aliased => vulkan_1_0.features.sparse_residency_aliased},
|
||||
{variable_multisample_rate => vulkan_1_0.features.variable_multisample_rate},
|
||||
{inherited_queries => vulkan_1_0.features.inherited_queries},
|
||||
pub use crate::autogen::Features;
|
||||
pub(crate) use {crate::autogen::FeaturesFfi, features};
|
||||
|
||||
// Vulkan 1.1
|
||||
{storage_buffer_16bit => vulkan_1_1.storage_buffer16_bit_access | khr_16bit_storage.storage_buffer16_bit_access},
|
||||
{storage_uniform_16bit => vulkan_1_1.uniform_and_storage_buffer16_bit_access | khr_16bit_storage.uniform_and_storage_buffer16_bit_access},
|
||||
{storage_push_constant_16bit => vulkan_1_1.storage_push_constant16 | khr_16bit_storage.storage_push_constant16},
|
||||
{storage_input_output_16bit => vulkan_1_1.storage_input_output16 | khr_16bit_storage.storage_input_output16},
|
||||
{multiview => vulkan_1_1.multiview | khr_multiview.multiview},
|
||||
{multiview_geometry_shader => vulkan_1_1.multiview_geometry_shader | khr_multiview.multiview_geometry_shader},
|
||||
{multiview_tessellation_shader => vulkan_1_1.multiview_tessellation_shader | khr_multiview.multiview_tessellation_shader},
|
||||
{variable_pointers_storage_buffer => vulkan_1_1.variable_pointers_storage_buffer | khr_variable_pointers.variable_pointers_storage_buffer},
|
||||
{variable_pointers => vulkan_1_1.variable_pointers | khr_variable_pointers.variable_pointers},
|
||||
{protected_memory => vulkan_1_1.protected_memory | protected_memory.protected_memory},
|
||||
{sampler_ycbcr_conversion => vulkan_1_1.sampler_ycbcr_conversion | khr_sampler_ycbcr_conversion.sampler_ycbcr_conversion},
|
||||
{shader_draw_parameters => vulkan_1_1.shader_draw_parameters | shader_draw_parameters.shader_draw_parameters},
|
||||
|
||||
// Vulkan 1.2
|
||||
{sampler_mirror_clamp_to_edge => vulkan_1_2.sampler_mirror_clamp_to_edge},
|
||||
{draw_indirect_count => vulkan_1_2.draw_indirect_count},
|
||||
{storage_buffer_8bit => vulkan_1_2.storage_buffer8_bit_access | khr_8bit_storage.storage_buffer8_bit_access},
|
||||
{storage_uniform_8bit => vulkan_1_2.uniform_and_storage_buffer8_bit_access | khr_8bit_storage.uniform_and_storage_buffer8_bit_access},
|
||||
{storage_push_constant_8bit => vulkan_1_2.storage_push_constant8 | khr_8bit_storage.storage_push_constant8},
|
||||
{shader_buffer_int64_atomics => vulkan_1_2.shader_buffer_int64_atomics | khr_shader_atomic_int64.shader_buffer_int64_atomics},
|
||||
{shader_shared_int64_atomics => vulkan_1_2.shader_shared_int64_atomics | khr_shader_atomic_int64.shader_shared_int64_atomics},
|
||||
{shader_float16 => vulkan_1_2.shader_float16 | khr_shader_float16_int8.shader_float16},
|
||||
{shader_int8 => vulkan_1_2.shader_int8 | khr_shader_float16_int8.shader_int8},
|
||||
{descriptor_indexing => vulkan_1_2.descriptor_indexing},
|
||||
{shader_input_attachment_array_dynamic_indexing => vulkan_1_2.shader_input_attachment_array_dynamic_indexing | ext_descriptor_indexing.shader_input_attachment_array_dynamic_indexing},
|
||||
{shader_uniform_texel_buffer_array_dynamic_indexing => vulkan_1_2.shader_uniform_texel_buffer_array_dynamic_indexing | ext_descriptor_indexing.shader_uniform_texel_buffer_array_dynamic_indexing},
|
||||
{shader_storage_texel_buffer_array_dynamic_indexing => vulkan_1_2.shader_storage_texel_buffer_array_dynamic_indexing | ext_descriptor_indexing.shader_storage_texel_buffer_array_dynamic_indexing},
|
||||
{shader_uniform_buffer_array_non_uniform_indexing => vulkan_1_2.shader_uniform_buffer_array_non_uniform_indexing | ext_descriptor_indexing.shader_uniform_buffer_array_non_uniform_indexing},
|
||||
{shader_sampled_image_array_non_uniform_indexing => vulkan_1_2.shader_sampled_image_array_non_uniform_indexing | ext_descriptor_indexing.shader_sampled_image_array_non_uniform_indexing},
|
||||
{shader_storage_buffer_array_non_uniform_indexing => vulkan_1_2.shader_storage_buffer_array_non_uniform_indexing | ext_descriptor_indexing.shader_storage_buffer_array_non_uniform_indexing},
|
||||
{shader_storage_image_array_non_uniform_indexing => vulkan_1_2.shader_storage_image_array_non_uniform_indexing | ext_descriptor_indexing.shader_storage_image_array_non_uniform_indexing},
|
||||
{shader_input_attachment_array_non_uniform_indexing => vulkan_1_2.shader_input_attachment_array_non_uniform_indexing | ext_descriptor_indexing.shader_input_attachment_array_non_uniform_indexing},
|
||||
{shader_uniform_texel_buffer_array_non_uniform_indexing => vulkan_1_2.shader_uniform_texel_buffer_array_non_uniform_indexing | ext_descriptor_indexing.shader_uniform_texel_buffer_array_non_uniform_indexing},
|
||||
{shader_storage_texel_buffer_array_non_uniform_indexing => vulkan_1_2.shader_storage_texel_buffer_array_non_uniform_indexing | ext_descriptor_indexing.shader_storage_texel_buffer_array_non_uniform_indexing},
|
||||
{descriptor_binding_uniform_buffer_update_after_bind => vulkan_1_2.descriptor_binding_uniform_buffer_update_after_bind | ext_descriptor_indexing.descriptor_binding_uniform_buffer_update_after_bind},
|
||||
{descriptor_binding_sampled_image_update_after_bind => vulkan_1_2.descriptor_binding_sampled_image_update_after_bind | ext_descriptor_indexing.descriptor_binding_sampled_image_update_after_bind},
|
||||
{descriptor_binding_storage_image_update_after_bind => vulkan_1_2.descriptor_binding_storage_image_update_after_bind | ext_descriptor_indexing.descriptor_binding_storage_image_update_after_bind},
|
||||
{descriptor_binding_storage_buffer_update_after_bind => vulkan_1_2.descriptor_binding_storage_buffer_update_after_bind | ext_descriptor_indexing.descriptor_binding_storage_buffer_update_after_bind},
|
||||
{descriptor_binding_uniform_texel_buffer_update_after_bind => vulkan_1_2.descriptor_binding_uniform_texel_buffer_update_after_bind | ext_descriptor_indexing.descriptor_binding_uniform_texel_buffer_update_after_bind},
|
||||
{descriptor_binding_storage_texel_buffer_update_after_bind => vulkan_1_2.descriptor_binding_storage_texel_buffer_update_after_bind | ext_descriptor_indexing.descriptor_binding_storage_texel_buffer_update_after_bind},
|
||||
{descriptor_binding_update_unused_while_pending => vulkan_1_2.descriptor_binding_update_unused_while_pending | ext_descriptor_indexing.descriptor_binding_update_unused_while_pending},
|
||||
{descriptor_binding_partially_bound => vulkan_1_2.descriptor_binding_partially_bound | ext_descriptor_indexing.descriptor_binding_partially_bound},
|
||||
{descriptor_binding_variable_descriptor_count => vulkan_1_2.descriptor_binding_variable_descriptor_count | ext_descriptor_indexing.descriptor_binding_variable_descriptor_count},
|
||||
{runtime_descriptor_array => vulkan_1_2.runtime_descriptor_array | ext_descriptor_indexing.runtime_descriptor_array},
|
||||
{sampler_filter_minmax => vulkan_1_2.sampler_filter_minmax},
|
||||
{scalar_block_layout => vulkan_1_2.scalar_block_layout | ext_scalar_block_layout.scalar_block_layout},
|
||||
{imageless_framebuffer => vulkan_1_2.imageless_framebuffer | khr_imageless_framebuffer.imageless_framebuffer},
|
||||
{uniform_buffer_standard_layout => vulkan_1_2.uniform_buffer_standard_layout | khr_uniform_buffer_standard_layout.uniform_buffer_standard_layout},
|
||||
{shader_subgroup_extended_types => vulkan_1_2.shader_subgroup_extended_types | khr_shader_subgroup_extended_types.shader_subgroup_extended_types},
|
||||
{separate_depth_stencil_layouts => vulkan_1_2.separate_depth_stencil_layouts | khr_separate_depth_stencil_layouts.separate_depth_stencil_layouts},
|
||||
{host_query_reset => vulkan_1_2.host_query_reset | ext_host_query_reset.host_query_reset},
|
||||
{timeline_semaphore => vulkan_1_2.timeline_semaphore | khr_timeline_semaphore.timeline_semaphore},
|
||||
{buffer_device_address => vulkan_1_2.buffer_device_address | khr_buffer_device_address.buffer_device_address},
|
||||
{buffer_device_address_capture_replay => vulkan_1_2.buffer_device_address_capture_replay | khr_buffer_device_address.buffer_device_address_capture_replay},
|
||||
{buffer_device_address_multi_device => vulkan_1_2.buffer_device_address_multi_device | khr_buffer_device_address.buffer_device_address_multi_device},
|
||||
{vulkan_memory_model => vulkan_1_2.vulkan_memory_model | khr_vulkan_memory_model.vulkan_memory_model},
|
||||
{vulkan_memory_model_device_scope => vulkan_1_2.vulkan_memory_model_device_scope | khr_vulkan_memory_model.vulkan_memory_model_device_scope},
|
||||
{vulkan_memory_model_availability_visibility_chains => vulkan_1_2.vulkan_memory_model_availability_visibility_chains | khr_vulkan_memory_model.vulkan_memory_model_availability_visibility_chains},
|
||||
{shader_output_viewport_index => vulkan_1_2.shader_output_viewport_index},
|
||||
{shader_output_layer => vulkan_1_2.shader_output_layer},
|
||||
{subgroup_broadcast_dynamic_id => vulkan_1_2.subgroup_broadcast_dynamic_id},
|
||||
|
||||
// Extensions
|
||||
{ext_buffer_device_address => ext_buffer_address.buffer_device_address},
|
||||
{ext_buffer_device_address_capture_replay => ext_buffer_address.buffer_device_address_capture_replay},
|
||||
{ext_buffer_device_address_multi_device => ext_buffer_address.buffer_device_address_multi_device},
|
||||
/// An error that can happen when enabling a feature on a device.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct FeatureRestrictionError {
|
||||
/// The feature in question.
|
||||
pub feature: &'static str,
|
||||
/// The restriction that was not met.
|
||||
pub restriction: FeatureRestriction,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct FeaturesFfi {
|
||||
_pinned: PhantomPinned,
|
||||
impl error::Error for FeatureRestrictionError {}
|
||||
|
||||
vulkan_1_0: ash::vk::PhysicalDeviceFeatures2KHR,
|
||||
vulkan_1_1: ash::vk::PhysicalDeviceVulkan11Features,
|
||||
vulkan_1_2: ash::vk::PhysicalDeviceVulkan12Features,
|
||||
|
||||
protected_memory: ash::vk::PhysicalDeviceProtectedMemoryFeatures,
|
||||
shader_draw_parameters: ash::vk::PhysicalDeviceShaderDrawParametersFeatures,
|
||||
|
||||
khr_16bit_storage: ash::vk::PhysicalDevice16BitStorageFeaturesKHR,
|
||||
khr_8bit_storage: ash::vk::PhysicalDevice8BitStorageFeaturesKHR,
|
||||
khr_buffer_device_address: ash::vk::PhysicalDeviceBufferDeviceAddressFeaturesKHR,
|
||||
khr_imageless_framebuffer: ash::vk::PhysicalDeviceImagelessFramebufferFeaturesKHR,
|
||||
khr_multiview: ash::vk::PhysicalDeviceMultiviewFeaturesKHR,
|
||||
khr_sampler_ycbcr_conversion: ash::vk::PhysicalDeviceSamplerYcbcrConversionFeaturesKHR,
|
||||
khr_separate_depth_stencil_layouts:
|
||||
ash::vk::PhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR,
|
||||
khr_shader_atomic_int64: ash::vk::PhysicalDeviceShaderAtomicInt64FeaturesKHR,
|
||||
khr_shader_float16_int8: ash::vk::PhysicalDeviceShaderFloat16Int8FeaturesKHR,
|
||||
khr_shader_subgroup_extended_types:
|
||||
ash::vk::PhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR,
|
||||
khr_timeline_semaphore: ash::vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR,
|
||||
khr_uniform_buffer_standard_layout:
|
||||
ash::vk::PhysicalDeviceUniformBufferStandardLayoutFeaturesKHR,
|
||||
khr_variable_pointers: ash::vk::PhysicalDeviceVariablePointersFeaturesKHR,
|
||||
khr_vulkan_memory_model: ash::vk::PhysicalDeviceVulkanMemoryModelFeaturesKHR,
|
||||
|
||||
ext_buffer_address: ash::vk::PhysicalDeviceBufferAddressFeaturesEXT,
|
||||
ext_descriptor_indexing: ash::vk::PhysicalDeviceDescriptorIndexingFeaturesEXT,
|
||||
ext_host_query_reset: ash::vk::PhysicalDeviceHostQueryResetFeaturesEXT,
|
||||
ext_scalar_block_layout: ash::vk::PhysicalDeviceScalarBlockLayoutFeaturesEXT,
|
||||
impl fmt::Display for FeatureRestrictionError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"a restriction for the feature {} was not met: {}",
|
||||
self.feature, self.restriction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! push_struct {
|
||||
($self:ident, $struct:ident) => {
|
||||
$self.$struct.p_next = $self.vulkan_1_0.p_next;
|
||||
$self.vulkan_1_0.p_next = addr_of_mut!($self.$struct) as _;
|
||||
};
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum FeatureRestriction {
|
||||
/// Not supported by the physical device.
|
||||
NotSupported,
|
||||
/// Requires a feature to be enabled.
|
||||
RequiresFeature(&'static str),
|
||||
/// Requires a feature to be disabled.
|
||||
ConflictsFeature(&'static str),
|
||||
/// An extension requires this feature to be enabled.
|
||||
RequiredByExtension(&'static str),
|
||||
}
|
||||
|
||||
impl FeaturesFfi {
|
||||
pub(crate) fn make_chain(&mut self, api_version: Version) {
|
||||
if api_version >= Version::V1_2 {
|
||||
push_struct!(self, vulkan_1_1);
|
||||
push_struct!(self, vulkan_1_2);
|
||||
} else {
|
||||
if api_version >= Version::V1_1 {
|
||||
push_struct!(self, protected_memory);
|
||||
push_struct!(self, shader_draw_parameters);
|
||||
impl fmt::Display for FeatureRestriction {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FeatureRestriction::NotSupported => {
|
||||
write!(fmt, "not supported by the physical device")
|
||||
}
|
||||
FeatureRestriction::RequiresFeature(feat) => {
|
||||
write!(fmt, "requires feature {} to be enabled", feat)
|
||||
}
|
||||
FeatureRestriction::ConflictsFeature(feat) => {
|
||||
write!(fmt, "requires feature {} to be disabled", feat)
|
||||
}
|
||||
FeatureRestriction::RequiredByExtension(ext) => {
|
||||
write!(fmt, "required to be enabled by extension {}", ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! features_ffi {
|
||||
{
|
||||
$api_version:ident,
|
||||
$extensions:ident,
|
||||
$($member:ident => {
|
||||
ty: $ty:ident,
|
||||
provided_by: [$($provided_by:expr),+],
|
||||
conflicts: [$($conflicts:ident),*],
|
||||
},)+
|
||||
} => {
|
||||
#[derive(Default)]
|
||||
pub(crate) struct FeaturesFfi {
|
||||
features_vulkan10: Option<ash::vk::PhysicalDeviceFeatures2KHR>,
|
||||
|
||||
$(
|
||||
$member: Option<ash::vk::$ty>,
|
||||
)+
|
||||
}
|
||||
|
||||
push_struct!(self, khr_16bit_storage);
|
||||
push_struct!(self, khr_8bit_storage);
|
||||
push_struct!(self, khr_buffer_device_address);
|
||||
push_struct!(self, khr_imageless_framebuffer);
|
||||
push_struct!(self, khr_multiview);
|
||||
push_struct!(self, khr_sampler_ycbcr_conversion);
|
||||
push_struct!(self, khr_separate_depth_stencil_layouts);
|
||||
push_struct!(self, khr_shader_atomic_int64);
|
||||
push_struct!(self, khr_shader_float16_int8);
|
||||
push_struct!(self, khr_shader_subgroup_extended_types);
|
||||
push_struct!(self, khr_timeline_semaphore);
|
||||
push_struct!(self, khr_uniform_buffer_standard_layout);
|
||||
push_struct!(self, khr_variable_pointers);
|
||||
push_struct!(self, khr_vulkan_memory_model);
|
||||
push_struct!(self, ext_descriptor_indexing);
|
||||
push_struct!(self, ext_host_query_reset);
|
||||
push_struct!(self, ext_scalar_block_layout);
|
||||
impl FeaturesFfi {
|
||||
pub(crate) fn make_chain(&mut self, $api_version: crate::Version, $extensions: &DeviceExtensions) {
|
||||
self.features_vulkan10 = Some(Default::default());
|
||||
$(
|
||||
if std::array::IntoIter::new([$($provided_by),+]).any(|x| x) &&
|
||||
std::array::IntoIter::new([$(self.$conflicts.is_none()),*]).all(|x| x) {
|
||||
self.$member = Some(Default::default());
|
||||
self.$member.unwrap().p_next = self.features_vulkan10.unwrap().p_next;
|
||||
self.features_vulkan10.unwrap().p_next = self.$member.as_mut().unwrap() as *mut _ as _;
|
||||
}
|
||||
|
||||
push_struct!(self, ext_buffer_address);
|
||||
)+
|
||||
}
|
||||
|
||||
pub(crate) fn head_as_ref(&self) -> &ash::vk::PhysicalDeviceFeatures2KHR {
|
||||
&self.vulkan_1_0
|
||||
self.features_vulkan10.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn head_as_mut(&mut self) -> &mut ash::vk::PhysicalDeviceFeatures2KHR {
|
||||
&mut self.vulkan_1_0
|
||||
self.features_vulkan10.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use features_ffi;
|
||||
|
@ -90,13 +90,15 @@
|
||||
//!
|
||||
//! TODO: write
|
||||
|
||||
pub use self::extensions::DeviceExtensions;
|
||||
pub use self::extensions::RawDeviceExtensions;
|
||||
pub use self::features::Features;
|
||||
pub(crate) use self::features::FeaturesFfi;
|
||||
pub use self::features::{FeatureRestriction, FeatureRestrictionError, Features};
|
||||
pub use crate::autogen::DeviceExtensions;
|
||||
use crate::check_errors;
|
||||
use crate::command_buffer::pool::StandardCommandPool;
|
||||
use crate::descriptor::descriptor_set::StdDescriptorPool;
|
||||
pub use crate::extensions::{
|
||||
ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError,
|
||||
};
|
||||
use crate::fns::DeviceFunctions;
|
||||
use crate::format::Format;
|
||||
use crate::image::ImageCreateFlags;
|
||||
@ -120,6 +122,7 @@ use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
use std::error;
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
use std::hash::BuildHasherDefault;
|
||||
use std::hash::Hash;
|
||||
@ -133,8 +136,8 @@ use std::sync::Mutex;
|
||||
use std::sync::MutexGuard;
|
||||
use std::sync::Weak;
|
||||
|
||||
mod extensions;
|
||||
mod features;
|
||||
pub(crate) mod extensions;
|
||||
pub(crate) mod features;
|
||||
|
||||
/// Represents a Vulkan context.
|
||||
pub struct Device {
|
||||
@ -186,68 +189,70 @@ impl Device {
|
||||
///
|
||||
// TODO: return Arc<Queue> and handle synchronization in the Queue
|
||||
// TODO: should take the PhysicalDevice by value
|
||||
pub fn new<'a, I, Ext>(
|
||||
phys: PhysicalDevice,
|
||||
pub fn new<'a, I>(
|
||||
physical_device: PhysicalDevice,
|
||||
requested_features: &Features,
|
||||
extensions: Ext,
|
||||
requested_extensions: &DeviceExtensions,
|
||||
queue_families: I,
|
||||
) -> Result<(Arc<Device>, QueuesIter), DeviceCreationError>
|
||||
where
|
||||
I: IntoIterator<Item = (QueueFamily<'a>, f32)>,
|
||||
Ext: Into<RawDeviceExtensions>,
|
||||
{
|
||||
let instance = phys.instance();
|
||||
let instance = physical_device.instance();
|
||||
let fns_i = instance.fns();
|
||||
|
||||
let max_api_version = instance.max_api_version();
|
||||
let api_version = std::cmp::min(max_api_version, phys.api_version());
|
||||
let api_version = std::cmp::min(max_api_version, physical_device.api_version());
|
||||
|
||||
let queue_families = queue_families.into_iter();
|
||||
// Check if the extensions are correct
|
||||
requested_extensions.check_requirements(
|
||||
&DeviceExtensions::supported_by_device(physical_device),
|
||||
api_version,
|
||||
instance.loaded_extensions(),
|
||||
)?;
|
||||
|
||||
if !phys.supported_features().superset_of(&requested_features) {
|
||||
return Err(DeviceCreationError::FeatureNotPresent);
|
||||
}
|
||||
let mut requested_features = requested_features.clone();
|
||||
|
||||
// TODO: The plan regarding `robust_buffer_access` is to check the shaders' code to see
|
||||
// if they can possibly perform out-of-bounds reads and writes. If the user tries
|
||||
// to use a shader that can perform out-of-bounds operations without having
|
||||
// `robust_buffer_access` enabled, an error is returned.
|
||||
//
|
||||
// However for the moment this verification isn't performed. In order to be safe,
|
||||
// we always enable the `robust_buffer_access` feature as it is guaranteed to be
|
||||
// supported everywhere.
|
||||
//
|
||||
// The only alternative (while waiting for shaders introspection to work) is to
|
||||
// make all shaders depend on `robust_buffer_access`. But since usually the
|
||||
// majority of shaders don't need this feature, it would be very annoying to have
|
||||
// to enable it manually when you don't need it.
|
||||
//
|
||||
// Note that if we ever remove this, don't forget to adjust the change in
|
||||
// `Device`'s construction below.
|
||||
requested_features.robust_buffer_access = true;
|
||||
|
||||
// Check if the features are correct
|
||||
requested_features.check_requirements(
|
||||
physical_device.supported_features(),
|
||||
api_version,
|
||||
requested_extensions,
|
||||
)?;
|
||||
|
||||
// device creation
|
||||
let (device, queues) = unsafe {
|
||||
// each element of `queues` is a `(queue_family, priorities)`
|
||||
// each queue family must only have one entry in `queues`
|
||||
let mut queues: Vec<(u32, Vec<f32>)> =
|
||||
Vec::with_capacity(physical_device.queue_families().len());
|
||||
|
||||
// this variable will contain the queue family ID and queue ID of each requested queue
|
||||
let mut output_queues: SmallVec<[(u32, u32); 8]> = SmallVec::new();
|
||||
|
||||
// Device layers were deprecated in Vulkan 1.0.13, and device layer requests should be
|
||||
// ignored by the driver. For backwards compatibility, the spec recommends passing the
|
||||
// exact instance layers to the device as well. There's no need to support separate
|
||||
// requests at device creation time for legacy drivers: the spec claims that "[at] the
|
||||
// time of deprecation there were no known device-only layers."
|
||||
//
|
||||
// Because there's no way to query the list of layers enabled for an instance, we need
|
||||
// to save it alongside the instance. (`vkEnumerateDeviceLayerProperties` should get
|
||||
// the right list post-1.0.13, but not pre-1.0.13, so we can't use it here.)
|
||||
let layers_ptr = instance
|
||||
.loaded_layers()
|
||||
.map(|layer| layer.as_ptr())
|
||||
.collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
let extensions = extensions.into();
|
||||
let extensions_list = extensions
|
||||
.iter()
|
||||
.map(|extension| extension.as_ptr())
|
||||
.collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
let requested_features = Features {
|
||||
// Always enabled; see below.
|
||||
robust_buffer_access: true,
|
||||
..*requested_features
|
||||
};
|
||||
|
||||
// device creation
|
||||
let device = unsafe {
|
||||
// each element of `queues` is a `(queue_family, priorities)`
|
||||
// each queue family must only have one entry in `queues`
|
||||
let mut queues: Vec<(u32, Vec<f32>)> = Vec::with_capacity(phys.queue_families().len());
|
||||
|
||||
for (queue_family, priority) in queue_families {
|
||||
// checking the parameters
|
||||
assert_eq!(
|
||||
queue_family.physical_device().internal_object(),
|
||||
phys.internal_object()
|
||||
physical_device.internal_object()
|
||||
);
|
||||
if priority < 0.0 || priority > 1.0 {
|
||||
return Err(DeviceCreationError::PriorityOutOfRange);
|
||||
@ -280,25 +285,29 @@ impl Device {
|
||||
)
|
||||
.collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
// TODO: The plan regarding `robust_buffer_access` is to check the shaders' code to see
|
||||
// if they can possibly perform out-of-bounds reads and writes. If the user tries
|
||||
// to use a shader that can perform out-of-bounds operations without having
|
||||
// `robust_buffer_access` enabled, an error is returned.
|
||||
//
|
||||
// However for the moment this verification isn't performed. In order to be safe,
|
||||
// we always enable the `robust_buffer_access` feature as it is guaranteed to be
|
||||
// supported everywhere.
|
||||
//
|
||||
// The only alternative (while waiting for shaders introspection to work) is to
|
||||
// make all shaders depend on `robust_buffer_access`. But since usually the
|
||||
// majority of shaders don't need this feature, it would be very annoying to have
|
||||
// to enable it manually when you don't need it.
|
||||
//
|
||||
// Note that if we ever remove this, don't forget to adjust the change in
|
||||
// `Device`'s construction below.
|
||||
let mut features_ffi = FeaturesFfi::default();
|
||||
features_ffi.make_chain(api_version, requested_extensions);
|
||||
features_ffi.write(&requested_features);
|
||||
|
||||
let mut features_ffi = <FeaturesFfi>::from(&requested_features);
|
||||
features_ffi.make_chain(api_version);
|
||||
// Device layers were deprecated in Vulkan 1.0.13, and device layer requests should be
|
||||
// ignored by the driver. For backwards compatibility, the spec recommends passing the
|
||||
// exact instance layers to the device as well. There's no need to support separate
|
||||
// requests at device creation time for legacy drivers: the spec claims that "[at] the
|
||||
// time of deprecation there were no known device-only layers."
|
||||
//
|
||||
// Because there's no way to query the list of layers enabled for an instance, we need
|
||||
// to save it alongside the instance. (`vkEnumerateDeviceLayerProperties` should get
|
||||
// the right list post-1.0.13, but not pre-1.0.13, so we can't use it here.)
|
||||
let layers_ptrs = instance
|
||||
.loaded_layers()
|
||||
.map(|layer| layer.as_ptr())
|
||||
.collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
let extensions_strings: Vec<CString> = requested_extensions.into();
|
||||
let extensions_ptrs = extensions_strings
|
||||
.iter()
|
||||
.map(|extension| extension.as_ptr())
|
||||
.collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
let has_khr_get_physical_device_properties2 = instance
|
||||
.loaded_extensions()
|
||||
@ -313,10 +322,10 @@ impl Device {
|
||||
flags: ash::vk::DeviceCreateFlags::empty(),
|
||||
queue_create_info_count: queues.len() as u32,
|
||||
p_queue_create_infos: queues.as_ptr(),
|
||||
enabled_layer_count: layers_ptr.len() as u32,
|
||||
pp_enabled_layer_names: layers_ptr.as_ptr(),
|
||||
enabled_extension_count: extensions_list.len() as u32,
|
||||
pp_enabled_extension_names: extensions_list.as_ptr(),
|
||||
enabled_layer_count: layers_ptrs.len() as u32,
|
||||
pp_enabled_layer_names: layers_ptrs.as_ptr(),
|
||||
enabled_extension_count: extensions_ptrs.len() as u32,
|
||||
pp_enabled_extension_names: extensions_ptrs.as_ptr(),
|
||||
p_enabled_features: if has_khr_get_physical_device_properties2 {
|
||||
ptr::null()
|
||||
} else {
|
||||
@ -327,12 +336,13 @@ impl Device {
|
||||
|
||||
let mut output = MaybeUninit::uninit();
|
||||
check_errors(fns_i.v1_0.create_device(
|
||||
phys.internal_object(),
|
||||
physical_device.internal_object(),
|
||||
&infos,
|
||||
ptr::null(),
|
||||
output.as_mut_ptr(),
|
||||
))?;
|
||||
output.assume_init()
|
||||
|
||||
(output.assume_init(), output_queues)
|
||||
};
|
||||
|
||||
// loading the function pointers of the newly-created device
|
||||
@ -341,7 +351,7 @@ impl Device {
|
||||
});
|
||||
|
||||
let mut active_queue_families: SmallVec<[u32; 8]> = SmallVec::new();
|
||||
for (queue_family, _) in output_queues.iter() {
|
||||
for (queue_family, _) in queues.iter() {
|
||||
if let None = active_queue_families
|
||||
.iter()
|
||||
.find(|&&qf| qf == *queue_family)
|
||||
@ -351,8 +361,8 @@ impl Device {
|
||||
}
|
||||
|
||||
let device = Arc::new(Device {
|
||||
instance: phys.instance().clone(),
|
||||
physical_device: phys.index(),
|
||||
instance: physical_device.instance().clone(),
|
||||
physical_device: physical_device.index(),
|
||||
device: device,
|
||||
api_version,
|
||||
fns,
|
||||
@ -364,7 +374,7 @@ impl Device {
|
||||
robust_buffer_access: true,
|
||||
..requested_features.clone()
|
||||
},
|
||||
extensions: (&extensions).into(),
|
||||
extensions: requested_extensions.clone(),
|
||||
active_queue_families,
|
||||
allocation_count: Mutex::new(0),
|
||||
fence_pool: Mutex::new(Vec::new()),
|
||||
@ -373,16 +383,16 @@ impl Device {
|
||||
});
|
||||
|
||||
// Iterator for the produced queues.
|
||||
let output_queues = QueuesIter {
|
||||
let queues = QueuesIter {
|
||||
next_queue: 0,
|
||||
device: device.clone(),
|
||||
families_and_ids: output_queues,
|
||||
families_and_ids: queues,
|
||||
};
|
||||
|
||||
Ok((device, output_queues))
|
||||
Ok((device, queues))
|
||||
}
|
||||
|
||||
/// Returns the Vulkan version supported by this `Device`.
|
||||
/// Returns the Vulkan version supported by the device.
|
||||
///
|
||||
/// This is the lower of the
|
||||
/// [physical device's supported version](crate::instance::PhysicalDevice::api_version) and
|
||||
@ -442,13 +452,13 @@ impl Device {
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the features that are enabled in the device.
|
||||
/// Returns the features that have been enabled on the device.
|
||||
#[inline]
|
||||
pub fn enabled_features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// Returns the list of extensions that have been loaded.
|
||||
/// Returns the extensions that have been enabled on the device.
|
||||
#[inline]
|
||||
pub fn loaded_extensions(&self) -> &DeviceExtensions {
|
||||
&self.extensions
|
||||
@ -742,7 +752,7 @@ impl Iterator for QueuesIter {
|
||||
impl ExactSizeIterator for QueuesIter {}
|
||||
|
||||
/// Error that can be returned when creating a device.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum DeviceCreationError {
|
||||
/// Failed to create the device for an implementation-specific reason.
|
||||
InitializationFailed,
|
||||
@ -763,6 +773,10 @@ pub enum DeviceCreationError {
|
||||
OutOfHostMemory,
|
||||
/// There is no memory available on the device (ie. video memory).
|
||||
OutOfDeviceMemory,
|
||||
/// A restriction for an extension was not met.
|
||||
ExtensionRestrictionNotMet(ExtensionRestrictionError),
|
||||
/// A restriction for a feature was not met.
|
||||
FeatureRestrictionNotMet(FeatureRestrictionError),
|
||||
}
|
||||
|
||||
impl error::Error for DeviceCreationError {}
|
||||
@ -770,35 +784,42 @@ impl error::Error for DeviceCreationError {}
|
||||
impl fmt::Display for DeviceCreationError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"{}",
|
||||
match *self {
|
||||
DeviceCreationError::InitializationFailed => {
|
||||
write!(
|
||||
fmt,
|
||||
"failed to create the device for an implementation-specific reason"
|
||||
)
|
||||
}
|
||||
DeviceCreationError::OutOfHostMemory => "no memory available on the host",
|
||||
DeviceCreationError::OutOfDeviceMemory =>
|
||||
"no memory available on the graphical device",
|
||||
DeviceCreationError::DeviceLost => "failed to connect to the device",
|
||||
DeviceCreationError::OutOfHostMemory => write!(fmt, "no memory available on the host"),
|
||||
DeviceCreationError::OutOfDeviceMemory => {
|
||||
write!(fmt, "no memory available on the graphical device")
|
||||
}
|
||||
DeviceCreationError::DeviceLost => write!(fmt, "failed to connect to the device"),
|
||||
DeviceCreationError::TooManyQueuesForFamily => {
|
||||
"tried to create too many queues for a given family"
|
||||
write!(fmt, "tried to create too many queues for a given family")
|
||||
}
|
||||
DeviceCreationError::FeatureNotPresent => {
|
||||
write!(
|
||||
fmt,
|
||||
"some of the requested features are unsupported by the physical device"
|
||||
)
|
||||
}
|
||||
DeviceCreationError::PriorityOutOfRange => {
|
||||
write!(
|
||||
fmt,
|
||||
"the priority of one of the queues is out of the [0.0; 1.0] range"
|
||||
)
|
||||
}
|
||||
DeviceCreationError::ExtensionNotPresent => {
|
||||
"some of the requested device extensions are not supported by the physical device"
|
||||
write!(fmt,"some of the requested device extensions are not supported by the physical device")
|
||||
}
|
||||
DeviceCreationError::TooManyObjects => {
|
||||
"you have reached the limit to the number of devices that can be created from the
|
||||
same physical device"
|
||||
write!(fmt,"you have reached the limit to the number of devices that can be created from the same physical device")
|
||||
}
|
||||
DeviceCreationError::ExtensionRestrictionNotMet(err) => err.fmt(fmt),
|
||||
DeviceCreationError::FeatureRestrictionNotMet(err) => err.fmt(fmt),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -818,6 +839,20 @@ impl From<Error> for DeviceCreationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExtensionRestrictionError> for DeviceCreationError {
|
||||
#[inline]
|
||||
fn from(err: ExtensionRestrictionError) -> Self {
|
||||
Self::ExtensionRestrictionNotMet(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FeatureRestrictionError> for DeviceCreationError {
|
||||
#[inline]
|
||||
fn from(err: FeatureRestrictionError) -> Self {
|
||||
Self::FeatureRestrictionNotMet(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a queue where commands can be submitted.
|
||||
// TODO: should use internal synchronization?
|
||||
#[derive(Debug)]
|
||||
@ -900,7 +935,7 @@ mod tests {
|
||||
use crate::device::Device;
|
||||
use crate::device::DeviceCreationError;
|
||||
use crate::device::DeviceExtensions;
|
||||
use crate::device::Features;
|
||||
use crate::device::{FeatureRestriction, FeatureRestrictionError, Features};
|
||||
use crate::instance;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -954,7 +989,10 @@ mod tests {
|
||||
&DeviceExtensions::none(),
|
||||
Some((family, 1.0)),
|
||||
) {
|
||||
Err(DeviceCreationError::FeatureNotPresent) => return, // Success
|
||||
Err(DeviceCreationError::FeatureRestrictionNotMet(FeatureRestrictionError {
|
||||
restriction: FeatureRestriction::NotSupported,
|
||||
..
|
||||
})) => return, // Success
|
||||
_ => panic!(),
|
||||
};
|
||||
}
|
||||
|
@ -7,28 +7,37 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
use crate::instance::loader::LoadingError;
|
||||
use crate::Error;
|
||||
use crate::OomError;
|
||||
use crate::Version;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
macro_rules! extensions {
|
||||
($sname:ident, $rawname:ident, $($ext:ident => $s:expr,)*) => (
|
||||
(
|
||||
$sname:ident,
|
||||
$($member:ident => {
|
||||
doc: $doc:expr,
|
||||
raw: $raw:expr,
|
||||
requires_core: $requires_core:expr,
|
||||
requires_device_extensions: [$($requires_device_extension:ident),*],
|
||||
requires_instance_extensions: [$($requires_instance_extension:ident),*]$(,)?
|
||||
},)*
|
||||
) => (
|
||||
/// List of extensions that are enabled or available.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct $sname {
|
||||
$(
|
||||
pub $ext: bool,
|
||||
#[doc = $doc]
|
||||
pub $member: bool,
|
||||
)*
|
||||
|
||||
/// This field ensures that an instance of this `Extensions` struct
|
||||
/// can only be created through Vulkano functions and the update
|
||||
/// syntax. This way, extensions can be added to Vulkano without
|
||||
/// breaking existing code.
|
||||
pub _unbuildable: Unbuildable,
|
||||
pub _unbuildable: crate::extensions::Unbuildable,
|
||||
}
|
||||
|
||||
impl $sname {
|
||||
@ -36,8 +45,8 @@ macro_rules! extensions {
|
||||
#[inline]
|
||||
pub const fn none() -> $sname {
|
||||
$sname {
|
||||
$($ext: false,)*
|
||||
_unbuildable: Unbuildable(())
|
||||
$($member: false,)*
|
||||
_unbuildable: crate::extensions::Unbuildable(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,9 +55,9 @@ macro_rules! extensions {
|
||||
pub const fn union(&self, other: &$sname) -> $sname {
|
||||
$sname {
|
||||
$(
|
||||
$ext: self.$ext || other.$ext,
|
||||
$member: self.$member || other.$member,
|
||||
)*
|
||||
_unbuildable: Unbuildable(())
|
||||
_unbuildable: crate::extensions::Unbuildable(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,9 +66,9 @@ macro_rules! extensions {
|
||||
pub const fn intersection(&self, other: &$sname) -> $sname {
|
||||
$sname {
|
||||
$(
|
||||
$ext: self.$ext && other.$ext,
|
||||
$member: self.$member && other.$member,
|
||||
)*
|
||||
_unbuildable: Unbuildable(())
|
||||
_unbuildable: crate::extensions::Unbuildable(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,25 +77,25 @@ macro_rules! extensions {
|
||||
pub const fn difference(&self, other: &$sname) -> $sname {
|
||||
$sname {
|
||||
$(
|
||||
$ext: self.$ext && !other.$ext,
|
||||
$member: self.$member && !other.$member,
|
||||
)*
|
||||
_unbuildable: Unbuildable(())
|
||||
_unbuildable: crate::extensions::Unbuildable(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for $sname {
|
||||
impl std::fmt::Debug for $sname {
|
||||
#[allow(unused_assignments)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "[")?;
|
||||
|
||||
let mut first = true;
|
||||
|
||||
$(
|
||||
if self.$ext {
|
||||
if self.$member {
|
||||
if !first { write!(f, ", ")? }
|
||||
else { first = false; }
|
||||
f.write_str(str::from_utf8($s).unwrap())?;
|
||||
f.write_str(std::str::from_utf8($raw).unwrap())?;
|
||||
}
|
||||
)*
|
||||
|
||||
@ -94,82 +103,28 @@ macro_rules! extensions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set of extensions, not restricted to those vulkano knows about.
|
||||
///
|
||||
/// This is useful when interacting with external code that has statically-unknown extension
|
||||
/// requirements.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct $rawname(HashSet<CString>);
|
||||
|
||||
impl $rawname {
|
||||
/// Constructs an extension set containing the supplied extensions.
|
||||
pub fn new<I>(extensions: I) -> Self
|
||||
where I: IntoIterator<Item=CString>
|
||||
{
|
||||
$rawname(extensions.into_iter().collect())
|
||||
}
|
||||
|
||||
/// Constructs an empty extension set.
|
||||
pub fn none() -> Self { $rawname(HashSet::new()) }
|
||||
|
||||
/// Adds an extension to the set if it is not already present.
|
||||
pub fn insert(&mut self, extension: CString) {
|
||||
self.0.insert(extension);
|
||||
}
|
||||
|
||||
/// Returns the intersection of this set and another.
|
||||
pub fn intersection(&self, other: &Self) -> Self {
|
||||
$rawname(self.0.intersection(&other.0).cloned().collect())
|
||||
}
|
||||
|
||||
/// Returns the difference of another set from this one.
|
||||
pub fn difference(&self, other: &Self) -> Self {
|
||||
$rawname(self.0.difference(&other.0).cloned().collect())
|
||||
}
|
||||
|
||||
/// Returns the union of both extension sets
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
$rawname(self.0.union(&other.0).cloned().collect())
|
||||
}
|
||||
|
||||
// TODO: impl Iterator
|
||||
pub fn iter(&self) -> ::std::collections::hash_set::Iter<CString> { self.0.iter() }
|
||||
}
|
||||
|
||||
impl fmt::Debug for $rawname {
|
||||
#[allow(unused_assignments)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<CString> for $rawname {
|
||||
fn from_iter<T>(iter: T) -> Self
|
||||
where T: IntoIterator<Item = CString>
|
||||
{
|
||||
$rawname(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a $sname> for $rawname {
|
||||
fn from(x: &'a $sname) -> Self {
|
||||
let mut data = HashSet::new();
|
||||
$(if x.$ext { data.insert(CString::new(&$s[..]).unwrap()); })*
|
||||
$rawname(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a $rawname> for $sname {
|
||||
fn from(x: &'a $rawname) -> Self {
|
||||
let mut extensions = $sname::none();
|
||||
impl<'a, I> From<I> for $sname where I: IntoIterator<Item = &'a std::ffi::CStr> {
|
||||
fn from(names: I) -> Self {
|
||||
let mut extensions = Self::none();
|
||||
for name in names {
|
||||
match name.to_bytes() {
|
||||
$(
|
||||
if x.0.iter().any(|x| x.as_bytes() == &$s[..]) {
|
||||
extensions.$ext = true;
|
||||
}
|
||||
$raw => { extensions.$member = true; }
|
||||
)*
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
extensions
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a $sname> for Vec<std::ffi::CString> {
|
||||
fn from(x: &'a $sname) -> Self {
|
||||
let mut data = Self::new();
|
||||
$(if x.$member { data.push(std::ffi::CString::new(&$raw[..]).unwrap()); })*
|
||||
data
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -233,3 +188,76 @@ impl From<Error> for SupportedExtensionsError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that can happen when enabling an extension on an instance or device.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ExtensionRestrictionError {
|
||||
/// The extension in question.
|
||||
pub extension: &'static str,
|
||||
/// The restriction that was not met.
|
||||
pub restriction: ExtensionRestriction,
|
||||
}
|
||||
|
||||
impl error::Error for ExtensionRestrictionError {}
|
||||
|
||||
impl fmt::Display for ExtensionRestrictionError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"a restriction for the extension {} was not met: {}",
|
||||
self.extension, self.restriction,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ExtensionRestriction {
|
||||
/// Not supported by the loader or physical device.
|
||||
NotSupported,
|
||||
/// Requires a minimum Vulkan API version.
|
||||
RequiresCore(Version),
|
||||
/// Requires a device extension to be enabled.
|
||||
RequiresDeviceExtension(&'static str),
|
||||
/// Requires an instance extension to be enabled.
|
||||
RequiresInstanceExtension(&'static str),
|
||||
/// Required to be enabled by the physical device.
|
||||
RequiredIfSupported,
|
||||
/// Requires a device extension to be disabled.
|
||||
ConflictsDeviceExtension(&'static str),
|
||||
}
|
||||
|
||||
impl fmt::Display for ExtensionRestriction {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
ExtensionRestriction::NotSupported => {
|
||||
write!(fmt, "not supported by the loader or physical device")
|
||||
}
|
||||
ExtensionRestriction::RequiresCore(version) => {
|
||||
write!(
|
||||
fmt,
|
||||
"requires Vulkan API version {}.{}",
|
||||
version.major, version.minor
|
||||
)
|
||||
}
|
||||
ExtensionRestriction::RequiresDeviceExtension(ext) => {
|
||||
write!(fmt, "requires device extension {} to be enabled", ext)
|
||||
}
|
||||
ExtensionRestriction::RequiresInstanceExtension(ext) => {
|
||||
write!(fmt, "requires instance extension {} to be enabled", ext)
|
||||
}
|
||||
ExtensionRestriction::RequiredIfSupported => {
|
||||
write!(fmt, "required to be enabled by the physical device")
|
||||
}
|
||||
ExtensionRestriction::ConflictsDeviceExtension(ext) => {
|
||||
write!(fmt, "requires device extension {} to be disabled", ext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This helper type can only be instantiated inside this module.
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Unbuildable(pub(crate) ());
|
||||
|
@ -7,21 +7,17 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use ash::vk::*;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
macro_rules! fns {
|
||||
($struct_name:ident, { $($member:ident => $fn_struct:ident,)+ }) => {
|
||||
pub struct $struct_name {
|
||||
$(
|
||||
pub $member: $fn_struct,
|
||||
pub $member: ash::vk::$fn_struct,
|
||||
)+
|
||||
}
|
||||
|
||||
impl $struct_name {
|
||||
pub fn load<F>(mut load_fn: F) -> $struct_name
|
||||
where F: FnMut(&CStr) -> *const c_void
|
||||
where F: FnMut(&std::ffi::CStr) -> *const std::ffi::c_void
|
||||
{
|
||||
$struct_name {
|
||||
$(
|
||||
@ -33,50 +29,5 @@ macro_rules! fns {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: would be nice if these could be generated automatically from Vulkano's list of extensions
|
||||
|
||||
fns!(EntryFunctions, {
|
||||
v1_0 => EntryFnV1_0,
|
||||
v1_1 => EntryFnV1_1,
|
||||
v1_2 => EntryFnV1_2,
|
||||
});
|
||||
|
||||
fns!(InstanceFunctions, {
|
||||
v1_0 => InstanceFnV1_0,
|
||||
v1_1 => InstanceFnV1_1,
|
||||
v1_2 => InstanceFnV1_2,
|
||||
|
||||
khr_android_surface => KhrAndroidSurfaceFn,
|
||||
khr_display => KhrDisplayFn,
|
||||
khr_get_physical_device_properties2 => KhrGetPhysicalDeviceProperties2Fn, // Promoted
|
||||
khr_surface => KhrSurfaceFn,
|
||||
khr_wayland_surface => KhrWaylandSurfaceFn,
|
||||
khr_win32_surface => KhrWin32SurfaceFn,
|
||||
khr_xcb_surface => KhrXcbSurfaceFn,
|
||||
khr_xlib_surface => KhrXlibSurfaceFn,
|
||||
|
||||
// This is an instance extension, so it should be loaded with `vkGetInstanceProcAddr`, despite
|
||||
// having device-level functions. This is an unfortunate exception in the spec that even causes
|
||||
// the LunarG people headaches:
|
||||
// https://github.com/KhronosGroup/Vulkan-Loader/issues/116#issuecomment-580982393
|
||||
ext_debug_utils => ExtDebugUtilsFn,
|
||||
|
||||
mvk_ios_surface => MvkIosSurfaceFn,
|
||||
mvk_macos_surface => MvkMacosSurfaceFn,
|
||||
|
||||
nn_vi_surface => NnViSurfaceFn,
|
||||
});
|
||||
|
||||
fns!(DeviceFunctions, {
|
||||
v1_0 => DeviceFnV1_0,
|
||||
v1_1 => DeviceFnV1_1,
|
||||
v1_2 => DeviceFnV1_2,
|
||||
|
||||
khr_external_memory_fd => KhrExternalMemoryFdFn,
|
||||
khr_get_memory_requirements2 => KhrGetMemoryRequirements2Fn, // Promoted
|
||||
khr_maintenance1 => KhrMaintenance1Fn, // Promoted
|
||||
khr_swapchain => KhrSwapchainFn,
|
||||
|
||||
ext_buffer_device_address => ExtBufferDeviceAddressFn,
|
||||
ext_full_screen_exclusive => ExtFullScreenExclusiveFn,
|
||||
});
|
||||
pub use crate::autogen::{DeviceFunctions, EntryFunctions, InstanceFunctions};
|
||||
pub(crate) use fns;
|
||||
|
@ -7,184 +7,147 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::iter::FromIterator;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
|
||||
use crate::check_errors;
|
||||
use crate::extensions::SupportedExtensionsError;
|
||||
pub use crate::extensions::{
|
||||
ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError,
|
||||
};
|
||||
use crate::instance::loader;
|
||||
use crate::instance::loader::LoadingError;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
|
||||
macro_rules! instance_extensions {
|
||||
($sname:ident, $rawname:ident, $($ext:ident => $s:expr,)*) => (
|
||||
(
|
||||
$($member:ident => {
|
||||
doc: $doc:expr,
|
||||
raw: $raw:expr,
|
||||
requires_core: $requires_core:expr,
|
||||
requires_extensions: [$($requires_extension:ident),*]$(,)?
|
||||
},)*
|
||||
) => (
|
||||
extensions! {
|
||||
$sname, $rawname,
|
||||
$( $ext => $s,)*
|
||||
InstanceExtensions,
|
||||
$($member => {
|
||||
doc: $doc,
|
||||
raw: $raw,
|
||||
requires_core: $requires_core,
|
||||
requires_device_extensions: [],
|
||||
requires_instance_extensions: [$($requires_extension),*],
|
||||
},)*
|
||||
}
|
||||
|
||||
impl $rawname {
|
||||
/// See the docs of supported_by_core().
|
||||
pub fn supported_by_core_raw() -> Result<Self, SupportedExtensionsError> {
|
||||
$rawname::supported_by_core_raw_with_loader(loader::auto_loader()?)
|
||||
}
|
||||
|
||||
/// Same as `supported_by_core_raw()`, but allows specifying a loader.
|
||||
pub fn supported_by_core_raw_with_loader<L>(ptrs: &loader::FunctionPointers<L>)
|
||||
-> Result<Self, SupportedExtensionsError>
|
||||
where L: loader::Loader
|
||||
{
|
||||
let fns = ptrs.fns();
|
||||
|
||||
let properties: Vec<ash::vk::ExtensionProperties> = unsafe {
|
||||
let mut num = 0;
|
||||
check_errors(fns.v1_0.enumerate_instance_extension_properties(
|
||||
ptr::null(), &mut num, ptr::null_mut()
|
||||
))?;
|
||||
|
||||
let mut properties = Vec::with_capacity(num as usize);
|
||||
check_errors(fns.v1_0.enumerate_instance_extension_properties(
|
||||
ptr::null(), &mut num, properties.as_mut_ptr()
|
||||
))?;
|
||||
properties.set_len(num as usize);
|
||||
properties
|
||||
};
|
||||
Ok($rawname(properties.iter().map(|x| unsafe { CStr::from_ptr(x.extension_name.as_ptr()) }.to_owned()).collect()))
|
||||
}
|
||||
|
||||
/// Returns a `RawExtensions` object with extensions supported by the core driver.
|
||||
pub fn supported_by_core() -> Result<Self, LoadingError> {
|
||||
match $rawname::supported_by_core_raw() {
|
||||
Ok(l) => Ok(l),
|
||||
Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
|
||||
Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `supported_by_core`, but allows specifying a loader.
|
||||
pub fn supported_by_core_with_loader<L>(ptrs: &loader::FunctionPointers<L>)
|
||||
-> Result<Self, LoadingError>
|
||||
where L: loader::Loader
|
||||
{
|
||||
match $rawname::supported_by_core_raw_with_loader(ptrs) {
|
||||
Ok(l) => Ok(l),
|
||||
Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
|
||||
Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $sname {
|
||||
/// See the docs of supported_by_core().
|
||||
pub fn supported_by_core_raw() -> Result<Self, SupportedExtensionsError> {
|
||||
$sname::supported_by_core_raw_with_loader(loader::auto_loader()?)
|
||||
}
|
||||
|
||||
/// See the docs of supported_by_core().
|
||||
pub fn supported_by_core_raw_with_loader<L>(ptrs: &loader::FunctionPointers<L>)
|
||||
-> Result<Self, SupportedExtensionsError>
|
||||
where L: loader::Loader
|
||||
{
|
||||
let fns = ptrs.fns();
|
||||
|
||||
let properties: Vec<ash::vk::ExtensionProperties> = unsafe {
|
||||
let mut num = 0;
|
||||
check_errors(fns.v1_0.enumerate_instance_extension_properties(
|
||||
ptr::null(), &mut num, ptr::null_mut()
|
||||
))?;
|
||||
|
||||
let mut properties = Vec::with_capacity(num as usize);
|
||||
check_errors(fns.v1_0.enumerate_instance_extension_properties(
|
||||
ptr::null(), &mut num, properties.as_mut_ptr()
|
||||
))?;
|
||||
properties.set_len(num as usize);
|
||||
properties
|
||||
};
|
||||
|
||||
let mut extensions = $sname::none();
|
||||
for property in properties {
|
||||
let name = unsafe { CStr::from_ptr(property.extension_name.as_ptr()) };
|
||||
impl InstanceExtensions {
|
||||
/// Checks enabled extensions against the instance version and each other.
|
||||
pub(super) fn check_requirements(
|
||||
&self,
|
||||
supported: &InstanceExtensions,
|
||||
api_version: crate::Version,
|
||||
) -> Result<(), crate::extensions::ExtensionRestrictionError> {
|
||||
$(
|
||||
// TODO: Check specVersion?
|
||||
if name.to_bytes() == &$s[..] {
|
||||
extensions.$ext = true;
|
||||
if self.$member {
|
||||
if !supported.$member {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::NotSupported,
|
||||
});
|
||||
}
|
||||
|
||||
if api_version < $requires_core {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::RequiresCore($requires_core),
|
||||
});
|
||||
} else {
|
||||
$(
|
||||
if !self.$requires_extension {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::RequiresInstanceExtension(stringify!($requires_extension)),
|
||||
});
|
||||
}
|
||||
)*
|
||||
}
|
||||
Ok(extensions)
|
||||
}
|
||||
|
||||
/// Returns a `RawExtensions` object with extensions supported by the core driver.
|
||||
pub fn supported_by_core() -> Result<Self, LoadingError> {
|
||||
match $sname::supported_by_core_raw() {
|
||||
Ok(l) => Ok(l),
|
||||
Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
|
||||
Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `supported_by_core`, but allows specifying a loader.
|
||||
pub fn supported_by_core_with_loader<L>(ptrs: &loader::FunctionPointers<L>)
|
||||
-> Result<Self, LoadingError>
|
||||
where L: loader::Loader
|
||||
{
|
||||
match $sname::supported_by_core_raw_with_loader(ptrs) {
|
||||
Ok(l) => Ok(l),
|
||||
Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
|
||||
Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
|
||||
}
|
||||
)*
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
instance_extensions! {
|
||||
InstanceExtensions,
|
||||
RawInstanceExtensions,
|
||||
pub use crate::autogen::InstanceExtensions;
|
||||
pub(crate) use instance_extensions;
|
||||
|
||||
// List in order: khr, ext, then alphabetical
|
||||
khr_android_surface => b"VK_KHR_android_surface",
|
||||
khr_device_group_creation => b"VK_KHR_device_group_creation",
|
||||
khr_display => b"VK_KHR_display",
|
||||
khr_external_fence_capabilities => b"VK_KHR_external_fence_capabilities",
|
||||
khr_external_memory_capabilities => b"VK_KHR_external_memory_capabilities",
|
||||
khr_external_semaphore_capabilities => b"VK_KHR_external_semaphore_capabilities",
|
||||
khr_get_display_properties2 => b"VK_KHR_get_display_properties2",
|
||||
khr_get_physical_device_properties2 => b"VK_KHR_get_physical_device_properties2",
|
||||
khr_get_surface_capabilities2 => b"VK_KHR_get_surface_capabilities2",
|
||||
khr_surface => b"VK_KHR_surface",
|
||||
khr_wayland_surface => b"VK_KHR_wayland_surface",
|
||||
khr_win32_surface => b"VK_KHR_win32_surface",
|
||||
khr_xcb_surface => b"VK_KHR_xcb_surface",
|
||||
khr_xlib_surface => b"VK_KHR_xlib_surface",
|
||||
ext_acquire_xlib_display => b"VK_EXT_acquire_xlib_display",
|
||||
ext_debug_report => b"VK_EXT_debug_report",
|
||||
ext_debug_utils => b"VK_EXT_debug_utils",
|
||||
ext_direct_mode_display => b"VK_EXT_direct_mode_display",
|
||||
ext_display_surface_counter => b"VK_EXT_display_surface_counter",
|
||||
ext_swapchain_colorspace => b"VK_EXT_swapchain_colorspace",
|
||||
mvk_ios_surface => b"VK_MVK_ios_surface",
|
||||
mvk_macos_surface => b"VK_MVK_macos_surface",
|
||||
mvk_moltenvk => b"VK_MVK_moltenvk", // TODO: confirm that it's an instance extension
|
||||
nn_vi_surface => b"VK_NN_vi_surface",
|
||||
impl InstanceExtensions {
|
||||
/// See the docs of supported_by_core().
|
||||
pub fn supported_by_core_raw() -> Result<Self, SupportedExtensionsError> {
|
||||
InstanceExtensions::supported_by_core_raw_with_loader(loader::auto_loader()?)
|
||||
}
|
||||
|
||||
/// Returns an `InstanceExtensions` object with extensions supported by the core driver.
|
||||
pub fn supported_by_core() -> Result<Self, LoadingError> {
|
||||
match InstanceExtensions::supported_by_core_raw() {
|
||||
Ok(l) => Ok(l),
|
||||
Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
|
||||
Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `supported_by_core`, but allows specifying a loader.
|
||||
pub fn supported_by_core_with_loader<L>(
|
||||
ptrs: &loader::FunctionPointers<L>,
|
||||
) -> Result<Self, LoadingError>
|
||||
where
|
||||
L: loader::Loader,
|
||||
{
|
||||
match InstanceExtensions::supported_by_core_raw_with_loader(ptrs) {
|
||||
Ok(l) => Ok(l),
|
||||
Err(SupportedExtensionsError::LoadingError(e)) => Err(e),
|
||||
Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs of supported_by_core().
|
||||
pub fn supported_by_core_raw_with_loader<L>(
|
||||
ptrs: &loader::FunctionPointers<L>,
|
||||
) -> Result<Self, SupportedExtensionsError>
|
||||
where
|
||||
L: loader::Loader,
|
||||
{
|
||||
let fns = ptrs.fns();
|
||||
|
||||
let properties: Vec<ash::vk::ExtensionProperties> = unsafe {
|
||||
let mut num = 0;
|
||||
check_errors(fns.v1_0.enumerate_instance_extension_properties(
|
||||
ptr::null(),
|
||||
&mut num,
|
||||
ptr::null_mut(),
|
||||
))?;
|
||||
|
||||
let mut properties = Vec::with_capacity(num as usize);
|
||||
check_errors(fns.v1_0.enumerate_instance_extension_properties(
|
||||
ptr::null(),
|
||||
&mut num,
|
||||
properties.as_mut_ptr(),
|
||||
))?;
|
||||
properties.set_len(num as usize);
|
||||
properties
|
||||
};
|
||||
|
||||
Ok(Self::from(properties.iter().map(|property| unsafe {
|
||||
CStr::from_ptr(property.extension_name.as_ptr())
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
/// This helper type can only be instantiated inside this module.
|
||||
/// See `*Extensions::_unbuildable`.
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Unbuildable(());
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::instance::{InstanceExtensions, RawInstanceExtensions};
|
||||
use crate::instance::InstanceExtensions;
|
||||
use std::ffi::CString;
|
||||
|
||||
#[test]
|
||||
fn empty_extensions() {
|
||||
let i: RawInstanceExtensions = (&InstanceExtensions::none()).into();
|
||||
let i: Vec<CString> = (&InstanceExtensions::none()).into();
|
||||
assert!(i.iter().next().is_none());
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,14 @@
|
||||
// according to those terms.
|
||||
|
||||
use crate::check_errors;
|
||||
use crate::extensions::ExtensionRestrictionError;
|
||||
use crate::fns::InstanceFunctions;
|
||||
use crate::instance::loader;
|
||||
use crate::instance::loader::FunctionPointers;
|
||||
use crate::instance::loader::Loader;
|
||||
use crate::instance::loader::LoadingError;
|
||||
use crate::instance::physical_device::{init_physical_devices, PhysicalDeviceInfos};
|
||||
use crate::instance::{InstanceExtensions, RawInstanceExtensions};
|
||||
use crate::instance::InstanceExtensions;
|
||||
use crate::Error;
|
||||
use crate::OomError;
|
||||
use crate::Version;
|
||||
@ -181,7 +182,7 @@ pub struct Instance {
|
||||
|
||||
pub(super) physical_devices: Vec<PhysicalDeviceInfos>,
|
||||
fns: InstanceFunctions,
|
||||
extensions: RawInstanceExtensions,
|
||||
extensions: InstanceExtensions,
|
||||
layers: SmallVec<[CString; 16]>,
|
||||
function_pointers: OwnedOrRef<FunctionPointers<Box<dyn Loader + Send + Sync>>>,
|
||||
}
|
||||
@ -217,15 +218,14 @@ impl Instance {
|
||||
// TODO: add a test for these ^
|
||||
// TODO: if no allocator is specified by the user, use Rust's allocator instead of leaving
|
||||
// the choice to Vulkan
|
||||
pub fn new<'a, L, Ext>(
|
||||
pub fn new<'a, L>(
|
||||
app_infos: Option<&ApplicationInfo>,
|
||||
max_api_version: Version,
|
||||
extensions: Ext,
|
||||
extensions: &InstanceExtensions,
|
||||
layers: L,
|
||||
) -> Result<Arc<Instance>, InstanceCreationError>
|
||||
where
|
||||
L: IntoIterator<Item = &'a str>,
|
||||
Ext: Into<RawInstanceExtensions>,
|
||||
{
|
||||
let layers = layers
|
||||
.into_iter()
|
||||
@ -235,23 +235,22 @@ impl Instance {
|
||||
Instance::new_inner(
|
||||
app_infos,
|
||||
max_api_version,
|
||||
extensions.into(),
|
||||
extensions,
|
||||
layers,
|
||||
OwnedOrRef::Ref(loader::auto_loader()?),
|
||||
)
|
||||
}
|
||||
|
||||
/// Same as `new`, but allows specifying a loader where to load Vulkan from.
|
||||
pub fn with_loader<'a, L, Ext>(
|
||||
pub fn with_loader<'a, L>(
|
||||
loader: FunctionPointers<Box<dyn Loader + Send + Sync>>,
|
||||
app_infos: Option<&ApplicationInfo>,
|
||||
max_api_version: Version,
|
||||
extensions: Ext,
|
||||
extensions: &InstanceExtensions,
|
||||
layers: L,
|
||||
) -> Result<Arc<Instance>, InstanceCreationError>
|
||||
where
|
||||
L: IntoIterator<Item = &'a str>,
|
||||
Ext: Into<RawInstanceExtensions>,
|
||||
{
|
||||
let layers = layers
|
||||
.into_iter()
|
||||
@ -261,7 +260,7 @@ impl Instance {
|
||||
Instance::new_inner(
|
||||
app_infos,
|
||||
max_api_version,
|
||||
extensions.into(),
|
||||
extensions,
|
||||
layers,
|
||||
OwnedOrRef::Owned(loader),
|
||||
)
|
||||
@ -270,10 +269,18 @@ impl Instance {
|
||||
fn new_inner(
|
||||
app_infos: Option<&ApplicationInfo>,
|
||||
max_api_version: Version,
|
||||
extensions: RawInstanceExtensions,
|
||||
extensions: &InstanceExtensions,
|
||||
layers: SmallVec<[CString; 16]>,
|
||||
function_pointers: OwnedOrRef<FunctionPointers<Box<dyn Loader + Send + Sync>>>,
|
||||
) -> Result<Arc<Instance>, InstanceCreationError> {
|
||||
let api_version = std::cmp::min(max_api_version, function_pointers.api_version()?);
|
||||
|
||||
// Check if the extensions are correct
|
||||
extensions.check_requirements(
|
||||
&InstanceExtensions::supported_by_core_with_loader(&function_pointers)?,
|
||||
api_version,
|
||||
)?;
|
||||
|
||||
// TODO: For now there are still buggy drivers that will segfault if you don't pass any
|
||||
// appinfos. Therefore for now we ensure that it can't be `None`.
|
||||
let def = Default::default();
|
||||
@ -299,8 +306,6 @@ impl Instance {
|
||||
None
|
||||
};
|
||||
|
||||
let api_version = std::cmp::min(max_api_version, function_pointers.api_version()?);
|
||||
|
||||
// Building the `vk::ApplicationInfo` if required.
|
||||
let app_infos = if let Some(app_infos) = app_infos {
|
||||
Some(ash::vk::ApplicationInfo {
|
||||
@ -334,12 +339,13 @@ impl Instance {
|
||||
};
|
||||
|
||||
// FIXME: check whether each layer is supported
|
||||
let layers_ptr = layers
|
||||
let layers_ptrs = layers
|
||||
.iter()
|
||||
.map(|layer| layer.as_ptr())
|
||||
.collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
let extensions_list = extensions
|
||||
let extensions_list: Vec<CString> = extensions.into();
|
||||
let extensions_ptrs = extensions_list
|
||||
.iter()
|
||||
.map(|extension| extension.as_ptr())
|
||||
.collect::<SmallVec<[_; 32]>>();
|
||||
@ -354,10 +360,10 @@ impl Instance {
|
||||
} else {
|
||||
ptr::null()
|
||||
},
|
||||
enabled_layer_count: layers_ptr.len() as u32,
|
||||
pp_enabled_layer_names: layers_ptr.as_ptr(),
|
||||
enabled_extension_count: extensions_list.len() as u32,
|
||||
pp_enabled_extension_names: extensions_list.as_ptr(),
|
||||
enabled_layer_count: layers_ptrs.len() as u32,
|
||||
pp_enabled_layer_names: layers_ptrs.as_ptr(),
|
||||
enabled_extension_count: extensions_ptrs.len() as u32,
|
||||
pp_enabled_extension_names: extensions_ptrs.as_ptr(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -383,7 +389,7 @@ impl Instance {
|
||||
//alloc: None,
|
||||
physical_devices: Vec::new(),
|
||||
fns,
|
||||
extensions,
|
||||
extensions: extensions.clone(),
|
||||
layers,
|
||||
function_pointers,
|
||||
};
|
||||
@ -402,7 +408,7 @@ impl Instance {
|
||||
unimplemented!()
|
||||
}*/
|
||||
|
||||
/// Returns the Vulkan version supported by this `Instance`.
|
||||
/// Returns the Vulkan version supported by the instance.
|
||||
///
|
||||
/// This is the lower of the
|
||||
/// [driver's supported version](crate::instance::loader::FunctionPointers::api_version) and
|
||||
@ -412,7 +418,7 @@ impl Instance {
|
||||
self.api_version
|
||||
}
|
||||
|
||||
/// Returns the maximum Vulkan version that was specified when creating this `Instance`.
|
||||
/// Returns the maximum Vulkan version that was specified when creating the instance.
|
||||
#[inline]
|
||||
pub fn max_api_version(&self) -> Version {
|
||||
self.max_api_version
|
||||
@ -424,7 +430,7 @@ impl Instance {
|
||||
&self.fns
|
||||
}
|
||||
|
||||
/// Returns the list of extensions that have been loaded.
|
||||
/// Returns the extensions that have been enabled on the instance.
|
||||
///
|
||||
/// This list is equal to what was passed to `Instance::new()`.
|
||||
///
|
||||
@ -437,19 +443,14 @@ impl Instance {
|
||||
///
|
||||
/// let extensions = InstanceExtensions::supported_by_core().unwrap();
|
||||
/// let instance = Instance::new(None, Version::V1_1, &extensions, None).unwrap();
|
||||
/// assert_eq!(instance.loaded_extensions(), extensions);
|
||||
/// assert_eq!(instance.loaded_extensions(), &extensions);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn loaded_extensions(&self) -> InstanceExtensions {
|
||||
InstanceExtensions::from(&self.extensions)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn raw_loaded_extensions(&self) -> &RawInstanceExtensions {
|
||||
pub fn loaded_extensions(&self) -> &InstanceExtensions {
|
||||
&self.extensions
|
||||
}
|
||||
|
||||
/// Returns the list of layers requested when creating this instance.
|
||||
/// Returns the layers that have been enabled on the instance.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn loaded_layers(&self) -> slice::Iter<CString> {
|
||||
@ -605,11 +606,12 @@ pub enum InstanceCreationError {
|
||||
InitializationFailed,
|
||||
/// One of the requested layers is missing.
|
||||
LayerNotPresent,
|
||||
/// One of the requested extensions is missing.
|
||||
/// One of the requested extensions is not supported by the implementation.
|
||||
ExtensionNotPresent,
|
||||
/// The version requested is not supported by the implementation.
|
||||
// TODO: more info about this once the question of the version has been resolved
|
||||
IncompatibleDriver,
|
||||
/// A restriction for an extension was not met.
|
||||
ExtensionRestrictionNotMet(ExtensionRestrictionError),
|
||||
}
|
||||
|
||||
impl error::Error for InstanceCreationError {
|
||||
@ -626,19 +628,17 @@ impl error::Error for InstanceCreationError {
|
||||
impl fmt::Display for InstanceCreationError {
|
||||
#[inline]
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(
|
||||
fmt,
|
||||
"{}",
|
||||
match *self {
|
||||
InstanceCreationError::LoadingError(_) =>
|
||||
"failed to load the Vulkan shared library",
|
||||
InstanceCreationError::OomError(_) => "not enough memory available",
|
||||
InstanceCreationError::InitializationFailed => "initialization failed",
|
||||
InstanceCreationError::LayerNotPresent => "layer not present",
|
||||
InstanceCreationError::ExtensionNotPresent => "extension not present",
|
||||
InstanceCreationError::IncompatibleDriver => "incompatible driver",
|
||||
InstanceCreationError::LoadingError(_) => {
|
||||
write!(fmt, "failed to load the Vulkan shared library")
|
||||
}
|
||||
InstanceCreationError::OomError(_) => write!(fmt, "not enough memory available"),
|
||||
InstanceCreationError::InitializationFailed => write!(fmt, "initialization failed"),
|
||||
InstanceCreationError::LayerNotPresent => write!(fmt, "layer not present"),
|
||||
InstanceCreationError::ExtensionNotPresent => write!(fmt, "extension not present"),
|
||||
InstanceCreationError::IncompatibleDriver => write!(fmt, "incompatible driver"),
|
||||
InstanceCreationError::ExtensionRestrictionNotMet(err) => err.fmt(fmt),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -656,6 +656,13 @@ impl From<LoadingError> for InstanceCreationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExtensionRestrictionError> for InstanceCreationError {
|
||||
#[inline]
|
||||
fn from(err: ExtensionRestrictionError) -> Self {
|
||||
Self::ExtensionRestrictionNotMet(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for InstanceCreationError {
|
||||
#[inline]
|
||||
fn from(err: Error) -> InstanceCreationError {
|
||||
|
@ -52,7 +52,6 @@
|
||||
//! `device` module for more info.
|
||||
|
||||
pub use self::extensions::InstanceExtensions;
|
||||
pub use self::extensions::RawInstanceExtensions;
|
||||
pub use self::instance::ApplicationInfo;
|
||||
pub use self::instance::Instance;
|
||||
pub use self::instance::InstanceCreationError;
|
||||
@ -71,10 +70,13 @@ pub use self::physical_device::PhysicalDeviceType;
|
||||
pub use self::physical_device::PhysicalDevicesIter;
|
||||
pub use self::physical_device::QueueFamiliesIter;
|
||||
pub use self::physical_device::QueueFamily;
|
||||
pub use crate::extensions::{
|
||||
ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError,
|
||||
};
|
||||
pub use crate::version::Version;
|
||||
|
||||
pub mod debug;
|
||||
mod extensions;
|
||||
pub(crate) mod extensions;
|
||||
mod instance;
|
||||
mod layers;
|
||||
mod limits;
|
||||
|
@ -8,7 +8,7 @@
|
||||
// according to those terms.
|
||||
|
||||
use crate::check_errors;
|
||||
use crate::device::{Features, FeaturesFfi};
|
||||
use crate::device::{DeviceExtensions, Features, FeaturesFfi};
|
||||
use crate::instance::limits::Limits;
|
||||
use crate::instance::{Instance, InstanceCreationError};
|
||||
use crate::sync::PipelineStage;
|
||||
@ -24,7 +24,7 @@ pub(super) fn init_physical_devices(
|
||||
instance: &Instance,
|
||||
) -> Result<Vec<PhysicalDeviceInfos>, InstanceCreationError> {
|
||||
let fns = instance.fns();
|
||||
let extensions = instance.loaded_extensions();
|
||||
let instance_extensions = instance.loaded_extensions();
|
||||
|
||||
let physical_devices: Vec<ash::vk::PhysicalDevice> = unsafe {
|
||||
let mut num = 0;
|
||||
@ -44,43 +44,81 @@ pub(super) fn init_physical_devices(
|
||||
devices
|
||||
};
|
||||
|
||||
let supported_extensions: Vec<DeviceExtensions> = physical_devices
|
||||
.iter()
|
||||
.map(
|
||||
|&physical_device| -> Result<DeviceExtensions, InstanceCreationError> {
|
||||
let extension_properties: Vec<ash::vk::ExtensionProperties> = unsafe {
|
||||
let mut num = 0;
|
||||
check_errors(fns.v1_0.enumerate_device_extension_properties(
|
||||
physical_device,
|
||||
ptr::null(),
|
||||
&mut num,
|
||||
ptr::null_mut(),
|
||||
))?;
|
||||
|
||||
let mut properties = Vec::with_capacity(num as usize);
|
||||
check_errors(fns.v1_0.enumerate_device_extension_properties(
|
||||
physical_device,
|
||||
ptr::null(),
|
||||
&mut num,
|
||||
properties.as_mut_ptr(),
|
||||
))?;
|
||||
properties.set_len(num as usize);
|
||||
properties
|
||||
};
|
||||
|
||||
Ok(DeviceExtensions::from(extension_properties.iter().map(
|
||||
|property| unsafe { CStr::from_ptr(property.extension_name.as_ptr()) },
|
||||
)))
|
||||
},
|
||||
)
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let iter = physical_devices
|
||||
.into_iter()
|
||||
.zip(supported_extensions.into_iter());
|
||||
|
||||
// Getting the properties of all physical devices.
|
||||
// If possible, we use VK_KHR_get_physical_device_properties2.
|
||||
let physical_devices = if instance.api_version() >= Version::V1_1
|
||||
|| extensions.khr_get_physical_device_properties2
|
||||
|| instance_extensions.khr_get_physical_device_properties2
|
||||
{
|
||||
init_physical_devices_inner2(instance, physical_devices)
|
||||
init_physical_devices_inner2(instance, iter)
|
||||
} else {
|
||||
init_physical_devices_inner(instance, physical_devices)
|
||||
init_physical_devices_inner(instance, iter)
|
||||
};
|
||||
|
||||
Ok(physical_devices)
|
||||
}
|
||||
|
||||
/// Initialize all physical devices
|
||||
fn init_physical_devices_inner(
|
||||
instance: &Instance,
|
||||
physical_devices: Vec<ash::vk::PhysicalDevice>,
|
||||
) -> Vec<PhysicalDeviceInfos> {
|
||||
fn init_physical_devices_inner<I>(instance: &Instance, info: I) -> Vec<PhysicalDeviceInfos>
|
||||
where
|
||||
I: IntoIterator<Item = (ash::vk::PhysicalDevice, DeviceExtensions)>,
|
||||
{
|
||||
let fns = instance.fns();
|
||||
let mut output = Vec::with_capacity(physical_devices.len());
|
||||
|
||||
for device in physical_devices.into_iter() {
|
||||
info.into_iter()
|
||||
.map(|(physical_device, supported_extensions)| {
|
||||
let properties: ash::vk::PhysicalDeviceProperties = unsafe {
|
||||
let mut output = MaybeUninit::uninit();
|
||||
fns.v1_0
|
||||
.get_physical_device_properties(device, output.as_mut_ptr());
|
||||
.get_physical_device_properties(physical_device, output.as_mut_ptr());
|
||||
output.assume_init()
|
||||
};
|
||||
|
||||
let queue_families = unsafe {
|
||||
let mut num = 0;
|
||||
fns.v1_0
|
||||
.get_physical_device_queue_family_properties(device, &mut num, ptr::null_mut());
|
||||
fns.v1_0.get_physical_device_queue_family_properties(
|
||||
physical_device,
|
||||
&mut num,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
|
||||
let mut families = Vec::with_capacity(num as usize);
|
||||
fns.v1_0.get_physical_device_queue_family_properties(
|
||||
device,
|
||||
physical_device,
|
||||
&mut num,
|
||||
families.as_mut_ptr(),
|
||||
);
|
||||
@ -91,39 +129,41 @@ fn init_physical_devices_inner(
|
||||
let memory: ash::vk::PhysicalDeviceMemoryProperties = unsafe {
|
||||
let mut output = MaybeUninit::uninit();
|
||||
fns.v1_0
|
||||
.get_physical_device_memory_properties(device, output.as_mut_ptr());
|
||||
.get_physical_device_memory_properties(physical_device, output.as_mut_ptr());
|
||||
output.assume_init()
|
||||
};
|
||||
|
||||
let available_features: Features = unsafe {
|
||||
let mut output = FeaturesFfi::default();
|
||||
fns.v1_0
|
||||
.get_physical_device_features(device, &mut output.head_as_mut().features);
|
||||
fns.v1_0.get_physical_device_features(
|
||||
physical_device,
|
||||
&mut output.head_as_mut().features,
|
||||
);
|
||||
Features::from(&output)
|
||||
};
|
||||
|
||||
output.push(PhysicalDeviceInfos {
|
||||
device,
|
||||
PhysicalDeviceInfos {
|
||||
physical_device,
|
||||
properties,
|
||||
extended_properties: PhysicalDeviceExtendedProperties::empty(),
|
||||
memory,
|
||||
queue_families,
|
||||
available_features: Features::from(available_features),
|
||||
});
|
||||
available_features,
|
||||
}
|
||||
output
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Initialize all physical devices, but use VK_KHR_get_physical_device_properties2
|
||||
/// TODO: Query extension-specific physical device properties, once a new instance extension is supported.
|
||||
fn init_physical_devices_inner2(
|
||||
instance: &Instance,
|
||||
physical_devices: Vec<ash::vk::PhysicalDevice>,
|
||||
) -> Vec<PhysicalDeviceInfos> {
|
||||
fn init_physical_devices_inner2<I>(instance: &Instance, info: I) -> Vec<PhysicalDeviceInfos>
|
||||
where
|
||||
I: IntoIterator<Item = (ash::vk::PhysicalDevice, DeviceExtensions)>,
|
||||
{
|
||||
let fns = instance.fns();
|
||||
let mut output = Vec::with_capacity(physical_devices.len());
|
||||
|
||||
for device in physical_devices.into_iter() {
|
||||
info.into_iter()
|
||||
.map(|(physical_device, supported_extensions)| {
|
||||
let mut extended_properties = PhysicalDeviceExtendedProperties::empty();
|
||||
|
||||
let properties: ash::vk::PhysicalDeviceProperties = unsafe {
|
||||
@ -145,10 +185,10 @@ fn init_physical_devices_inner2(
|
||||
|
||||
if instance.api_version() >= Version::V1_1 {
|
||||
fns.v1_1
|
||||
.get_physical_device_properties2(device, &mut output);
|
||||
.get_physical_device_properties2(physical_device, &mut output);
|
||||
} else {
|
||||
fns.khr_get_physical_device_properties2
|
||||
.get_physical_device_properties2_khr(device, &mut output);
|
||||
.get_physical_device_properties2_khr(physical_device, &mut output);
|
||||
}
|
||||
|
||||
extended_properties = PhysicalDeviceExtendedProperties {
|
||||
@ -169,14 +209,14 @@ fn init_physical_devices_inner2(
|
||||
|
||||
if instance.api_version() >= Version::V1_1 {
|
||||
fns.v1_1.get_physical_device_queue_family_properties2(
|
||||
device,
|
||||
physical_device,
|
||||
&mut num,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
} else {
|
||||
fns.khr_get_physical_device_properties2
|
||||
.get_physical_device_queue_family_properties2_khr(
|
||||
device,
|
||||
physical_device,
|
||||
&mut num,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
@ -186,14 +226,14 @@ fn init_physical_devices_inner2(
|
||||
|
||||
if instance.api_version() >= Version::V1_1 {
|
||||
fns.v1_1.get_physical_device_queue_family_properties2(
|
||||
device,
|
||||
physical_device,
|
||||
&mut num,
|
||||
families.as_mut_ptr(),
|
||||
);
|
||||
} else {
|
||||
fns.khr_get_physical_device_properties2
|
||||
.get_physical_device_queue_family_properties2_khr(
|
||||
device,
|
||||
physical_device,
|
||||
&mut num,
|
||||
families.as_mut_ptr(),
|
||||
);
|
||||
@ -210,10 +250,10 @@ fn init_physical_devices_inner2(
|
||||
|
||||
if instance.api_version() >= Version::V1_1 {
|
||||
fns.v1_1
|
||||
.get_physical_device_memory_properties2(device, &mut output);
|
||||
.get_physical_device_memory_properties2(physical_device, &mut output);
|
||||
} else {
|
||||
fns.khr_get_physical_device_properties2
|
||||
.get_physical_device_memory_properties2_khr(device, &mut output);
|
||||
.get_physical_device_memory_properties2_khr(physical_device, &mut output);
|
||||
}
|
||||
|
||||
output.memory_properties
|
||||
@ -221,36 +261,37 @@ fn init_physical_devices_inner2(
|
||||
|
||||
let available_features: Features = unsafe {
|
||||
let max_api_version = instance.max_api_version();
|
||||
let api_version = std::cmp::min(max_api_version, Version::from(properties.api_version));
|
||||
let api_version =
|
||||
std::cmp::min(max_api_version, Version::from(properties.api_version));
|
||||
|
||||
let mut output = FeaturesFfi::default();
|
||||
output.make_chain(api_version);
|
||||
output.make_chain(api_version, &supported_extensions);
|
||||
|
||||
if instance.api_version() >= Version::V1_1 {
|
||||
fns.v1_1
|
||||
.get_physical_device_features2(device, output.head_as_mut());
|
||||
.get_physical_device_features2(physical_device, output.head_as_mut());
|
||||
} else {
|
||||
fns.khr_get_physical_device_properties2
|
||||
.get_physical_device_features2_khr(device, output.head_as_mut());
|
||||
.get_physical_device_features2_khr(physical_device, output.head_as_mut());
|
||||
}
|
||||
|
||||
Features::from(&output)
|
||||
};
|
||||
|
||||
output.push(PhysicalDeviceInfos {
|
||||
device,
|
||||
PhysicalDeviceInfos {
|
||||
physical_device,
|
||||
properties,
|
||||
extended_properties,
|
||||
memory,
|
||||
queue_families,
|
||||
available_features,
|
||||
});
|
||||
}
|
||||
output
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(super) struct PhysicalDeviceInfos {
|
||||
device: ash::vk::PhysicalDevice,
|
||||
physical_device: ash::vk::PhysicalDevice,
|
||||
properties: ash::vk::PhysicalDeviceProperties,
|
||||
extended_properties: PhysicalDeviceExtendedProperties,
|
||||
queue_families: Vec<ash::vk::QueueFamilyProperties>,
|
||||
@ -588,7 +629,7 @@ unsafe impl<'a> VulkanObject for PhysicalDevice<'a> {
|
||||
|
||||
#[inline]
|
||||
fn internal_object(&self) -> ash::vk::PhysicalDevice {
|
||||
self.infos().device
|
||||
self.infos().physical_device
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,11 @@ pub mod sampler;
|
||||
pub mod swapchain;
|
||||
pub mod sync;
|
||||
|
||||
mod autogen {
|
||||
// Generated by build.rs
|
||||
include!(concat!(env!("OUT_DIR"), "/autogen.rs"));
|
||||
}
|
||||
|
||||
/// Alternative to the `Deref` trait. Contrary to `Deref`, must always return the same object.
|
||||
pub unsafe trait SafeDeref: Deref {}
|
||||
unsafe impl<'a, T: ?Sized> SafeDeref for &'a T {}
|
||||
|
@ -291,7 +291,7 @@ impl<'a> DeviceMemoryBuilder<'a> {
|
||||
if !(export_handle_bits & ash::vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
|
||||
.is_empty()
|
||||
{
|
||||
if !self.device.loaded_extensions().ext_external_memory_dmabuf {
|
||||
if !self.device.loaded_extensions().ext_external_memory_dma_buf {
|
||||
return Err(DeviceMemoryAllocError::MissingExtension(
|
||||
"ext_external_memory_dmabuf",
|
||||
));
|
||||
@ -310,7 +310,7 @@ impl<'a> DeviceMemoryBuilder<'a> {
|
||||
if !(import_handle_bits & ash::vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
|
||||
.is_empty()
|
||||
{
|
||||
if !self.device.loaded_extensions().ext_external_memory_dmabuf {
|
||||
if !self.device.loaded_extensions().ext_external_memory_dma_buf {
|
||||
return Err(DeviceMemoryAllocError::MissingExtension(
|
||||
"ext_external_memory_dmabuf",
|
||||
));
|
||||
|
15238
vulkano/vk.xml
Normal file
15238
vulkano/vk.xml
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user