mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 06:45:23 +00:00
Add auto-generated device properties (#1605)
* 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 * Autogenerate device properties * Propagate bugfix in properties chain to features
This commit is contained in:
parent
bb4234df45
commit
0d5edc130d
@ -50,14 +50,13 @@ vulkano::impl_vertex!(Vertex, position);
|
||||
|
||||
fn main() {
|
||||
let required_extensions = vulkano_win::required_extensions();
|
||||
let instance =
|
||||
Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
|
||||
let instance = Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
|
||||
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
||||
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical.name(),
|
||||
physical.ty()
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
physical.properties().device_type.unwrap()
|
||||
);
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
|
@ -105,8 +105,9 @@ fn main() {
|
||||
let data: Vec<u8> = vec![3, 11, 7];
|
||||
let min_dynamic_align = device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.min_uniform_buffer_offset_alignment() as usize;
|
||||
.properties()
|
||||
.min_uniform_buffer_offset_alignment
|
||||
.unwrap() as usize;
|
||||
println!(
|
||||
"Minimum uniform buffer offset alignment: {}",
|
||||
min_dynamic_align
|
||||
|
@ -125,11 +125,11 @@ fn main() {
|
||||
// In this case we can find appropriate value in this table: https://vulkan.gpuinfo.org/
|
||||
// or just use fallback constant for simplicity, but failure to set proper
|
||||
// local size can lead to significant performance penalty.
|
||||
let (local_size_x, local_size_y) = match physical.extended_properties().subgroup_size() {
|
||||
let (local_size_x, local_size_y) = match physical.properties().subgroup_size {
|
||||
Some(subgroup_size) => {
|
||||
println!(
|
||||
"Subgroup size for '{}' device is {}",
|
||||
physical.name(),
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
subgroup_size
|
||||
);
|
||||
|
||||
|
@ -41,13 +41,12 @@ fn main() {
|
||||
// `triangle` example if you haven't done so yet.
|
||||
|
||||
let required_extensions = vulkano_win::required_extensions();
|
||||
let instance =
|
||||
Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
|
||||
let instance = Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
|
||||
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical.name(),
|
||||
physical.ty()
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
physical.properties().device_type.unwrap(),
|
||||
);
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
|
@ -65,13 +65,12 @@ impl_vertex!(Vertex, position);
|
||||
|
||||
fn main() {
|
||||
let required_extensions = vulkano_win::required_extensions();
|
||||
let instance =
|
||||
Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
|
||||
let instance = Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
|
||||
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical.name(),
|
||||
physical.ty()
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
physical.properties().device_type.unwrap(),
|
||||
);
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
|
@ -68,8 +68,8 @@ fn main() {
|
||||
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical.name(),
|
||||
physical.ty()
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
physical.properties().device_type.unwrap(),
|
||||
);
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
|
@ -62,12 +62,7 @@ fn main() {
|
||||
// will lead to a runtime error when creating the `RenderPass`.
|
||||
// The `max_multiview_view_count` function will return `None`
|
||||
// when the `VK_KHR_get_physical_device_properties2` instance extension has not been enabled.
|
||||
if physical
|
||||
.extended_properties()
|
||||
.max_multiview_view_count()
|
||||
.unwrap_or(0)
|
||||
< 2
|
||||
{
|
||||
if physical.properties().max_multiview_view_count.unwrap_or(0) < 2 {
|
||||
println!("The device doesn't support two multiview views or the VK_KHR_get_physical_device_properties2 instance extension has not been loaded");
|
||||
|
||||
// A real application should probably fall back to rendering the framebuffer layers
|
||||
|
@ -36,14 +36,13 @@ use winit::window::{Window, WindowBuilder};
|
||||
|
||||
fn main() {
|
||||
let required_extensions = vulkano_win::required_extensions();
|
||||
let instance =
|
||||
Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
|
||||
let instance = Instance::new(None, Version::V1_1, &required_extensions, None).unwrap();
|
||||
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
||||
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical.name(),
|
||||
physical.ty()
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
physical.properties().device_type.unwrap(),
|
||||
);
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
|
@ -48,8 +48,8 @@ fn main() {
|
||||
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical.name(),
|
||||
physical.ty()
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
physical.properties().device_type.unwrap(),
|
||||
);
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
|
@ -142,8 +142,8 @@ fn main() {
|
||||
let physical = PhysicalDevice::enumerate(&instance).next().unwrap();
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical.name(),
|
||||
physical.ty()
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
physical.properties().device_type.unwrap()
|
||||
);
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
|
@ -70,8 +70,8 @@ fn main() {
|
||||
// Some little debug infos.
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical.name(),
|
||||
physical.ty()
|
||||
physical.properties().device_name.as_ref().unwrap(),
|
||||
physical.properties().device_type.unwrap(),
|
||||
);
|
||||
|
||||
// The objective of this example is to draw a triangle on a window. To do so, we first need to
|
||||
|
@ -240,7 +240,7 @@ where
|
||||
.unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"\n\t\trequires_extensions: [{}],",
|
||||
"\n\t\trequires_instance_extensions: [{}],",
|
||||
ext.requires_instance_extensions.join(", ")
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -8,12 +8,13 @@
|
||||
// according to those terms.
|
||||
|
||||
use heck::SnakeCase;
|
||||
use indexmap::IndexMap;
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
io::Write,
|
||||
};
|
||||
use vk_parse::{Type, TypeMember, TypeMemberMarkup, TypeSpec};
|
||||
use vk_parse::{Extension, 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] {
|
||||
@ -57,10 +58,14 @@ fn required_by_extensions(name: &str) -> &'static [&'static str] {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(writer: &mut W, types: &HashMap<&str, (&Type, Vec<&str>)>) {
|
||||
pub fn write<W: Write>(
|
||||
writer: &mut W,
|
||||
types: &HashMap<&str, (&Type, Vec<&str>)>,
|
||||
extensions: &IndexMap<&str, &Extension>,
|
||||
) {
|
||||
write!(writer, "crate::device::features::features! {{").unwrap();
|
||||
|
||||
for feat in make_vulkano_features(&types) {
|
||||
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();
|
||||
@ -93,11 +98,11 @@ pub fn write<W: Write>(writer: &mut W, types: &HashMap<&str, (&Type, Vec<&str>)>
|
||||
|
||||
write!(
|
||||
writer,
|
||||
"\n}}\n\ncrate::device::features::features_ffi! {{\n\tapi_version,\n\textensions,"
|
||||
"\n}}\n\ncrate::device::features::features_ffi! {{\n\tapi_version,\n\tdevice_extensions,\n\tinstance_extensions,"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for ffi in make_vulkano_features_ffi(&types) {
|
||||
for ffi in make_vulkano_features_ffi(types, extensions) {
|
||||
write!(writer, "\n\t{} => {{", ffi.member).unwrap();
|
||||
write!(writer, "\n\t\tty: {},", ffi.ty).unwrap();
|
||||
write!(
|
||||
@ -258,7 +263,10 @@ struct VulkanoFeatureFfi {
|
||||
conflicts: Vec<String>,
|
||||
}
|
||||
|
||||
fn make_vulkano_features_ffi(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<VulkanoFeatureFfi> {
|
||||
fn make_vulkano_features_ffi<'a>(
|
||||
types: &'a HashMap<&str, (&Type, Vec<&str>)>,
|
||||
extensions: &IndexMap<&'a str, &Extension>,
|
||||
) -> Vec<VulkanoFeatureFfi> {
|
||||
let mut feature_included_in: HashMap<&str, Vec<&str>> = HashMap::new();
|
||||
sorted_structs(types)
|
||||
.into_iter()
|
||||
@ -271,7 +279,8 @@ fn make_vulkano_features_ffi(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<V
|
||||
format!("api_version >= crate::Version::V{}", version)
|
||||
} else {
|
||||
format!(
|
||||
"extensions.{}",
|
||||
"{}_extensions.{}",
|
||||
extensions[provided_by].ext_type.as_ref().unwrap().as_str(),
|
||||
provided_by
|
||||
.strip_prefix("VK_")
|
||||
.unwrap()
|
||||
|
@ -17,6 +17,7 @@ use vk_parse::{
|
||||
mod extensions;
|
||||
mod features;
|
||||
mod fns;
|
||||
mod properties;
|
||||
|
||||
pub fn write<W: Write>(writer: &mut W) {
|
||||
let registry = get_registry("vk.xml");
|
||||
@ -40,7 +41,9 @@ pub fn write<W: Write>(writer: &mut W) {
|
||||
write!(writer, "\n\n").unwrap();
|
||||
fns::write(writer, &extensions);
|
||||
write!(writer, "\n\n").unwrap();
|
||||
features::write(writer, &types);
|
||||
features::write(writer, &types, &extensions);
|
||||
write!(writer, "\n\n").unwrap();
|
||||
properties::write(writer, &types, &extensions);
|
||||
write!(writer, "\n").unwrap();
|
||||
}
|
||||
|
||||
|
349
vulkano/autogen/properties.rs
Normal file
349
vulkano/autogen/properties.rs
Normal file
@ -0,0 +1,349 @@
|
||||
// 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 regex::Regex;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap},
|
||||
io::Write,
|
||||
};
|
||||
use vk_parse::{Extension, Type, TypeMember, TypeMemberMarkup, TypeSpec};
|
||||
|
||||
pub fn write<W: Write>(
|
||||
writer: &mut W,
|
||||
types: &HashMap<&str, (&Type, Vec<&str>)>,
|
||||
extensions: &IndexMap<&str, &Extension>,
|
||||
) {
|
||||
write!(writer, "crate::device::properties::properties! {{").unwrap();
|
||||
|
||||
for feat in make_vulkano_properties(&types) {
|
||||
write!(writer, "\n\t{} => {{", feat.member).unwrap();
|
||||
write_doc(writer, &feat);
|
||||
write!(writer, "\n\t\tty: {},", feat.ty).unwrap();
|
||||
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}},").unwrap();
|
||||
}
|
||||
|
||||
write!(
|
||||
writer,
|
||||
"\n}}\n\ncrate::device::properties::properties_ffi! {{\n\tapi_version,\n\tdevice_extensions,\n\tinstance_extensions,"
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for ffi in make_vulkano_properties_ffi(types, extensions) {
|
||||
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 VulkanoProperty {
|
||||
member: String,
|
||||
ty: String,
|
||||
vulkan_doc: String,
|
||||
ffi_name: String,
|
||||
ffi_members: Vec<String>,
|
||||
}
|
||||
|
||||
fn make_vulkano_properties(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<VulkanoProperty> {
|
||||
let mut properties = HashMap::new();
|
||||
std::array::IntoIter::new([
|
||||
&types["VkPhysicalDeviceProperties"],
|
||||
&types["VkPhysicalDeviceLimits"],
|
||||
&types["VkPhysicalDeviceSparseProperties"],
|
||||
])
|
||||
.chain(sorted_structs(types).into_iter())
|
||||
.filter(|(ty, _)| {
|
||||
let name = ty.name.as_ref().map(|s| s.as_str());
|
||||
name == Some("VkPhysicalDeviceProperties")
|
||||
|| name == Some("VkPhysicalDeviceLimits")
|
||||
|| name == Some("VkPhysicalDeviceSparseProperties")
|
||||
|| ty.structextends.as_ref().map(|s| s.as_str()) == Some("VkPhysicalDeviceProperties2")
|
||||
})
|
||||
.for_each(|(ty, _)| {
|
||||
let vulkan_ty_name = ty.name.as_ref().unwrap();
|
||||
|
||||
let ty_name = if vulkan_ty_name == "VkPhysicalDeviceProperties" {
|
||||
"properties_vulkan10.properties".to_owned()
|
||||
} else if vulkan_ty_name == "VkPhysicalDeviceLimits" {
|
||||
"properties_vulkan10.properties.limits".to_owned()
|
||||
} else if vulkan_ty_name == "VkPhysicalDeviceSparseProperties" {
|
||||
"properties_vulkan10.properties.sparse_properties".to_owned()
|
||||
} else {
|
||||
ffi_member(vulkan_ty_name)
|
||||
};
|
||||
|
||||
members(ty)
|
||||
.into_iter()
|
||||
.for_each(|Member { name, ty, len }| {
|
||||
if ty == "VkPhysicalDeviceLimits" || ty == "VkPhysicalDeviceSparseProperties" {
|
||||
return;
|
||||
}
|
||||
|
||||
let vulkano_member = name.to_snake_case();
|
||||
let vulkano_ty = match name {
|
||||
"apiVersion" => "crate::Version",
|
||||
_ => vulkano_type(ty, len),
|
||||
};
|
||||
match properties.entry(vulkano_member.clone()) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(VulkanoProperty {
|
||||
member: vulkano_member.clone(),
|
||||
ty: vulkano_ty.to_owned(),
|
||||
vulkan_doc: format!("{}.html#limits-{}", vulkan_ty_name, name),
|
||||
ffi_name: vulkano_member,
|
||||
ffi_members: vec![ty_name.to_owned()],
|
||||
});
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
entry.into_mut().ffi_members.push(ty_name.to_owned());
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
let mut names: Vec<_> = properties
|
||||
.values()
|
||||
.map(|feat| feat.member.clone())
|
||||
.collect();
|
||||
names.sort_unstable();
|
||||
names
|
||||
.into_iter()
|
||||
.map(|name| properties.remove(&name).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn write_doc<W>(writer: &mut W, feat: &VulkanoProperty)
|
||||
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();
|
||||
write!(writer, "\n\t\t\",").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct VulkanoPropertyFfi {
|
||||
member: String,
|
||||
ty: String,
|
||||
provided_by: Vec<String>,
|
||||
conflicts: Vec<String>,
|
||||
}
|
||||
|
||||
fn make_vulkano_properties_ffi<'a>(
|
||||
types: &'a HashMap<&str, (&Type, Vec<&str>)>,
|
||||
extensions: &IndexMap<&'a str, &Extension>,
|
||||
) -> Vec<VulkanoPropertyFfi> {
|
||||
let mut property_included_in: HashMap<&str, Vec<&str>> = HashMap::new();
|
||||
sorted_structs(types)
|
||||
.into_iter()
|
||||
.map(|(ty, provided_by)| {
|
||||
let ty_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.{}",
|
||||
extensions[provided_by].ext_type.as_ref().unwrap().as_str(),
|
||||
provided_by
|
||||
.strip_prefix("VK_")
|
||||
.unwrap()
|
||||
.to_ascii_lowercase()
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut conflicts = vec![];
|
||||
members(ty).into_iter().for_each(|Member { name, .. }| {
|
||||
match property_included_in.entry(name) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(vec![ty_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(ty_name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
VulkanoPropertyFfi {
|
||||
member: ffi_member(ty_name),
|
||||
ty: 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("VkPhysicalDeviceProperties2")
|
||||
})
|
||||
.collect();
|
||||
let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Properties$").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("_properties").unwrap();
|
||||
format!("properties_{}{}", base, suffix)
|
||||
}
|
||||
|
||||
struct Member<'a> {
|
||||
name: &'a str,
|
||||
ty: &'a str,
|
||||
len: Option<&'a str>,
|
||||
}
|
||||
|
||||
fn members(ty: &Type) -> Vec<Member> {
|
||||
let regex = Regex::new(r"\[([A-Za-z0-9_]+)\]\s*$").unwrap();
|
||||
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,
|
||||
});
|
||||
let ty = def.markup.iter().find_map(|markup| match markup {
|
||||
TypeMemberMarkup::Type(ty) => Some(ty.as_str()),
|
||||
_ => None,
|
||||
});
|
||||
let len = def
|
||||
.markup
|
||||
.iter()
|
||||
.find_map(|markup| match markup {
|
||||
TypeMemberMarkup::Enum(len) => Some(len.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.or_else(|| {
|
||||
regex
|
||||
.captures(&def.code)
|
||||
.and_then(|cap| cap.get(1))
|
||||
.map(|m| m.as_str())
|
||||
});
|
||||
if name != Some("sType") && name != Some("pNext") {
|
||||
return name.map(|name| Member {
|
||||
name,
|
||||
ty: ty.unwrap(),
|
||||
len,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
fn vulkano_type(ty: &str, len: Option<&str>) -> &'static str {
|
||||
if let Some(len) = len {
|
||||
match ty {
|
||||
"char" => "String",
|
||||
"uint8_t" if len == "VK_LUID_SIZE" => "[u8; 8]",
|
||||
"uint8_t" if len == "VK_UUID_SIZE" => "[u8; 16]",
|
||||
"uint32_t" if len == "2" => "[u32; 2]",
|
||||
"uint32_t" if len == "3" => "[u32; 3]",
|
||||
"float" if len == "2" => "[f32; 2]",
|
||||
_ => unimplemented!("{}[{}]", ty, len),
|
||||
}
|
||||
} else {
|
||||
match ty {
|
||||
"float" => "f32",
|
||||
"int32_t" => "i32",
|
||||
"size_t" => "usize",
|
||||
"uint8_t" => "u8",
|
||||
"uint32_t" => "u32",
|
||||
"uint64_t" => "u64",
|
||||
"VkBool32" => "bool",
|
||||
"VkConformanceVersion" => "crate::instance::ConformanceVersion",
|
||||
"VkDeviceSize" => "u64",
|
||||
"VkDriverId" => "crate::instance::DriverId",
|
||||
"VkExtent2D" => "[u32; 2]",
|
||||
"VkPhysicalDeviceType" => "crate::instance::PhysicalDeviceType",
|
||||
"VkPointClippingBehavior" => "crate::instance::PointClippingBehavior",
|
||||
"VkResolveModeFlags" => "crate::render_pass::ResolveModes",
|
||||
"VkSampleCountFlags" => "crate::image::SampleCounts",
|
||||
"VkSampleCountFlagBits" => "crate::image::SampleCount",
|
||||
"VkShaderCorePropertiesFlagsAMD" => "crate::instance::ShaderCoreProperties",
|
||||
"VkShaderFloatControlsIndependence" => {
|
||||
"crate::instance::ShaderFloatControlsIndependence"
|
||||
}
|
||||
"VkShaderStageFlags" => "crate::descriptor::descriptor::ShaderStages",
|
||||
"VkSubgroupFeatureFlags" => "crate::instance::SubgroupFeatures",
|
||||
_ => unimplemented!("{}", ty),
|
||||
}
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ fn main() {
|
||||
}
|
||||
|
||||
// Write autogen.rs
|
||||
println!("cargo:rerun-if-changed=vk.xml");
|
||||
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);
|
||||
|
@ -468,16 +468,18 @@ where
|
||||
if self.usage.uniform_buffer {
|
||||
self.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.min_uniform_buffer_offset_alignment() as usize
|
||||
.properties()
|
||||
.min_uniform_buffer_offset_alignment
|
||||
.unwrap() as usize
|
||||
} else {
|
||||
1
|
||||
},
|
||||
if self.usage.storage_buffer {
|
||||
self.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.min_storage_buffer_offset_alignment() as usize
|
||||
.properties()
|
||||
.min_storage_buffer_offset_alignment
|
||||
.unwrap() as usize
|
||||
} else {
|
||||
1
|
||||
},
|
||||
|
@ -214,25 +214,25 @@ impl UnsafeBuffer {
|
||||
};
|
||||
|
||||
// We have to manually enforce some additional requirements for some buffer types.
|
||||
let limits = device.physical_device().limits();
|
||||
let properties = device.physical_device().properties();
|
||||
if usage.uniform_texel_buffer || usage.storage_texel_buffer {
|
||||
output.alignment = align(
|
||||
output.alignment,
|
||||
limits.min_texel_buffer_offset_alignment() as usize,
|
||||
properties.min_texel_buffer_offset_alignment.unwrap() as usize,
|
||||
);
|
||||
}
|
||||
|
||||
if usage.storage_buffer {
|
||||
output.alignment = align(
|
||||
output.alignment,
|
||||
limits.min_storage_buffer_offset_alignment() as usize,
|
||||
properties.min_storage_buffer_offset_alignment.unwrap() as usize,
|
||||
);
|
||||
}
|
||||
|
||||
if usage.uniform_buffer {
|
||||
output.alignment = align(
|
||||
output.alignment,
|
||||
limits.min_uniform_buffer_offset_alignment() as usize,
|
||||
properties.min_uniform_buffer_offset_alignment.unwrap() as usize,
|
||||
);
|
||||
}
|
||||
|
||||
@ -270,15 +270,21 @@ impl UnsafeBuffer {
|
||||
|
||||
// Check for alignment correctness.
|
||||
{
|
||||
let limits = self.device().physical_device().limits();
|
||||
let properties = self.device().physical_device().properties();
|
||||
if self.usage().uniform_texel_buffer || self.usage().storage_texel_buffer {
|
||||
debug_assert!(offset % limits.min_texel_buffer_offset_alignment() as usize == 0);
|
||||
debug_assert!(
|
||||
offset % properties.min_texel_buffer_offset_alignment.unwrap() as usize == 0
|
||||
);
|
||||
}
|
||||
if self.usage().storage_buffer {
|
||||
debug_assert!(offset % limits.min_storage_buffer_offset_alignment() as usize == 0);
|
||||
debug_assert!(
|
||||
offset % properties.min_storage_buffer_offset_alignment.unwrap() as usize == 0
|
||||
);
|
||||
}
|
||||
if self.usage().uniform_buffer {
|
||||
debug_assert!(offset % limits.min_uniform_buffer_offset_alignment() as usize == 0);
|
||||
debug_assert!(
|
||||
offset % properties.min_uniform_buffer_offset_alignment.unwrap() as usize == 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,8 +96,9 @@ where
|
||||
if (offset
|
||||
% device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.min_texel_buffer_offset_alignment() as usize)
|
||||
.properties()
|
||||
.min_texel_buffer_offset_alignment
|
||||
.unwrap() as usize)
|
||||
!= 0
|
||||
{
|
||||
return Err(BufferViewCreationError::WrongBufferAlignment);
|
||||
@ -114,8 +115,9 @@ where
|
||||
.expect("Can't use a compressed format for buffer views");
|
||||
let l = device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_texel_buffer_elements();
|
||||
.properties()
|
||||
.max_texel_buffer_elements
|
||||
.unwrap();
|
||||
if nb > l as usize {
|
||||
return Err(BufferViewCreationError::MaxTexelBufferElementsExceeded);
|
||||
}
|
||||
|
@ -1218,7 +1218,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
|
||||
///
|
||||
/// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`.
|
||||
/// The maximum number of draw commands in the buffer is limited by the
|
||||
/// [`max_draw_indirect_count`](crate::instance::Limits::max_draw_indirect_count) limit.
|
||||
/// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit.
|
||||
/// This limit is 1 unless the
|
||||
/// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been
|
||||
/// enabled.
|
||||
@ -1264,8 +1264,9 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
|
||||
let limit = self
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_draw_indirect_count();
|
||||
.properties()
|
||||
.max_draw_indirect_count
|
||||
.unwrap();
|
||||
|
||||
if requested > limit {
|
||||
return Err(
|
||||
@ -1397,7 +1398,7 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
|
||||
///
|
||||
/// One draw is performed for each [`DrawIndirectCommand`] struct in `indirect_buffer`.
|
||||
/// The maximum number of draw commands in the buffer is limited by the
|
||||
/// [`max_draw_indirect_count`](crate::instance::Limits::max_draw_indirect_count) limit.
|
||||
/// [`max_draw_indirect_count`](crate::device::Properties::max_draw_indirect_count) limit.
|
||||
/// This limit is 1 unless the
|
||||
/// [`multi_draw_indirect`](crate::device::Features::multi_draw_indirect) feature has been
|
||||
/// enabled.
|
||||
@ -1448,8 +1449,9 @@ impl<L, P> AutoCommandBufferBuilder<L, P> {
|
||||
let limit = self
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_draw_indirect_count();
|
||||
.properties()
|
||||
.max_draw_indirect_count
|
||||
.unwrap();
|
||||
|
||||
if requested > limit {
|
||||
return Err(
|
||||
@ -2197,9 +2199,9 @@ where
|
||||
// Ensure that the number of dynamic_offsets is correct and that each
|
||||
// dynamic offset is a multiple of the minimum offset alignment specified
|
||||
// by the physical device.
|
||||
let limits = pipeline_layout.device().physical_device().limits();
|
||||
let min_uniform_off_align = limits.min_uniform_buffer_offset_alignment() as u32;
|
||||
let min_storage_off_align = limits.min_storage_buffer_offset_alignment() as u32;
|
||||
let properties = pipeline_layout.device().physical_device().properties();
|
||||
let min_uniform_off_align = properties.min_uniform_buffer_offset_alignment.unwrap() as u32;
|
||||
let min_storage_off_align = properties.min_storage_buffer_offset_alignment.unwrap() as u32;
|
||||
let mut dynamic_offset_index = 0;
|
||||
for set in &sets {
|
||||
for desc_index in 0..set.num_bindings() {
|
||||
|
@ -428,8 +428,9 @@ impl UnsafeCommandBufferBuilder {
|
||||
let max_bindings = self
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_bindings();
|
||||
.properties()
|
||||
.max_vertex_input_bindings
|
||||
.unwrap();
|
||||
first_binding + num_bindings <= max_bindings
|
||||
});
|
||||
|
||||
@ -1018,8 +1019,9 @@ impl UnsafeCommandBufferBuilder {
|
||||
let max_group_counts = self
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_compute_work_group_count();
|
||||
.properties()
|
||||
.max_compute_work_group_count
|
||||
.unwrap();
|
||||
group_counts[0] <= max_group_counts[0]
|
||||
&& group_counts[1] <= max_group_counts[1]
|
||||
&& group_counts[2] <= max_group_counts[2]
|
||||
@ -1412,7 +1414,12 @@ impl UnsafeCommandBufferBuilder {
|
||||
|| self.device().enabled_features().multi_viewport
|
||||
);
|
||||
debug_assert!({
|
||||
let max = self.device().physical_device().limits().max_viewports();
|
||||
let max = self
|
||||
.device()
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_viewports
|
||||
.unwrap();
|
||||
first_scissor + scissors.len() as u32 <= max
|
||||
});
|
||||
|
||||
@ -1442,7 +1449,12 @@ impl UnsafeCommandBufferBuilder {
|
||||
|| self.device().enabled_features().multi_viewport
|
||||
);
|
||||
debug_assert!({
|
||||
let max = self.device().physical_device().limits().max_viewports();
|
||||
let max = self
|
||||
.device()
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_viewports
|
||||
.unwrap();
|
||||
first_viewport + viewports.len() as u32 <= max
|
||||
});
|
||||
|
||||
|
@ -16,8 +16,9 @@ use crate::device::Device;
|
||||
pub fn check_dispatch(device: &Device, dimensions: [u32; 3]) -> Result<(), CheckDispatchError> {
|
||||
let max = device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_compute_work_group_count();
|
||||
.properties()
|
||||
.max_compute_work_group_count
|
||||
.unwrap();
|
||||
|
||||
if dimensions[0] > max[0] || dimensions[1] > max[1] || dimensions[2] > max[2] {
|
||||
return Err(CheckDispatchError::UnsupportedDimensions {
|
||||
@ -71,8 +72,9 @@ mod tests {
|
||||
// Just in case the device is some kind of software implementation.
|
||||
if device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_compute_work_group_count()
|
||||
.properties()
|
||||
.max_compute_work_group_count
|
||||
.unwrap()
|
||||
== attempted
|
||||
{
|
||||
return;
|
||||
|
@ -46,8 +46,8 @@ where
|
||||
let max_instance_index = pipeline
|
||||
.device()
|
||||
.physical_device()
|
||||
.extended_properties()
|
||||
.max_multiview_instance_index()
|
||||
.properties()
|
||||
.max_multiview_instance_index
|
||||
.unwrap_or(0) as usize;
|
||||
|
||||
// vulkano currently always uses `0` as the first instance which means the highest
|
||||
|
@ -754,7 +754,7 @@ impl ShaderStages {
|
||||
|
||||
impl From<ShaderStages> for ash::vk::ShaderStageFlags {
|
||||
#[inline]
|
||||
fn from(val: ShaderStages) -> ash::vk::ShaderStageFlags {
|
||||
fn from(val: ShaderStages) -> Self {
|
||||
let mut result = ash::vk::ShaderStageFlags::empty();
|
||||
if val.vertex {
|
||||
result |= ash::vk::ShaderStageFlags::VERTEX;
|
||||
@ -778,6 +778,21 @@ impl From<ShaderStages> for ash::vk::ShaderStageFlags {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ash::vk::ShaderStageFlags> for ShaderStages {
|
||||
#[inline]
|
||||
fn from(val: ash::vk::ShaderStageFlags) -> Self {
|
||||
Self {
|
||||
vertex: val.intersects(ash::vk::ShaderStageFlags::VERTEX),
|
||||
tessellation_control: val.intersects(ash::vk::ShaderStageFlags::TESSELLATION_CONTROL),
|
||||
tessellation_evaluation: val
|
||||
.intersects(ash::vk::ShaderStageFlags::TESSELLATION_EVALUATION),
|
||||
geometry: val.intersects(ash::vk::ShaderStageFlags::GEOMETRY),
|
||||
fragment: val.intersects(ash::vk::ShaderStageFlags::FRAGMENT),
|
||||
compute: val.intersects(ash::vk::ShaderStageFlags::COMPUTE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for ShaderStages {
|
||||
type Output = ShaderStages;
|
||||
|
||||
|
@ -950,16 +950,18 @@ impl DescriptorWrite {
|
||||
% buffer
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.min_uniform_buffer_offset_alignment() as usize,
|
||||
.properties()
|
||||
.min_uniform_buffer_offset_alignment
|
||||
.unwrap() as usize,
|
||||
0
|
||||
);
|
||||
debug_assert!(
|
||||
size <= buffer
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_uniform_buffer_range() as usize
|
||||
.properties()
|
||||
.max_uniform_buffer_range
|
||||
.unwrap() as usize
|
||||
);
|
||||
|
||||
DescriptorWrite {
|
||||
@ -984,16 +986,18 @@ impl DescriptorWrite {
|
||||
% buffer
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.min_storage_buffer_offset_alignment() as usize,
|
||||
.properties()
|
||||
.min_storage_buffer_offset_alignment
|
||||
.unwrap() as usize,
|
||||
0
|
||||
);
|
||||
debug_assert!(
|
||||
size <= buffer
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_storage_buffer_range() as usize
|
||||
.properties()
|
||||
.max_storage_buffer_range
|
||||
.unwrap() as usize
|
||||
);
|
||||
|
||||
DescriptorWrite {
|
||||
@ -1022,16 +1026,18 @@ impl DescriptorWrite {
|
||||
% buffer
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.min_uniform_buffer_offset_alignment() as usize,
|
||||
.properties()
|
||||
.min_uniform_buffer_offset_alignment
|
||||
.unwrap() as usize,
|
||||
0
|
||||
);
|
||||
debug_assert!(
|
||||
size <= buffer
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_uniform_buffer_range() as usize
|
||||
.properties()
|
||||
.max_uniform_buffer_range
|
||||
.unwrap() as usize
|
||||
);
|
||||
|
||||
DescriptorWrite {
|
||||
@ -1062,16 +1068,18 @@ impl DescriptorWrite {
|
||||
% buffer
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.min_storage_buffer_offset_alignment() as usize,
|
||||
.properties()
|
||||
.min_storage_buffer_offset_alignment
|
||||
.unwrap() as usize,
|
||||
0
|
||||
);
|
||||
debug_assert!(
|
||||
size <= buffer
|
||||
.device()
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_storage_buffer_range() as usize
|
||||
.properties()
|
||||
.max_storage_buffer_range
|
||||
.unwrap() as usize
|
||||
);
|
||||
|
||||
DescriptorWrite {
|
||||
|
@ -179,7 +179,7 @@ macro_rules! features {
|
||||
}
|
||||
|
||||
pub use crate::autogen::Features;
|
||||
pub(crate) use {crate::autogen::FeaturesFfi, features};
|
||||
pub(crate) use features;
|
||||
|
||||
/// An error that can happen when enabling a feature on a device.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@ -238,7 +238,8 @@ impl fmt::Display for FeatureRestriction {
|
||||
macro_rules! features_ffi {
|
||||
{
|
||||
$api_version:ident,
|
||||
$extensions:ident,
|
||||
$device_extensions:ident,
|
||||
$instance_extensions:ident,
|
||||
$($member:ident => {
|
||||
ty: $ty:ident,
|
||||
provided_by: [$($provided_by:expr),+],
|
||||
@ -255,14 +256,22 @@ macro_rules! features_ffi {
|
||||
}
|
||||
|
||||
impl FeaturesFfi {
|
||||
pub(crate) fn make_chain(&mut self, $api_version: crate::Version, $extensions: &DeviceExtensions) {
|
||||
pub(crate) fn make_chain(
|
||||
&mut self,
|
||||
$api_version: crate::Version,
|
||||
$device_extensions: &DeviceExtensions,
|
||||
$instance_extensions: &InstanceExtensions,
|
||||
) {
|
||||
self.features_vulkan10 = Some(Default::default());
|
||||
let head = self.features_vulkan10.as_mut().unwrap();
|
||||
|
||||
$(
|
||||
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 _;
|
||||
let member = self.$member.as_mut().unwrap();
|
||||
member.p_next = head.p_next;
|
||||
head.p_next = member as *mut _ as _;
|
||||
}
|
||||
)+
|
||||
}
|
||||
@ -278,4 +287,4 @@ macro_rules! features_ffi {
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use features_ffi;
|
||||
pub(crate) use {crate::autogen::FeaturesFfi, features_ffi};
|
||||
|
@ -92,6 +92,8 @@
|
||||
|
||||
pub(crate) use self::features::FeaturesFfi;
|
||||
pub use self::features::{FeatureRestriction, FeatureRestrictionError, Features};
|
||||
pub use self::properties::Properties;
|
||||
pub(crate) use self::properties::PropertiesFfi;
|
||||
pub use crate::autogen::DeviceExtensions;
|
||||
use crate::check_errors;
|
||||
use crate::command_buffer::pool::StandardCommandPool;
|
||||
@ -138,6 +140,7 @@ use std::sync::Weak;
|
||||
|
||||
pub(crate) mod extensions;
|
||||
pub(crate) mod features;
|
||||
pub(crate) mod properties;
|
||||
|
||||
/// Represents a Vulkan context.
|
||||
pub struct Device {
|
||||
@ -200,9 +203,7 @@ impl Device {
|
||||
{
|
||||
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, physical_device.api_version());
|
||||
let api_version = physical_device.api_version();
|
||||
|
||||
// Check if the extensions are correct
|
||||
requested_extensions.check_requirements(
|
||||
@ -286,7 +287,11 @@ impl Device {
|
||||
.collect::<SmallVec<[_; 16]>>();
|
||||
|
||||
let mut features_ffi = FeaturesFfi::default();
|
||||
features_ffi.make_chain(api_version, requested_extensions);
|
||||
features_ffi.make_chain(
|
||||
api_version,
|
||||
requested_extensions,
|
||||
instance.loaded_extensions(),
|
||||
);
|
||||
features_ffi.write(&requested_features);
|
||||
|
||||
// Device layers were deprecated in Vulkan 1.0.13, and device layer requests should be
|
||||
|
282
vulkano/src/device/properties.rs
Normal file
282
vulkano/src/device/properties.rs
Normal file
@ -0,0 +1,282 @@
|
||||
use crate::descriptor::descriptor::ShaderStages;
|
||||
use crate::image::{SampleCount, SampleCounts};
|
||||
use crate::instance::{
|
||||
ConformanceVersion, DriverId, PhysicalDeviceType, PointClippingBehavior, ShaderCoreProperties,
|
||||
ShaderFloatControlsIndependence, SubgroupFeatures,
|
||||
};
|
||||
use crate::render_pass::ResolveModes;
|
||||
use crate::Version;
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::CStr;
|
||||
|
||||
macro_rules! properties {
|
||||
{
|
||||
$($member:ident => {
|
||||
doc: $doc:expr,
|
||||
ty: $ty:ty,
|
||||
ffi_name: $ffi_field:ident,
|
||||
ffi_members: [$($ffi_struct:ident $(.$ffi_struct_field:ident)*),+],
|
||||
},)*
|
||||
} => {
|
||||
/// Represents all the properties of a physical device.
|
||||
///
|
||||
/// Depending on the highest version of Vulkan supported by the physical device, and the
|
||||
/// available extensions, not every property may be available. For that reason, properties
|
||||
/// are wrapped in an `Option`.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Properties {
|
||||
$(
|
||||
#[doc = $doc]
|
||||
pub $member: Option<$ty>,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<&PropertiesFfi> for Properties {
|
||||
fn from(properties_ffi: &PropertiesFfi) -> Self {
|
||||
use crate::device::properties::FromVulkan;
|
||||
|
||||
Properties {
|
||||
$(
|
||||
$member: std::array::IntoIter::new([
|
||||
$(properties_ffi.$ffi_struct.map(|s| s$(.$ffi_struct_field)*.$ffi_field)),+
|
||||
]).flatten().next().and_then(|x| <$ty>::from_vulkan(x)),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use crate::autogen::Properties;
|
||||
pub(crate) use properties;
|
||||
|
||||
macro_rules! properties_ffi {
|
||||
{
|
||||
$api_version:ident,
|
||||
$device_extensions:ident,
|
||||
$instance_extensions:ident,
|
||||
$($member:ident => {
|
||||
ty: $ty:ident,
|
||||
provided_by: [$($provided_by:expr),+],
|
||||
conflicts: [$($conflicts:ident),*],
|
||||
},)+
|
||||
} => {
|
||||
#[derive(Default)]
|
||||
pub(crate) struct PropertiesFfi {
|
||||
properties_vulkan10: Option<ash::vk::PhysicalDeviceProperties2KHR>,
|
||||
|
||||
$(
|
||||
$member: Option<ash::vk::$ty>,
|
||||
)+
|
||||
}
|
||||
|
||||
impl PropertiesFfi {
|
||||
pub(crate) fn make_chain(
|
||||
&mut self,
|
||||
$api_version: crate::Version,
|
||||
$device_extensions: &crate::device::DeviceExtensions,
|
||||
$instance_extensions: &crate::instance::InstanceExtensions,
|
||||
) {
|
||||
self.properties_vulkan10 = Some(Default::default());
|
||||
let head = self.properties_vulkan10.as_mut().unwrap();
|
||||
|
||||
$(
|
||||
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());
|
||||
let member = self.$member.as_mut().unwrap();
|
||||
member.p_next = head.p_next;
|
||||
head.p_next = member as *mut _ as _;
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
||||
pub(crate) fn head_as_ref(&self) -> &ash::vk::PhysicalDeviceProperties2KHR {
|
||||
self.properties_vulkan10.as_ref().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn head_as_mut(&mut self) -> &mut ash::vk::PhysicalDeviceProperties2KHR {
|
||||
self.properties_vulkan10.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use {crate::autogen::PropertiesFfi, properties_ffi};
|
||||
|
||||
// A bit of a hack...
|
||||
pub(crate) trait FromVulkan<F>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
fn from_vulkan(val: F) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl FromVulkan<u8> for u8 {
|
||||
#[inline]
|
||||
fn from_vulkan(val: u8) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FromVulkan<[u8; N]> for [u8; N] {
|
||||
#[inline]
|
||||
fn from_vulkan(val: [u8; N]) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<u32> for u32 {
|
||||
#[inline]
|
||||
fn from_vulkan(val: u32) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FromVulkan<[u32; N]> for [u32; N] {
|
||||
#[inline]
|
||||
fn from_vulkan(val: [u32; N]) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<u64> for u64 {
|
||||
#[inline]
|
||||
fn from_vulkan(val: u64) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<usize> for usize {
|
||||
#[inline]
|
||||
fn from_vulkan(val: usize) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<i32> for i32 {
|
||||
#[inline]
|
||||
fn from_vulkan(val: i32) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<f32> for f32 {
|
||||
#[inline]
|
||||
fn from_vulkan(val: f32) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FromVulkan<[f32; N]> for [f32; N] {
|
||||
#[inline]
|
||||
fn from_vulkan(val: [f32; N]) -> Option<Self> {
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FromVulkan<[std::os::raw::c_char; N]> for String {
|
||||
#[inline]
|
||||
fn from_vulkan(val: [i8; N]) -> Option<Self> {
|
||||
Some(unsafe { CStr::from_ptr(val.as_ptr()).to_string_lossy().into_owned() })
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<u32> for Version {
|
||||
#[inline]
|
||||
fn from_vulkan(val: u32) -> Option<Self> {
|
||||
val.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::Bool32> for bool {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::Bool32) -> Option<Self> {
|
||||
Some(val != 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::ConformanceVersion> for ConformanceVersion {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::ConformanceVersion) -> Option<Self> {
|
||||
Some(val.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::DriverId> for DriverId {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::DriverId) -> Option<Self> {
|
||||
val.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::Extent2D> for [u32; 2] {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::Extent2D) -> Option<Self> {
|
||||
Some([val.width, val.height])
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::PhysicalDeviceType> for PhysicalDeviceType {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::PhysicalDeviceType) -> Option<Self> {
|
||||
val.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::PointClippingBehavior> for PointClippingBehavior {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::PointClippingBehavior) -> Option<Self> {
|
||||
val.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::ResolveModeFlags> for ResolveModes {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::ResolveModeFlags) -> Option<Self> {
|
||||
Some(val.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::SampleCountFlags> for SampleCounts {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::SampleCountFlags) -> Option<Self> {
|
||||
Some(val.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::SampleCountFlags> for SampleCount {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::SampleCountFlags) -> Option<Self> {
|
||||
val.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::ShaderCorePropertiesFlagsAMD> for ShaderCoreProperties {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::ShaderCorePropertiesFlagsAMD) -> Option<Self> {
|
||||
Some(val.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::ShaderFloatControlsIndependence> for ShaderFloatControlsIndependence {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::ShaderFloatControlsIndependence) -> Option<Self> {
|
||||
val.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::ShaderStageFlags> for ShaderStages {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::ShaderStageFlags) -> Option<Self> {
|
||||
Some(val.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVulkan<ash::vk::SubgroupFeatureFlags> for SubgroupFeatures {
|
||||
#[inline]
|
||||
fn from_vulkan(val: ash::vk::SubgroupFeatureFlags) -> Option<Self> {
|
||||
Some(val.into())
|
||||
}
|
||||
}
|
@ -92,6 +92,24 @@ impl From<SampleCount> for ash::vk::SampleCountFlags {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ash::vk::SampleCountFlags> for SampleCount {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: ash::vk::SampleCountFlags) -> Result<Self, Self::Error> {
|
||||
match val {
|
||||
ash::vk::SampleCountFlags::TYPE_1 => Ok(Self::Sample1),
|
||||
ash::vk::SampleCountFlags::TYPE_2 => Ok(Self::Sample2),
|
||||
ash::vk::SampleCountFlags::TYPE_4 => Ok(Self::Sample4),
|
||||
ash::vk::SampleCountFlags::TYPE_8 => Ok(Self::Sample8),
|
||||
ash::vk::SampleCountFlags::TYPE_16 => Ok(Self::Sample16),
|
||||
ash::vk::SampleCountFlags::TYPE_32 => Ok(Self::Sample32),
|
||||
ash::vk::SampleCountFlags::TYPE_64 => Ok(Self::Sample64),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for SampleCount {
|
||||
type Error = ();
|
||||
|
||||
|
@ -249,41 +249,47 @@ impl UnsafeImage {
|
||||
FormatTy::Float | FormatTy::Compressed => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.sampled_image_color_sample_counts()
|
||||
.properties()
|
||||
.sampled_image_color_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::Uint | FormatTy::Sint => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.sampled_image_integer_sample_counts()
|
||||
.properties()
|
||||
.sampled_image_integer_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::Depth => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.sampled_image_depth_sample_counts()
|
||||
.properties()
|
||||
.sampled_image_depth_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::Stencil => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.sampled_image_stencil_sample_counts()
|
||||
.properties()
|
||||
.sampled_image_stencil_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::DepthStencil => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.sampled_image_depth_sample_counts()
|
||||
.properties()
|
||||
.sampled_image_depth_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.sampled_image_stencil_sample_counts()
|
||||
.properties()
|
||||
.sampled_image_stencil_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::Ycbcr => {
|
||||
@ -299,8 +305,9 @@ impl UnsafeImage {
|
||||
if usage.storage {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.storage_image_sample_counts()
|
||||
.properties()
|
||||
.storage_image_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
|
||||
@ -313,34 +320,39 @@ impl UnsafeImage {
|
||||
FormatTy::Float | FormatTy::Compressed | FormatTy::Uint | FormatTy::Sint => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.framebuffer_color_sample_counts()
|
||||
.properties()
|
||||
.framebuffer_color_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::Depth => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.framebuffer_depth_sample_counts()
|
||||
.properties()
|
||||
.framebuffer_depth_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::Stencil => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.framebuffer_stencil_sample_counts()
|
||||
.properties()
|
||||
.framebuffer_stencil_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::DepthStencil => {
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.framebuffer_depth_sample_counts()
|
||||
.properties()
|
||||
.framebuffer_depth_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
supported_samples &= device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.framebuffer_stencil_sample_counts()
|
||||
.properties()
|
||||
.framebuffer_stencil_sample_counts
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
FormatTy::Ycbcr => {
|
||||
@ -434,26 +446,46 @@ impl UnsafeImage {
|
||||
}
|
||||
|
||||
// Checking the dimensions against the limits.
|
||||
if array_layers > device.physical_device().limits().max_image_array_layers() {
|
||||
if array_layers
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_image_array_layers
|
||||
.unwrap()
|
||||
{
|
||||
let err = ImageCreationError::UnsupportedDimensions { dimensions };
|
||||
capabilities_error = Some(err);
|
||||
}
|
||||
match ty {
|
||||
ash::vk::ImageType::TYPE_1D => {
|
||||
if extent.width > device.physical_device().limits().max_image_dimension_1d() {
|
||||
if extent.width
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_image_dimension1_d
|
||||
.unwrap()
|
||||
{
|
||||
let err = ImageCreationError::UnsupportedDimensions { dimensions };
|
||||
capabilities_error = Some(err);
|
||||
}
|
||||
}
|
||||
ash::vk::ImageType::TYPE_2D => {
|
||||
let limit = device.physical_device().limits().max_image_dimension_2d();
|
||||
let limit = device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_image_dimension2_d
|
||||
.unwrap();
|
||||
if extent.width > limit || extent.height > limit {
|
||||
let err = ImageCreationError::UnsupportedDimensions { dimensions };
|
||||
capabilities_error = Some(err);
|
||||
}
|
||||
|
||||
if flags.cube_compatible {
|
||||
let limit = device.physical_device().limits().max_image_dimension_cube();
|
||||
let limit = device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_image_dimension_cube
|
||||
.unwrap();
|
||||
if extent.width > limit {
|
||||
let err = ImageCreationError::UnsupportedDimensions { dimensions };
|
||||
capabilities_error = Some(err);
|
||||
@ -461,7 +493,11 @@ impl UnsafeImage {
|
||||
}
|
||||
}
|
||||
ash::vk::ImageType::TYPE_3D => {
|
||||
let limit = device.physical_device().limits().max_image_dimension_3d();
|
||||
let limit = device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_image_dimension3_d
|
||||
.unwrap();
|
||||
if extent.width > limit || extent.height > limit || extent.depth > limit {
|
||||
let err = ImageCreationError::UnsupportedDimensions { dimensions };
|
||||
capabilities_error = Some(err);
|
||||
|
@ -22,7 +22,7 @@ macro_rules! instance_extensions {
|
||||
doc: $doc:expr,
|
||||
raw: $raw:expr,
|
||||
requires_core: $requires_core:expr,
|
||||
requires_extensions: [$($requires_extension:ident),*]$(,)?
|
||||
requires_instance_extensions: [$($requires_instance_extension:ident),*]$(,)?
|
||||
},)*
|
||||
) => (
|
||||
extensions! {
|
||||
@ -32,7 +32,7 @@ macro_rules! instance_extensions {
|
||||
raw: $raw,
|
||||
requires_core: $requires_core,
|
||||
requires_device_extensions: [],
|
||||
requires_instance_extensions: [$($requires_extension),*],
|
||||
requires_instance_extensions: [$($requires_instance_extension),*],
|
||||
},)*
|
||||
}
|
||||
|
||||
@ -59,10 +59,10 @@ macro_rules! instance_extensions {
|
||||
});
|
||||
} else {
|
||||
$(
|
||||
if !self.$requires_extension {
|
||||
if !self.$requires_instance_extension {
|
||||
return Err(crate::extensions::ExtensionRestrictionError {
|
||||
extension: stringify!($member),
|
||||
restriction: crate::extensions::ExtensionRestriction::RequiresInstanceExtension(stringify!($requires_extension)),
|
||||
restriction: crate::extensions::ExtensionRestriction::RequiresInstanceExtension(stringify!($requires_instance_extension)),
|
||||
});
|
||||
}
|
||||
)*
|
||||
|
@ -1,147 +0,0 @@
|
||||
// Copyright (c) 2017 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.
|
||||
|
||||
//! The `Limits` struct provides a nicer API around `vkPhysicalDeviceLimits`.
|
||||
|
||||
use crate::image::SampleCounts;
|
||||
|
||||
/// Limits of a physical device.
|
||||
pub struct Limits<'a> {
|
||||
limits: &'a ash::vk::PhysicalDeviceLimits,
|
||||
}
|
||||
|
||||
macro_rules! limits_impl {
|
||||
($($name:ident: $t:ty => $target:ident,)*) => (
|
||||
impl<'a> Limits<'a> {
|
||||
/// Builds the `Limits` object.
|
||||
#[inline]
|
||||
pub(crate) fn from_vk_limits(limits: &'a ash::vk::PhysicalDeviceLimits) -> Limits<'a> {
|
||||
Limits {
|
||||
limits
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
#[inline]
|
||||
pub fn $name(&self) -> $t {
|
||||
<$t>::from(self.limits.$target)
|
||||
}
|
||||
)*
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
limits_impl! {
|
||||
max_image_dimension_1d: u32 => max_image_dimension1_d,
|
||||
max_image_dimension_2d: u32 => max_image_dimension2_d,
|
||||
max_image_dimension_3d: u32 => max_image_dimension3_d,
|
||||
max_image_dimension_cube: u32 => max_image_dimension_cube,
|
||||
max_image_array_layers: u32 => max_image_array_layers,
|
||||
max_texel_buffer_elements: u32 => max_texel_buffer_elements,
|
||||
max_uniform_buffer_range: u32 => max_uniform_buffer_range,
|
||||
max_storage_buffer_range: u32 => max_storage_buffer_range,
|
||||
max_push_constants_size: u32 => max_push_constants_size,
|
||||
max_memory_allocation_count: u32 => max_memory_allocation_count,
|
||||
max_sampler_allocation_count: u32 => max_sampler_allocation_count,
|
||||
buffer_image_granularity: u64 => buffer_image_granularity,
|
||||
sparse_address_space_size: u64 => sparse_address_space_size,
|
||||
max_bound_descriptor_sets: u32 => max_bound_descriptor_sets,
|
||||
max_per_stage_descriptor_samplers: u32 => max_per_stage_descriptor_samplers,
|
||||
max_per_stage_descriptor_uniform_buffers: u32 => max_per_stage_descriptor_uniform_buffers,
|
||||
max_per_stage_descriptor_storage_buffers: u32 => max_per_stage_descriptor_storage_buffers,
|
||||
max_per_stage_descriptor_sampled_images: u32 => max_per_stage_descriptor_sampled_images,
|
||||
max_per_stage_descriptor_storage_images: u32 => max_per_stage_descriptor_storage_images,
|
||||
max_per_stage_descriptor_input_attachments: u32 => max_per_stage_descriptor_input_attachments,
|
||||
max_per_stage_resources: u32 => max_per_stage_resources,
|
||||
max_descriptor_set_samplers: u32 => max_descriptor_set_samplers,
|
||||
max_descriptor_set_uniform_buffers: u32 => max_descriptor_set_uniform_buffers,
|
||||
max_descriptor_set_uniform_buffers_dynamic: u32 => max_descriptor_set_uniform_buffers_dynamic,
|
||||
max_descriptor_set_storage_buffers: u32 => max_descriptor_set_storage_buffers,
|
||||
max_descriptor_set_storage_buffers_dynamic: u32 => max_descriptor_set_storage_buffers_dynamic,
|
||||
max_descriptor_set_sampled_images: u32 => max_descriptor_set_sampled_images,
|
||||
max_descriptor_set_storage_images: u32 => max_descriptor_set_storage_images,
|
||||
max_descriptor_set_input_attachments: u32 => max_descriptor_set_input_attachments,
|
||||
max_vertex_input_attributes: u32 => max_vertex_input_attributes,
|
||||
max_vertex_input_bindings: u32 => max_vertex_input_bindings,
|
||||
max_vertex_input_attribute_offset: u32 => max_vertex_input_attribute_offset,
|
||||
max_vertex_input_binding_stride: u32 => max_vertex_input_binding_stride,
|
||||
max_vertex_output_components: u32 => max_vertex_output_components,
|
||||
max_tessellation_generation_level: u32 => max_tessellation_generation_level,
|
||||
max_tessellation_patch_size: u32 => max_tessellation_patch_size,
|
||||
max_tessellation_control_per_vertex_input_components: u32 => max_tessellation_control_per_vertex_input_components,
|
||||
max_tessellation_control_per_vertex_output_components: u32 => max_tessellation_control_per_vertex_output_components,
|
||||
max_tessellation_control_per_patch_output_components: u32 => max_tessellation_control_per_patch_output_components,
|
||||
max_tessellation_control_total_output_components: u32 => max_tessellation_control_total_output_components,
|
||||
max_tessellation_evaluation_input_components: u32 => max_tessellation_evaluation_input_components,
|
||||
max_tessellation_evaluation_output_components: u32 => max_tessellation_evaluation_output_components,
|
||||
max_geometry_shader_invocations: u32 => max_geometry_shader_invocations,
|
||||
max_geometry_input_components: u32 => max_geometry_input_components,
|
||||
max_geometry_output_components: u32 => max_geometry_output_components,
|
||||
max_geometry_output_vertices: u32 => max_geometry_output_vertices,
|
||||
max_geometry_total_output_components: u32 => max_geometry_total_output_components,
|
||||
max_fragment_input_components: u32 => max_fragment_input_components,
|
||||
max_fragment_output_attachments: u32 => max_fragment_output_attachments,
|
||||
max_fragment_dual_src_attachments: u32 => max_fragment_dual_src_attachments,
|
||||
max_fragment_combined_output_resources: u32 => max_fragment_combined_output_resources,
|
||||
max_compute_shared_memory_size: u32 => max_compute_shared_memory_size,
|
||||
max_compute_work_group_count: [u32; 3] => max_compute_work_group_count,
|
||||
max_compute_work_group_invocations: u32 => max_compute_work_group_invocations,
|
||||
max_compute_work_group_size: [u32; 3] => max_compute_work_group_size,
|
||||
sub_pixel_precision_bits: u32 => sub_pixel_precision_bits,
|
||||
sub_texel_precision_bits: u32 => sub_texel_precision_bits,
|
||||
mipmap_precision_bits: u32 => mipmap_precision_bits,
|
||||
max_draw_indexed_index_value: u32 => max_draw_indexed_index_value,
|
||||
max_draw_indirect_count: u32 => max_draw_indirect_count,
|
||||
max_sampler_lod_bias: f32 => max_sampler_lod_bias,
|
||||
max_sampler_anisotropy: f32 => max_sampler_anisotropy,
|
||||
max_viewports: u32 => max_viewports,
|
||||
max_viewport_dimensions: [u32; 2] => max_viewport_dimensions,
|
||||
viewport_bounds_range: [f32; 2] => viewport_bounds_range,
|
||||
viewport_sub_pixel_bits: u32 => viewport_sub_pixel_bits,
|
||||
min_memory_map_alignment: usize => min_memory_map_alignment,
|
||||
min_texel_buffer_offset_alignment: u64 => min_texel_buffer_offset_alignment,
|
||||
min_uniform_buffer_offset_alignment: u64 => min_uniform_buffer_offset_alignment,
|
||||
min_storage_buffer_offset_alignment: u64 => min_storage_buffer_offset_alignment,
|
||||
min_texel_offset: i32 => min_texel_offset,
|
||||
max_texel_offset: u32 => max_texel_offset,
|
||||
min_texel_gather_offset: i32 => min_texel_gather_offset,
|
||||
max_texel_gather_offset: u32 => max_texel_gather_offset,
|
||||
min_interpolation_offset: f32 => min_interpolation_offset,
|
||||
max_interpolation_offset: f32 => max_interpolation_offset,
|
||||
sub_pixel_interpolation_offset_bits: u32 => sub_pixel_interpolation_offset_bits,
|
||||
max_framebuffer_width: u32 => max_framebuffer_width,
|
||||
max_framebuffer_height: u32 => max_framebuffer_height,
|
||||
max_framebuffer_layers: u32 => max_framebuffer_layers,
|
||||
framebuffer_color_sample_counts: SampleCounts => framebuffer_color_sample_counts, // FIXME: SampleCountFlag
|
||||
framebuffer_depth_sample_counts: SampleCounts => framebuffer_depth_sample_counts, // FIXME: SampleCountFlag
|
||||
framebuffer_stencil_sample_counts: SampleCounts => framebuffer_stencil_sample_counts, // FIXME: SampleCountFlag
|
||||
framebuffer_no_attachments_sample_counts: SampleCounts => framebuffer_no_attachments_sample_counts, // FIXME: SampleCountFlag
|
||||
max_color_attachments: u32 => max_color_attachments,
|
||||
sampled_image_color_sample_counts: SampleCounts => sampled_image_color_sample_counts, // FIXME: SampleCountFlag
|
||||
sampled_image_integer_sample_counts: SampleCounts => sampled_image_integer_sample_counts, // FIXME: SampleCountFlag
|
||||
sampled_image_depth_sample_counts: SampleCounts => sampled_image_depth_sample_counts, // FIXME: SampleCountFlag
|
||||
sampled_image_stencil_sample_counts: SampleCounts => sampled_image_stencil_sample_counts, // FIXME: SampleCountFlag
|
||||
storage_image_sample_counts: SampleCounts => storage_image_sample_counts, // FIXME: SampleCountFlag
|
||||
max_sample_mask_words: u32 => max_sample_mask_words,
|
||||
timestamp_compute_and_graphics: u32 => timestamp_compute_and_graphics, // TODO: these are booleans
|
||||
timestamp_period: f32 => timestamp_period,
|
||||
max_clip_distances: u32 => max_clip_distances,
|
||||
max_cull_distances: u32 => max_cull_distances,
|
||||
max_combined_clip_and_cull_distances: u32 => max_combined_clip_and_cull_distances,
|
||||
discrete_queue_priorities: u32 => discrete_queue_priorities,
|
||||
point_size_range: [f32; 2] => point_size_range,
|
||||
line_width_range: [f32; 2] => line_width_range,
|
||||
point_size_granularity: f32 => point_size_granularity,
|
||||
line_width_granularity: f32 => line_width_granularity,
|
||||
strict_lines: u32 => strict_lines, // TODO: these are booleans
|
||||
standard_sample_locations: u32 => standard_sample_locations, // TODO: these are booleans
|
||||
optimal_buffer_copy_offset_alignment: u64 => optimal_buffer_copy_offset_alignment,
|
||||
optimal_buffer_copy_row_pitch_alignment: u64 => optimal_buffer_copy_row_pitch_alignment,
|
||||
non_coherent_atom_size: u64 => non_coherent_atom_size,
|
||||
}
|
@ -35,7 +35,7 @@
|
||||
//!
|
||||
//! # let instance = Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None).unwrap();
|
||||
//! for physical_device in PhysicalDevice::enumerate(&instance) {
|
||||
//! println!("Available device: {}", physical_device.name());
|
||||
//! println!("Available device: {}", physical_device.properties().device_name.as_ref().unwrap());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
@ -59,8 +59,9 @@ pub use self::layers::layers_list;
|
||||
pub use self::layers::LayerProperties;
|
||||
pub use self::layers::LayersIterator;
|
||||
pub use self::layers::LayersListError;
|
||||
pub use self::limits::Limits;
|
||||
pub use self::loader::LoadingError;
|
||||
pub use self::physical_device::ConformanceVersion;
|
||||
pub use self::physical_device::DriverId;
|
||||
pub use self::physical_device::MemoryHeap;
|
||||
pub use self::physical_device::MemoryHeapsIter;
|
||||
pub use self::physical_device::MemoryType;
|
||||
@ -68,8 +69,12 @@ pub use self::physical_device::MemoryTypesIter;
|
||||
pub use self::physical_device::PhysicalDevice;
|
||||
pub use self::physical_device::PhysicalDeviceType;
|
||||
pub use self::physical_device::PhysicalDevicesIter;
|
||||
pub use self::physical_device::PointClippingBehavior;
|
||||
pub use self::physical_device::QueueFamiliesIter;
|
||||
pub use self::physical_device::QueueFamily;
|
||||
pub use self::physical_device::ShaderCoreProperties;
|
||||
pub use self::physical_device::ShaderFloatControlsIndependence;
|
||||
pub use self::physical_device::SubgroupFeatures;
|
||||
pub use crate::extensions::{
|
||||
ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError,
|
||||
};
|
||||
@ -79,6 +84,5 @@ pub mod debug;
|
||||
pub(crate) mod extensions;
|
||||
mod instance;
|
||||
mod layers;
|
||||
mod limits;
|
||||
pub mod loader;
|
||||
mod physical_device;
|
||||
|
@ -8,13 +8,14 @@
|
||||
// according to those terms.
|
||||
|
||||
use crate::check_errors;
|
||||
use crate::device::{DeviceExtensions, Features, FeaturesFfi};
|
||||
use crate::instance::limits::Limits;
|
||||
use crate::device::{DeviceExtensions, Features, FeaturesFfi, Properties, PropertiesFfi};
|
||||
use crate::instance::{Instance, InstanceCreationError};
|
||||
use crate::sync::PipelineStage;
|
||||
use crate::Version;
|
||||
use crate::VulkanObject;
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
@ -44,49 +45,55 @@ 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
|
||||
let info: Vec<_> = physical_devices
|
||||
.into_iter()
|
||||
.zip(supported_extensions.into_iter());
|
||||
.map(|physical_device| -> Result<_, InstanceCreationError> {
|
||||
let api_version = unsafe {
|
||||
let mut output = MaybeUninit::uninit();
|
||||
fns.v1_0
|
||||
.get_physical_device_properties(physical_device, output.as_mut_ptr());
|
||||
let api_version = Version::try_from(output.assume_init().api_version).unwrap();
|
||||
std::cmp::min(instance.max_api_version(), api_version)
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
let extensions = DeviceExtensions::from(
|
||||
extension_properties
|
||||
.iter()
|
||||
.map(|property| unsafe { CStr::from_ptr(property.extension_name.as_ptr()) }),
|
||||
);
|
||||
|
||||
Ok((physical_device, api_version, extensions))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
// 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
|
||||
|| instance_extensions.khr_get_physical_device_properties2
|
||||
{
|
||||
init_physical_devices_inner2(instance, iter)
|
||||
init_physical_devices_inner2(instance, info)
|
||||
} else {
|
||||
init_physical_devices_inner(instance, iter)
|
||||
init_physical_devices_inner(instance, info)
|
||||
};
|
||||
|
||||
Ok(physical_devices)
|
||||
@ -95,17 +102,24 @@ pub(super) fn init_physical_devices(
|
||||
/// Initialize all physical devices
|
||||
fn init_physical_devices_inner<I>(instance: &Instance, info: I) -> Vec<PhysicalDeviceInfos>
|
||||
where
|
||||
I: IntoIterator<Item = (ash::vk::PhysicalDevice, DeviceExtensions)>,
|
||||
I: IntoIterator<Item = (ash::vk::PhysicalDevice, Version, DeviceExtensions)>,
|
||||
{
|
||||
let fns = instance.fns();
|
||||
|
||||
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(physical_device, output.as_mut_ptr());
|
||||
output.assume_init()
|
||||
.map(|(physical_device, api_version, supported_extensions)| {
|
||||
let properties: Properties = unsafe {
|
||||
let mut output = PropertiesFfi::default();
|
||||
output.make_chain(
|
||||
api_version,
|
||||
&supported_extensions,
|
||||
instance.loaded_extensions(),
|
||||
);
|
||||
fns.v1_0.get_physical_device_properties(
|
||||
physical_device,
|
||||
&mut output.head_as_mut().properties,
|
||||
);
|
||||
Properties::from(&output)
|
||||
};
|
||||
|
||||
let queue_families = unsafe {
|
||||
@ -126,7 +140,7 @@ where
|
||||
families
|
||||
};
|
||||
|
||||
let memory: ash::vk::PhysicalDeviceMemoryProperties = unsafe {
|
||||
let memory_properties: ash::vk::PhysicalDeviceMemoryProperties = unsafe {
|
||||
let mut output = MaybeUninit::uninit();
|
||||
fns.v1_0
|
||||
.get_physical_device_memory_properties(physical_device, output.as_mut_ptr());
|
||||
@ -144,9 +158,9 @@ where
|
||||
|
||||
PhysicalDeviceInfos {
|
||||
physical_device,
|
||||
api_version,
|
||||
properties,
|
||||
extended_properties: PhysicalDeviceExtendedProperties::empty(),
|
||||
memory,
|
||||
memory_properties,
|
||||
queue_families,
|
||||
available_features,
|
||||
}
|
||||
@ -158,50 +172,29 @@ where
|
||||
/// TODO: Query extension-specific physical device properties, once a new instance extension is supported.
|
||||
fn init_physical_devices_inner2<I>(instance: &Instance, info: I) -> Vec<PhysicalDeviceInfos>
|
||||
where
|
||||
I: IntoIterator<Item = (ash::vk::PhysicalDevice, DeviceExtensions)>,
|
||||
I: IntoIterator<Item = (ash::vk::PhysicalDevice, Version, DeviceExtensions)>,
|
||||
{
|
||||
let fns = instance.fns();
|
||||
|
||||
info.into_iter()
|
||||
.map(|(physical_device, supported_extensions)| {
|
||||
let mut extended_properties = PhysicalDeviceExtendedProperties::empty();
|
||||
|
||||
let properties: ash::vk::PhysicalDeviceProperties = unsafe {
|
||||
let mut subgroup_properties = ash::vk::PhysicalDeviceSubgroupProperties::default();
|
||||
|
||||
let mut multiview_properties = ash::vk::PhysicalDeviceMultiviewProperties {
|
||||
p_next: &mut subgroup_properties as *mut _ as *mut c_void,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut output = ash::vk::PhysicalDeviceProperties2 {
|
||||
p_next: if instance.api_version() >= Version::V1_1 {
|
||||
&mut multiview_properties as *mut _ as *mut c_void
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
.map(|(physical_device, api_version, supported_extensions)| {
|
||||
let properties: Properties = unsafe {
|
||||
let mut output = PropertiesFfi::default();
|
||||
output.make_chain(
|
||||
api_version,
|
||||
&supported_extensions,
|
||||
instance.loaded_extensions(),
|
||||
);
|
||||
|
||||
if instance.api_version() >= Version::V1_1 {
|
||||
fns.v1_1
|
||||
.get_physical_device_properties2(physical_device, &mut output);
|
||||
.get_physical_device_properties2(physical_device, output.head_as_mut());
|
||||
} else {
|
||||
fns.khr_get_physical_device_properties2
|
||||
.get_physical_device_properties2_khr(physical_device, &mut output);
|
||||
.get_physical_device_properties2_khr(physical_device, output.head_as_mut());
|
||||
}
|
||||
|
||||
extended_properties = PhysicalDeviceExtendedProperties {
|
||||
subgroup_size: Some(subgroup_properties.subgroup_size),
|
||||
max_multiview_view_count: Some(multiview_properties.max_multiview_view_count),
|
||||
max_multiview_instance_index: Some(
|
||||
multiview_properties.max_multiview_instance_index,
|
||||
),
|
||||
|
||||
..extended_properties
|
||||
};
|
||||
|
||||
output.properties
|
||||
Properties::from(&output)
|
||||
};
|
||||
|
||||
let queue_families = unsafe {
|
||||
@ -245,7 +238,7 @@ where
|
||||
.collect()
|
||||
};
|
||||
|
||||
let memory: ash::vk::PhysicalDeviceMemoryProperties = unsafe {
|
||||
let memory_properties: ash::vk::PhysicalDeviceMemoryProperties = unsafe {
|
||||
let mut output = ash::vk::PhysicalDeviceMemoryProperties2KHR::default();
|
||||
|
||||
if instance.api_version() >= Version::V1_1 {
|
||||
@ -260,12 +253,12 @@ where
|
||||
};
|
||||
|
||||
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 mut output = FeaturesFfi::default();
|
||||
output.make_chain(api_version, &supported_extensions);
|
||||
output.make_chain(
|
||||
api_version,
|
||||
&supported_extensions,
|
||||
instance.loaded_extensions(),
|
||||
);
|
||||
|
||||
if instance.api_version() >= Version::V1_1 {
|
||||
fns.v1_1
|
||||
@ -280,9 +273,9 @@ where
|
||||
|
||||
PhysicalDeviceInfos {
|
||||
physical_device,
|
||||
api_version,
|
||||
properties,
|
||||
extended_properties,
|
||||
memory,
|
||||
memory_properties,
|
||||
queue_families,
|
||||
available_features,
|
||||
}
|
||||
@ -292,60 +285,13 @@ where
|
||||
|
||||
pub(super) struct PhysicalDeviceInfos {
|
||||
physical_device: ash::vk::PhysicalDevice,
|
||||
properties: ash::vk::PhysicalDeviceProperties,
|
||||
extended_properties: PhysicalDeviceExtendedProperties,
|
||||
api_version: Version,
|
||||
properties: Properties,
|
||||
queue_families: Vec<ash::vk::QueueFamilyProperties>,
|
||||
memory: ash::vk::PhysicalDeviceMemoryProperties,
|
||||
memory_properties: ash::vk::PhysicalDeviceMemoryProperties,
|
||||
available_features: Features,
|
||||
}
|
||||
|
||||
/// Represents additional information related to Physical Devices fetched from
|
||||
/// `vkGetPhysicalDeviceProperties` call. Certain features available only when
|
||||
/// appropriate `Instance` extensions enabled. The core extension required
|
||||
/// for this features is `InstanceExtensions::khr_get_physical_device_properties2`
|
||||
///
|
||||
/// TODO: Only a small subset of available properties(https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceProperties2.html) is implemented at this moment.
|
||||
pub struct PhysicalDeviceExtendedProperties {
|
||||
subgroup_size: Option<u32>,
|
||||
max_multiview_view_count: Option<u32>,
|
||||
max_multiview_instance_index: Option<u32>,
|
||||
}
|
||||
|
||||
impl PhysicalDeviceExtendedProperties {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
subgroup_size: None,
|
||||
max_multiview_view_count: None,
|
||||
max_multiview_instance_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// The default number of invocations in each subgroup
|
||||
///
|
||||
/// See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceSubgroupProperties.html for details
|
||||
#[inline]
|
||||
pub fn subgroup_size(&self) -> &Option<u32> {
|
||||
&self.subgroup_size
|
||||
}
|
||||
|
||||
/// The maximum number of views that can be used in a subpass using the multiview feature.
|
||||
///
|
||||
/// See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceMultiviewProperties.html for details
|
||||
#[inline]
|
||||
pub fn max_multiview_view_count(&self) -> &Option<u32> {
|
||||
&self.max_multiview_view_count
|
||||
}
|
||||
|
||||
/// The maximum number valid value of instance index (for instanced rendering)
|
||||
/// allowed to be generated by a drawing command using this multiview description.
|
||||
///
|
||||
/// See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceMultiviewProperties.html for details
|
||||
#[inline]
|
||||
pub fn max_multiview_instance_index(&self) -> &Option<u32> {
|
||||
&self.max_multiview_instance_index
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents one of the available devices on this machine.
|
||||
///
|
||||
/// This struct simply contains a pointer to an instance and a number representing the physical
|
||||
@ -365,7 +311,7 @@ impl PhysicalDeviceExtendedProperties {
|
||||
/// }
|
||||
///
|
||||
/// fn print_infos(dev: PhysicalDevice) {
|
||||
/// println!("Name: {}", dev.name());
|
||||
/// println!("Name: {}", dev.properties().device_name.as_ref().unwrap());
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@ -387,7 +333,7 @@ impl<'a> PhysicalDevice<'a> {
|
||||
///
|
||||
/// # let instance = Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None).unwrap();
|
||||
/// for physical_device in PhysicalDevice::enumerate(&instance) {
|
||||
/// println!("Available device: {}", physical_device.name());
|
||||
/// println!("Available device: {}", physical_device.properties().device_name.as_ref().unwrap());
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
@ -451,53 +397,20 @@ impl<'a> PhysicalDevice<'a> {
|
||||
self.device
|
||||
}
|
||||
|
||||
/// Returns the human-readable name of the device.
|
||||
#[inline]
|
||||
pub fn name(&self) -> &str {
|
||||
unsafe {
|
||||
let val = &self.infos().properties.device_name;
|
||||
let val = CStr::from_ptr(val.as_ptr());
|
||||
val.to_str()
|
||||
.expect("physical device name contained non-UTF8 characters")
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the type of the device.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use vulkano::instance::Instance;
|
||||
/// # use vulkano::instance::InstanceExtensions;
|
||||
/// # use vulkano::Version;
|
||||
/// use vulkano::instance::PhysicalDevice;
|
||||
///
|
||||
/// # let instance = Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None).unwrap();
|
||||
/// for physical_device in PhysicalDevice::enumerate(&instance) {
|
||||
/// println!("Available device: {} (type: {:?})",
|
||||
/// physical_device.name(), physical_device.ty());
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn ty(&self) -> PhysicalDeviceType {
|
||||
match self.instance.physical_devices[self.device]
|
||||
.properties
|
||||
.device_type
|
||||
{
|
||||
ash::vk::PhysicalDeviceType::OTHER => PhysicalDeviceType::Other,
|
||||
ash::vk::PhysicalDeviceType::INTEGRATED_GPU => PhysicalDeviceType::IntegratedGpu,
|
||||
ash::vk::PhysicalDeviceType::DISCRETE_GPU => PhysicalDeviceType::DiscreteGpu,
|
||||
ash::vk::PhysicalDeviceType::VIRTUAL_GPU => PhysicalDeviceType::VirtualGpu,
|
||||
ash::vk::PhysicalDeviceType::CPU => PhysicalDeviceType::Cpu,
|
||||
_ => panic!("Unrecognized Vulkan device type"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the version of Vulkan supported by this device.
|
||||
///
|
||||
/// Unlike the `api_version` property, which is the version reported by the device directly,
|
||||
/// this function returns the version the device can actually support, based on the instance's,
|
||||
/// `max_api_version`.
|
||||
#[inline]
|
||||
pub fn api_version(&self) -> Version {
|
||||
let val = self.infos().properties.api_version;
|
||||
Version::from(val)
|
||||
self.infos().api_version
|
||||
}
|
||||
|
||||
/// Returns the Vulkan properties reported by the device.
|
||||
#[inline]
|
||||
pub fn properties(&self) -> &'a Properties {
|
||||
&self.infos().properties
|
||||
}
|
||||
|
||||
/// Returns the Vulkan features that are supported by this physical device.
|
||||
@ -540,7 +453,7 @@ impl<'a> PhysicalDevice<'a> {
|
||||
/// Returns the memory type with the given index, or `None` if out of range.
|
||||
#[inline]
|
||||
pub fn memory_type_by_id(&self, id: u32) -> Option<MemoryType<'a>> {
|
||||
if id < self.infos().memory.memory_type_count {
|
||||
if id < self.infos().memory_properties.memory_type_count {
|
||||
Some(MemoryType {
|
||||
physical_device: *self,
|
||||
id,
|
||||
@ -562,7 +475,7 @@ impl<'a> PhysicalDevice<'a> {
|
||||
/// Returns the memory heap with the given index, or `None` if out of range.
|
||||
#[inline]
|
||||
pub fn memory_heap_by_id(&self, id: u32) -> Option<MemoryHeap<'a>> {
|
||||
if id < self.infos().memory.memory_heap_count {
|
||||
if id < self.infos().memory_properties.memory_heap_count {
|
||||
Some(MemoryHeap {
|
||||
physical_device: *self,
|
||||
id,
|
||||
@ -572,51 +485,6 @@ impl<'a> PhysicalDevice<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives access to the limits of the physical device.
|
||||
///
|
||||
/// This function should be zero-cost in release mode. It only exists to not pollute the
|
||||
/// namespace of `PhysicalDevice` with all the limits-related getters.
|
||||
#[inline]
|
||||
pub fn limits(&self) -> Limits<'a> {
|
||||
Limits::from_vk_limits(&self.infos().properties.limits)
|
||||
}
|
||||
|
||||
/// Returns an opaque number representing the version of the driver of this device.
|
||||
///
|
||||
/// The meaning of this number is implementation-specific. It can be used in bug reports, for
|
||||
/// example.
|
||||
#[inline]
|
||||
pub fn driver_version(&self) -> u32 {
|
||||
self.infos().properties.driver_version
|
||||
}
|
||||
|
||||
/// Returns the PCI ID of the device.
|
||||
#[inline]
|
||||
pub fn pci_device_id(&self) -> u32 {
|
||||
self.infos().properties.device_id
|
||||
}
|
||||
|
||||
/// Returns the PCI ID of the vendor.
|
||||
#[inline]
|
||||
pub fn pci_vendor_id(&self) -> u32 {
|
||||
self.infos().properties.vendor_id
|
||||
}
|
||||
|
||||
/// Returns a unique identifier for the device.
|
||||
///
|
||||
/// Can be stored in a configuration file, so that you can retrieve the device again the next
|
||||
/// time the program is run.
|
||||
#[inline]
|
||||
pub fn uuid(&self) -> &[u8; 16] {
|
||||
// must be equal to ash::vk::UUID_SIZE
|
||||
&self.infos().properties.pipeline_cache_uuid
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn extended_properties(&self) -> &PhysicalDeviceExtendedProperties {
|
||||
&self.infos().extended_properties
|
||||
}
|
||||
|
||||
// Internal function to make it easier to get the infos of this device.
|
||||
#[inline]
|
||||
fn infos(&self) -> &'a PhysicalDeviceInfos {
|
||||
@ -668,19 +536,35 @@ impl<'a> Iterator for PhysicalDevicesIter<'a> {
|
||||
impl<'a> ExactSizeIterator for PhysicalDevicesIter<'a> {}
|
||||
|
||||
/// Type of a physical device.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
#[repr(i32)]
|
||||
pub enum PhysicalDeviceType {
|
||||
/// The device is an integrated GPU.
|
||||
IntegratedGpu = 1,
|
||||
IntegratedGpu = ash::vk::PhysicalDeviceType::INTEGRATED_GPU.as_raw(),
|
||||
/// The device is a discrete GPU.
|
||||
DiscreteGpu = 2,
|
||||
DiscreteGpu = ash::vk::PhysicalDeviceType::DISCRETE_GPU.as_raw(),
|
||||
/// The device is a virtual GPU.
|
||||
VirtualGpu = 3,
|
||||
VirtualGpu = ash::vk::PhysicalDeviceType::VIRTUAL_GPU.as_raw(),
|
||||
/// The device is a CPU.
|
||||
Cpu = 4,
|
||||
Cpu = ash::vk::PhysicalDeviceType::CPU.as_raw(),
|
||||
/// The device is something else.
|
||||
Other = 0,
|
||||
Other = ash::vk::PhysicalDeviceType::OTHER.as_raw(),
|
||||
}
|
||||
|
||||
impl TryFrom<ash::vk::PhysicalDeviceType> for PhysicalDeviceType {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: ash::vk::PhysicalDeviceType) -> Result<Self, Self::Error> {
|
||||
match val {
|
||||
ash::vk::PhysicalDeviceType::INTEGRATED_GPU => Ok(Self::IntegratedGpu),
|
||||
ash::vk::PhysicalDeviceType::DISCRETE_GPU => Ok(Self::DiscreteGpu),
|
||||
ash::vk::PhysicalDeviceType::VIRTUAL_GPU => Ok(Self::VirtualGpu),
|
||||
ash::vk::PhysicalDeviceType::CPU => Ok(Self::Cpu),
|
||||
ash::vk::PhysicalDeviceType::OTHER => Ok(Self::Other),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a queue family in a physical device.
|
||||
@ -845,7 +729,8 @@ impl<'a> MemoryType<'a> {
|
||||
/// Returns the heap that corresponds to this memory type.
|
||||
#[inline]
|
||||
pub fn heap(&self) -> MemoryHeap<'a> {
|
||||
let heap_id = self.physical_device.infos().memory.memory_types[self.id as usize].heap_index;
|
||||
let heap_id = self.physical_device.infos().memory_properties.memory_types[self.id as usize]
|
||||
.heap_index;
|
||||
MemoryHeap {
|
||||
physical_device: self.physical_device,
|
||||
id: heap_id,
|
||||
@ -897,7 +782,7 @@ impl<'a> MemoryType<'a> {
|
||||
/// Internal utility function that returns the flags of this queue family.
|
||||
#[inline]
|
||||
fn flags(&self) -> ash::vk::MemoryPropertyFlags {
|
||||
self.physical_device.infos().memory.memory_types[self.id as usize].property_flags
|
||||
self.physical_device.infos().memory_properties.memory_types[self.id as usize].property_flags
|
||||
}
|
||||
}
|
||||
|
||||
@ -913,7 +798,13 @@ impl<'a> Iterator for MemoryTypesIter<'a> {
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<MemoryType<'a>> {
|
||||
if self.current_id >= self.physical_device.infos().memory.memory_type_count {
|
||||
if self.current_id
|
||||
>= self
|
||||
.physical_device
|
||||
.infos()
|
||||
.memory_properties
|
||||
.memory_type_count
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -928,7 +819,11 @@ impl<'a> Iterator for MemoryTypesIter<'a> {
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let len = self.physical_device.infos().memory.memory_type_count;
|
||||
let len = self
|
||||
.physical_device
|
||||
.infos()
|
||||
.memory_properties
|
||||
.memory_type_count;
|
||||
let remain = (len - self.current_id) as usize;
|
||||
(remain, Some(remain))
|
||||
}
|
||||
@ -959,13 +854,14 @@ impl<'a> MemoryHeap<'a> {
|
||||
/// Returns the size in bytes on this heap.
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
self.physical_device.infos().memory.memory_heaps[self.id as usize].size as usize
|
||||
self.physical_device.infos().memory_properties.memory_heaps[self.id as usize].size as usize
|
||||
}
|
||||
|
||||
/// Returns true if the heap is local to the GPU.
|
||||
#[inline]
|
||||
pub fn is_device_local(&self) -> bool {
|
||||
let flags = self.physical_device.infos().memory.memory_heaps[self.id as usize].flags;
|
||||
let flags =
|
||||
self.physical_device.infos().memory_properties.memory_heaps[self.id as usize].flags;
|
||||
!(flags & ash::vk::MemoryHeapFlags::DEVICE_LOCAL).is_empty()
|
||||
}
|
||||
|
||||
@ -973,7 +869,8 @@ impl<'a> MemoryHeap<'a> {
|
||||
/// heap will replicate to each physical-device's instance of heap.
|
||||
#[inline]
|
||||
pub fn is_multi_instance(&self) -> bool {
|
||||
let flags = self.physical_device.infos().memory.memory_heaps[self.id as usize].flags;
|
||||
let flags =
|
||||
self.physical_device.infos().memory_properties.memory_heaps[self.id as usize].flags;
|
||||
!(flags & ash::vk::MemoryHeapFlags::MULTI_INSTANCE).is_empty()
|
||||
}
|
||||
}
|
||||
@ -990,7 +887,13 @@ impl<'a> Iterator for MemoryHeapsIter<'a> {
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<MemoryHeap<'a>> {
|
||||
if self.current_id >= self.physical_device.infos().memory.memory_heap_count {
|
||||
if self.current_id
|
||||
>= self
|
||||
.physical_device
|
||||
.infos()
|
||||
.memory_properties
|
||||
.memory_heap_count
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -1005,10 +908,179 @@ impl<'a> Iterator for MemoryHeapsIter<'a> {
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let len = self.physical_device.infos().memory.memory_heap_count;
|
||||
let len = self
|
||||
.physical_device
|
||||
.infos()
|
||||
.memory_properties
|
||||
.memory_heap_count;
|
||||
let remain = (len - self.current_id) as usize;
|
||||
(remain, Some(remain))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for MemoryHeapsIter<'a> {}
|
||||
|
||||
/// The version of the Vulkan conformance test that a driver is conformant against.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct ConformanceVersion {
|
||||
pub major: u8,
|
||||
pub minor: u8,
|
||||
pub subminor: u8,
|
||||
pub patch: u8,
|
||||
}
|
||||
|
||||
impl From<ash::vk::ConformanceVersion> for ConformanceVersion {
|
||||
#[inline]
|
||||
fn from(val: ash::vk::ConformanceVersion) -> Self {
|
||||
ConformanceVersion {
|
||||
major: val.major,
|
||||
minor: val.minor,
|
||||
subminor: val.subminor,
|
||||
patch: val.patch,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ConformanceVersion {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ConformanceVersion {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(self, formatter)
|
||||
}
|
||||
}
|
||||
|
||||
/// An identifier for the driver of a physical device.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(i32)]
|
||||
pub enum DriverId {
|
||||
AMDProprietary = ash::vk::DriverId::AMD_PROPRIETARY.as_raw(),
|
||||
AMDOpenSource = ash::vk::DriverId::AMD_OPEN_SOURCE.as_raw(),
|
||||
MesaRADV = ash::vk::DriverId::MESA_RADV.as_raw(),
|
||||
NvidiaProprietary = ash::vk::DriverId::NVIDIA_PROPRIETARY.as_raw(),
|
||||
IntelProprietaryWindows = ash::vk::DriverId::INTEL_PROPRIETARY_WINDOWS.as_raw(),
|
||||
IntelOpenSourceMesa = ash::vk::DriverId::INTEL_OPEN_SOURCE_MESA.as_raw(),
|
||||
ImaginationProprietary = ash::vk::DriverId::IMAGINATION_PROPRIETARY.as_raw(),
|
||||
QualcommProprietary = ash::vk::DriverId::QUALCOMM_PROPRIETARY.as_raw(),
|
||||
ARMProprietary = ash::vk::DriverId::ARM_PROPRIETARY.as_raw(),
|
||||
GoogleSwiftshader = ash::vk::DriverId::GOOGLE_SWIFTSHADER.as_raw(),
|
||||
GGPProprietary = ash::vk::DriverId::GGP_PROPRIETARY.as_raw(),
|
||||
BroadcomProprietary = ash::vk::DriverId::BROADCOM_PROPRIETARY.as_raw(),
|
||||
MesaLLVMpipe = ash::vk::DriverId::MESA_LLVMPIPE.as_raw(),
|
||||
MoltenVK = ash::vk::DriverId::MOLTENVK.as_raw(),
|
||||
}
|
||||
|
||||
impl TryFrom<ash::vk::DriverId> for DriverId {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: ash::vk::DriverId) -> Result<Self, Self::Error> {
|
||||
match val {
|
||||
ash::vk::DriverId::AMD_PROPRIETARY => Ok(Self::AMDProprietary),
|
||||
ash::vk::DriverId::AMD_OPEN_SOURCE => Ok(Self::AMDOpenSource),
|
||||
ash::vk::DriverId::MESA_RADV => Ok(Self::MesaRADV),
|
||||
ash::vk::DriverId::NVIDIA_PROPRIETARY => Ok(Self::NvidiaProprietary),
|
||||
ash::vk::DriverId::INTEL_PROPRIETARY_WINDOWS => Ok(Self::IntelProprietaryWindows),
|
||||
ash::vk::DriverId::INTEL_OPEN_SOURCE_MESA => Ok(Self::IntelOpenSourceMesa),
|
||||
ash::vk::DriverId::IMAGINATION_PROPRIETARY => Ok(Self::ImaginationProprietary),
|
||||
ash::vk::DriverId::QUALCOMM_PROPRIETARY => Ok(Self::QualcommProprietary),
|
||||
ash::vk::DriverId::ARM_PROPRIETARY => Ok(Self::ARMProprietary),
|
||||
ash::vk::DriverId::GOOGLE_SWIFTSHADER => Ok(Self::GoogleSwiftshader),
|
||||
ash::vk::DriverId::GGP_PROPRIETARY => Ok(Self::GGPProprietary),
|
||||
ash::vk::DriverId::BROADCOM_PROPRIETARY => Ok(Self::BroadcomProprietary),
|
||||
ash::vk::DriverId::MESA_LLVMPIPE => Ok(Self::MesaLLVMpipe),
|
||||
ash::vk::DriverId::MOLTENVK => Ok(Self::MoltenVK),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies which subgroup operations are supported.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct SubgroupFeatures {
|
||||
pub basic: bool,
|
||||
pub vote: bool,
|
||||
pub arithmetic: bool,
|
||||
pub ballot: bool,
|
||||
pub shuffle: bool,
|
||||
pub shuffle_relative: bool,
|
||||
pub clustered: bool,
|
||||
pub quad: bool,
|
||||
}
|
||||
|
||||
impl From<ash::vk::SubgroupFeatureFlags> for SubgroupFeatures {
|
||||
#[inline]
|
||||
fn from(val: ash::vk::SubgroupFeatureFlags) -> Self {
|
||||
Self {
|
||||
basic: val.intersects(ash::vk::SubgroupFeatureFlags::BASIC),
|
||||
vote: val.intersects(ash::vk::SubgroupFeatureFlags::VOTE),
|
||||
arithmetic: val.intersects(ash::vk::SubgroupFeatureFlags::ARITHMETIC),
|
||||
ballot: val.intersects(ash::vk::SubgroupFeatureFlags::BALLOT),
|
||||
shuffle: val.intersects(ash::vk::SubgroupFeatureFlags::SHUFFLE),
|
||||
shuffle_relative: val.intersects(ash::vk::SubgroupFeatureFlags::SHUFFLE_RELATIVE),
|
||||
clustered: val.intersects(ash::vk::SubgroupFeatureFlags::CLUSTERED),
|
||||
quad: val.intersects(ash::vk::SubgroupFeatureFlags::QUAD),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies how the device clips single point primitives.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(i32)]
|
||||
pub enum PointClippingBehavior {
|
||||
/// Points are clipped if they lie outside any clip plane, both those bounding the view volume
|
||||
/// and user-defined clip planes.
|
||||
AllClipPlanes = ash::vk::PointClippingBehavior::ALL_CLIP_PLANES.as_raw(),
|
||||
/// Points are clipped only if they lie outside a user-defined clip plane.
|
||||
UserClipPlanesOnly = ash::vk::PointClippingBehavior::USER_CLIP_PLANES_ONLY.as_raw(),
|
||||
}
|
||||
|
||||
impl TryFrom<ash::vk::PointClippingBehavior> for PointClippingBehavior {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: ash::vk::PointClippingBehavior) -> Result<Self, Self::Error> {
|
||||
match val {
|
||||
ash::vk::PointClippingBehavior::ALL_CLIP_PLANES => Ok(Self::AllClipPlanes),
|
||||
ash::vk::PointClippingBehavior::USER_CLIP_PLANES_ONLY => Ok(Self::UserClipPlanesOnly),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies whether, and how, shader float controls can be set independently.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(i32)]
|
||||
pub enum ShaderFloatControlsIndependence {
|
||||
Float32Only = ash::vk::ShaderFloatControlsIndependence::TYPE_32_ONLY.as_raw(),
|
||||
All = ash::vk::ShaderFloatControlsIndependence::ALL.as_raw(),
|
||||
None = ash::vk::ShaderFloatControlsIndependence::NONE.as_raw(),
|
||||
}
|
||||
|
||||
impl TryFrom<ash::vk::ShaderFloatControlsIndependence> for ShaderFloatControlsIndependence {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: ash::vk::ShaderFloatControlsIndependence) -> Result<Self, Self::Error> {
|
||||
match val {
|
||||
ash::vk::ShaderFloatControlsIndependence::TYPE_32_ONLY => Ok(Self::Float32Only),
|
||||
ash::vk::ShaderFloatControlsIndependence::ALL => Ok(Self::All),
|
||||
ash::vk::ShaderFloatControlsIndependence::NONE => Ok(Self::None),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specifies shader core properties.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ShaderCoreProperties {}
|
||||
|
||||
impl From<ash::vk::ShaderCorePropertiesFlagsAMD> for ShaderCoreProperties {
|
||||
#[inline]
|
||||
fn from(val: ash::vk::ShaderCorePropertiesFlagsAMD) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
@ -335,7 +335,12 @@ impl<'a> DeviceMemoryBuilder<'a> {
|
||||
.lock()
|
||||
.expect("Poisoned mutex");
|
||||
|
||||
if *allocation_count >= physical_device.limits().max_memory_allocation_count() {
|
||||
if *allocation_count
|
||||
>= physical_device
|
||||
.properties()
|
||||
.max_memory_allocation_count
|
||||
.unwrap()
|
||||
{
|
||||
return Err(DeviceMemoryAllocError::TooManyObjects);
|
||||
}
|
||||
let fns = self.device.fns();
|
||||
|
@ -440,17 +440,18 @@ where
|
||||
if stride
|
||||
> device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_binding_stride() as usize
|
||||
.properties()
|
||||
.max_vertex_input_binding_stride
|
||||
.unwrap() as usize
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputBindingStrideExceeded {
|
||||
binding: num as usize,
|
||||
max: device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_binding_stride()
|
||||
as usize,
|
||||
.properties()
|
||||
.max_vertex_input_binding_stride
|
||||
.unwrap() as usize,
|
||||
obtained: stride,
|
||||
},
|
||||
);
|
||||
@ -470,16 +471,17 @@ where
|
||||
if info.offset
|
||||
> device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_attribute_offset() as usize
|
||||
.properties()
|
||||
.max_vertex_input_attribute_offset
|
||||
.unwrap() as usize
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputAttributeOffsetExceeded {
|
||||
max: device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_attribute_offset()
|
||||
as usize,
|
||||
.properties()
|
||||
.max_vertex_input_attribute_offset
|
||||
.unwrap() as usize,
|
||||
obtained: info.offset,
|
||||
},
|
||||
);
|
||||
@ -504,15 +506,17 @@ where
|
||||
if binding_descriptions.len()
|
||||
> device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_bindings() as usize
|
||||
.properties()
|
||||
.max_vertex_input_bindings
|
||||
.unwrap() as usize
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputBindingsExceeded {
|
||||
max: device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_bindings() as usize,
|
||||
.properties()
|
||||
.max_vertex_input_bindings
|
||||
.unwrap() as usize,
|
||||
obtained: binding_descriptions.len(),
|
||||
},
|
||||
);
|
||||
@ -521,15 +525,17 @@ where
|
||||
if attribute_descriptions.len()
|
||||
> device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_attributes() as usize
|
||||
.properties()
|
||||
.max_vertex_input_attributes
|
||||
.unwrap() as usize
|
||||
{
|
||||
return Err(
|
||||
GraphicsPipelineCreationError::MaxVertexInputAttributesExceeded {
|
||||
max: device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_vertex_input_attributes() as usize,
|
||||
.properties()
|
||||
.max_vertex_input_attributes
|
||||
.unwrap() as usize,
|
||||
obtained: attribute_descriptions.len(),
|
||||
},
|
||||
);
|
||||
@ -576,8 +582,9 @@ where
|
||||
if vertices_per_patch
|
||||
> device
|
||||
.physical_device()
|
||||
.limits()
|
||||
.max_tessellation_patch_size()
|
||||
.properties()
|
||||
.max_tessellation_patch_size
|
||||
.unwrap()
|
||||
{
|
||||
return Err(GraphicsPipelineCreationError::MaxTessellationPatchSizeExceeded);
|
||||
}
|
||||
@ -636,24 +643,54 @@ where
|
||||
return Err(GraphicsPipelineCreationError::MultiViewportFeatureNotEnabled);
|
||||
}
|
||||
|
||||
if vp_num > device.physical_device().limits().max_viewports() {
|
||||
if vp_num > device.physical_device().properties().max_viewports.unwrap() {
|
||||
return Err(GraphicsPipelineCreationError::MaxViewportsExceeded {
|
||||
obtained: vp_num,
|
||||
max: device.physical_device().limits().max_viewports(),
|
||||
max: device.physical_device().properties().max_viewports.unwrap(),
|
||||
});
|
||||
}
|
||||
|
||||
for vp in vp_vp.iter() {
|
||||
if vp.width > device.physical_device().limits().max_viewport_dimensions()[0] as f32
|
||||
|| vp.height > device.physical_device().limits().max_viewport_dimensions()[1] as f32
|
||||
if vp.width
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_viewport_dimensions
|
||||
.unwrap()[0] as f32
|
||||
|| vp.height
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_viewport_dimensions
|
||||
.unwrap()[1] as f32
|
||||
{
|
||||
return Err(GraphicsPipelineCreationError::MaxViewportDimensionsExceeded);
|
||||
}
|
||||
|
||||
if vp.x < device.physical_device().limits().viewport_bounds_range()[0]
|
||||
|| vp.x + vp.width > device.physical_device().limits().viewport_bounds_range()[1]
|
||||
|| vp.y < device.physical_device().limits().viewport_bounds_range()[0]
|
||||
|| vp.y + vp.height > device.physical_device().limits().viewport_bounds_range()[1]
|
||||
if vp.x
|
||||
< device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.viewport_bounds_range
|
||||
.unwrap()[0]
|
||||
|| vp.x + vp.width
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.viewport_bounds_range
|
||||
.unwrap()[1]
|
||||
|| vp.y
|
||||
< device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.viewport_bounds_range
|
||||
.unwrap()[0]
|
||||
|| vp.y + vp.height
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.viewport_bounds_range
|
||||
.unwrap()[1]
|
||||
{
|
||||
return Err(GraphicsPipelineCreationError::ViewportBoundsExceeded);
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ impl PipelineLayoutDesc {
|
||||
&self,
|
||||
device: &Device,
|
||||
) -> Result<(), limits_check::PipelineLayoutLimitsError> {
|
||||
limits_check::check_desc_against_limits(self, device.physical_device().limits())
|
||||
limits_check::check_desc_against_limits(self, device.physical_device().properties())
|
||||
}
|
||||
|
||||
/// Makes sure that `self` is a superset of `other`. Returns an `Err` if this is not the case.
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
use crate::descriptor::descriptor::DescriptorType;
|
||||
use crate::descriptor::descriptor::ShaderStages;
|
||||
use crate::instance::Limits;
|
||||
use crate::device::Properties;
|
||||
use crate::pipeline::layout::PipelineLayoutDesc;
|
||||
use crate::pipeline::layout::PipelineLayoutDescPcRange;
|
||||
use std::error;
|
||||
@ -20,7 +20,7 @@ use std::fmt;
|
||||
/// Checks whether the pipeline layout description fulfills the device limits requirements.
|
||||
pub fn check_desc_against_limits(
|
||||
desc: &PipelineLayoutDesc,
|
||||
limits: Limits,
|
||||
properties: &Properties,
|
||||
) -> Result<(), PipelineLayoutLimitsError> {
|
||||
let mut num_resources = Counter::default();
|
||||
let mut num_samplers = Counter::default();
|
||||
@ -72,140 +72,166 @@ pub fn check_desc_against_limits(
|
||||
}
|
||||
}
|
||||
|
||||
if desc.descriptor_sets().len() > limits.max_bound_descriptor_sets() as usize {
|
||||
if desc.descriptor_sets().len() > properties.max_bound_descriptor_sets.unwrap() as usize {
|
||||
return Err(PipelineLayoutLimitsError::MaxDescriptorSetsLimitExceeded {
|
||||
limit: limits.max_bound_descriptor_sets() as usize,
|
||||
limit: properties.max_bound_descriptor_sets.unwrap() as usize,
|
||||
requested: desc.descriptor_sets().len(),
|
||||
});
|
||||
}
|
||||
|
||||
if num_resources.max_per_stage() > limits.max_per_stage_resources() {
|
||||
if num_resources.max_per_stage() > properties.max_per_stage_resources.unwrap() {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageResourcesLimitExceeded {
|
||||
limit: limits.max_per_stage_resources(),
|
||||
limit: properties.max_per_stage_resources.unwrap(),
|
||||
requested: num_resources.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if num_samplers.max_per_stage() > limits.max_per_stage_descriptor_samplers() {
|
||||
if num_samplers.max_per_stage() > properties.max_per_stage_descriptor_samplers.unwrap() {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorSamplersLimitExceeded {
|
||||
limit: limits.max_per_stage_descriptor_samplers(),
|
||||
limit: properties.max_per_stage_descriptor_samplers.unwrap(),
|
||||
requested: num_samplers.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_uniform_buffers.max_per_stage() > limits.max_per_stage_descriptor_uniform_buffers() {
|
||||
if num_uniform_buffers.max_per_stage()
|
||||
> properties.max_per_stage_descriptor_uniform_buffers.unwrap()
|
||||
{
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorUniformBuffersLimitExceeded {
|
||||
limit: limits.max_per_stage_descriptor_uniform_buffers(),
|
||||
limit: properties.max_per_stage_descriptor_uniform_buffers.unwrap(),
|
||||
requested: num_uniform_buffers.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_buffers.max_per_stage() > limits.max_per_stage_descriptor_storage_buffers() {
|
||||
if num_storage_buffers.max_per_stage()
|
||||
> properties.max_per_stage_descriptor_storage_buffers.unwrap()
|
||||
{
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageBuffersLimitExceeded {
|
||||
limit: limits.max_per_stage_descriptor_storage_buffers(),
|
||||
limit: properties.max_per_stage_descriptor_storage_buffers.unwrap(),
|
||||
requested: num_storage_buffers.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_sampled_images.max_per_stage() > limits.max_per_stage_descriptor_sampled_images() {
|
||||
if num_sampled_images.max_per_stage()
|
||||
> properties.max_per_stage_descriptor_sampled_images.unwrap()
|
||||
{
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorSampledImagesLimitExceeded {
|
||||
limit: limits.max_per_stage_descriptor_sampled_images(),
|
||||
limit: properties.max_per_stage_descriptor_sampled_images.unwrap(),
|
||||
requested: num_sampled_images.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_images.max_per_stage() > limits.max_per_stage_descriptor_storage_images() {
|
||||
if num_storage_images.max_per_stage()
|
||||
> properties.max_per_stage_descriptor_storage_images.unwrap()
|
||||
{
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorStorageImagesLimitExceeded {
|
||||
limit: limits.max_per_stage_descriptor_storage_images(),
|
||||
limit: properties.max_per_stage_descriptor_storage_images.unwrap(),
|
||||
requested: num_storage_images.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_input_attachments.max_per_stage() > limits.max_per_stage_descriptor_input_attachments() {
|
||||
if num_input_attachments.max_per_stage()
|
||||
> properties
|
||||
.max_per_stage_descriptor_input_attachments
|
||||
.unwrap()
|
||||
{
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxPerStageDescriptorInputAttachmentsLimitExceeded {
|
||||
limit: limits.max_per_stage_descriptor_input_attachments(),
|
||||
limit: properties
|
||||
.max_per_stage_descriptor_input_attachments
|
||||
.unwrap(),
|
||||
requested: num_input_attachments.max_per_stage(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if num_samplers.total > limits.max_descriptor_set_samplers() {
|
||||
if num_samplers.total > properties.max_descriptor_set_samplers.unwrap() {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetSamplersLimitExceeded {
|
||||
limit: limits.max_descriptor_set_samplers(),
|
||||
limit: properties.max_descriptor_set_samplers.unwrap(),
|
||||
requested: num_samplers.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_uniform_buffers.total > limits.max_descriptor_set_uniform_buffers() {
|
||||
if num_uniform_buffers.total > properties.max_descriptor_set_uniform_buffers.unwrap() {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersLimitExceeded {
|
||||
limit: limits.max_descriptor_set_uniform_buffers(),
|
||||
limit: properties.max_descriptor_set_uniform_buffers.unwrap(),
|
||||
requested: num_uniform_buffers.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_uniform_buffers_dynamic > limits.max_descriptor_set_uniform_buffers_dynamic() {
|
||||
if num_uniform_buffers_dynamic
|
||||
> properties
|
||||
.max_descriptor_set_uniform_buffers_dynamic
|
||||
.unwrap()
|
||||
{
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetUniformBuffersDynamicLimitExceeded {
|
||||
limit: limits.max_descriptor_set_uniform_buffers_dynamic(),
|
||||
limit: properties
|
||||
.max_descriptor_set_uniform_buffers_dynamic
|
||||
.unwrap(),
|
||||
requested: num_uniform_buffers_dynamic,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_buffers.total > limits.max_descriptor_set_storage_buffers() {
|
||||
if num_storage_buffers.total > properties.max_descriptor_set_storage_buffers.unwrap() {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersLimitExceeded {
|
||||
limit: limits.max_descriptor_set_storage_buffers(),
|
||||
limit: properties.max_descriptor_set_storage_buffers.unwrap(),
|
||||
requested: num_storage_buffers.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_buffers_dynamic > limits.max_descriptor_set_storage_buffers_dynamic() {
|
||||
if num_storage_buffers_dynamic
|
||||
> properties
|
||||
.max_descriptor_set_storage_buffers_dynamic
|
||||
.unwrap()
|
||||
{
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageBuffersDynamicLimitExceeded {
|
||||
limit: limits.max_descriptor_set_storage_buffers_dynamic(),
|
||||
limit: properties
|
||||
.max_descriptor_set_storage_buffers_dynamic
|
||||
.unwrap(),
|
||||
requested: num_storage_buffers_dynamic,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_sampled_images.total > limits.max_descriptor_set_sampled_images() {
|
||||
if num_sampled_images.total > properties.max_descriptor_set_sampled_images.unwrap() {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetSampledImagesLimitExceeded {
|
||||
limit: limits.max_descriptor_set_sampled_images(),
|
||||
limit: properties.max_descriptor_set_sampled_images.unwrap(),
|
||||
requested: num_sampled_images.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_storage_images.total > limits.max_descriptor_set_storage_images() {
|
||||
if num_storage_images.total > properties.max_descriptor_set_storage_images.unwrap() {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetStorageImagesLimitExceeded {
|
||||
limit: limits.max_descriptor_set_storage_images(),
|
||||
limit: properties.max_descriptor_set_storage_images.unwrap(),
|
||||
requested: num_storage_images.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
if num_input_attachments.total > limits.max_descriptor_set_input_attachments() {
|
||||
if num_input_attachments.total > properties.max_descriptor_set_input_attachments.unwrap() {
|
||||
return Err(
|
||||
PipelineLayoutLimitsError::MaxDescriptorSetInputAttachmentsLimitExceeded {
|
||||
limit: limits.max_descriptor_set_input_attachments(),
|
||||
limit: properties.max_descriptor_set_input_attachments.unwrap(),
|
||||
requested: num_input_attachments.total,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
for &PipelineLayoutDescPcRange { offset, size, .. } in desc.push_constants() {
|
||||
if offset + size > limits.max_push_constants_size() as usize {
|
||||
if offset + size > properties.max_push_constants_size.unwrap() as usize {
|
||||
return Err(PipelineLayoutLimitsError::MaxPushConstantsSizeExceeded {
|
||||
limit: limits.max_push_constants_size() as usize,
|
||||
limit: properties.max_push_constants_size.unwrap() as usize,
|
||||
requested: offset + size,
|
||||
});
|
||||
}
|
||||
|
@ -399,3 +399,36 @@ impl MultiviewDesc {
|
||||
.count_ones()
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible resolve modes for depth and stencil attachments.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum ResolveMode {
|
||||
None = ash::vk::ResolveModeFlags::NONE.as_raw(),
|
||||
SampleZero = ash::vk::ResolveModeFlags::SAMPLE_ZERO.as_raw(),
|
||||
Average = ash::vk::ResolveModeFlags::AVERAGE.as_raw(),
|
||||
Min = ash::vk::ResolveModeFlags::MIN.as_raw(),
|
||||
Max = ash::vk::ResolveModeFlags::MAX.as_raw(),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ResolveModes {
|
||||
pub none: bool,
|
||||
pub sample_zero: bool,
|
||||
pub average: bool,
|
||||
pub min: bool,
|
||||
pub max: bool,
|
||||
}
|
||||
|
||||
impl From<ash::vk::ResolveModeFlags> for ResolveModes {
|
||||
#[inline]
|
||||
fn from(val: ash::vk::ResolveModeFlags) -> Self {
|
||||
Self {
|
||||
none: val.intersects(ash::vk::ResolveModeFlags::NONE),
|
||||
sample_zero: val.intersects(ash::vk::ResolveModeFlags::SAMPLE_ZERO),
|
||||
average: val.intersects(ash::vk::ResolveModeFlags::AVERAGE),
|
||||
min: val.intersects(ash::vk::ResolveModeFlags::MIN),
|
||||
max: val.intersects(ash::vk::ResolveModeFlags::MAX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -280,11 +280,11 @@ where
|
||||
|
||||
// Checking the dimensions against the limits.
|
||||
{
|
||||
let limits = device.physical_device().limits();
|
||||
let properties = device.physical_device().properties();
|
||||
let limits = [
|
||||
limits.max_framebuffer_width(),
|
||||
limits.max_framebuffer_height(),
|
||||
limits.max_framebuffer_layers(),
|
||||
properties.max_framebuffer_width.unwrap(),
|
||||
properties.max_framebuffer_height.unwrap(),
|
||||
properties.max_framebuffer_layers.unwrap(),
|
||||
];
|
||||
if dimensions[0] > limits[0] || dimensions[1] > limits[1] || dimensions[2] > limits[2] {
|
||||
return Err(FramebufferCreationError::DimensionsTooLarge);
|
||||
|
@ -30,11 +30,13 @@ pub use self::compat_atch::ensure_image_view_compatible;
|
||||
pub use self::compat_atch::IncompatibleRenderPassAttachmentError;
|
||||
pub use self::desc::AttachmentDesc;
|
||||
pub use self::desc::LoadOp;
|
||||
pub use self::desc::MultiviewDesc;
|
||||
pub use self::desc::RenderPassDesc;
|
||||
pub use self::desc::ResolveMode;
|
||||
pub use self::desc::ResolveModes;
|
||||
pub use self::desc::StoreOp;
|
||||
pub use self::desc::SubpassDependencyDesc;
|
||||
pub use self::desc::SubpassDesc;
|
||||
pub use self::desc::MultiviewDesc;
|
||||
pub use self::framebuffer::Framebuffer;
|
||||
pub use self::framebuffer::FramebufferAbstract;
|
||||
pub use self::framebuffer::FramebufferBuilder;
|
||||
|
@ -286,7 +286,11 @@ impl RenderPass {
|
||||
|
||||
for pass in description.subpasses() {
|
||||
if pass.color_attachments.len() as u32
|
||||
> device.physical_device().limits().max_color_attachments()
|
||||
> device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_color_attachments
|
||||
.unwrap()
|
||||
{
|
||||
return Err(RenderPassCreationError::ColorAttachmentsLimitExceeded);
|
||||
}
|
||||
@ -383,8 +387,8 @@ impl RenderPass {
|
||||
debug_assert!(
|
||||
device
|
||||
.physical_device()
|
||||
.extended_properties()
|
||||
.max_multiview_view_count()
|
||||
.properties()
|
||||
.max_multiview_view_count
|
||||
.unwrap_or(0)
|
||||
>= multiview.used_layer_count()
|
||||
);
|
||||
@ -850,7 +854,13 @@ mod tests {
|
||||
fn too_many_color_atch() {
|
||||
let (device, _) = gfx_dev_and_queue!();
|
||||
|
||||
if device.physical_device().limits().max_color_attachments() >= 10 {
|
||||
if device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_color_attachments
|
||||
.unwrap()
|
||||
>= 10
|
||||
{
|
||||
return; // test ignored
|
||||
}
|
||||
|
||||
|
@ -266,7 +266,11 @@ impl Sampler {
|
||||
return Err(SamplerCreationError::SamplerAnisotropyFeatureNotEnabled);
|
||||
}
|
||||
|
||||
let limit = device.physical_device().limits().max_sampler_anisotropy();
|
||||
let limit = device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_sampler_anisotropy
|
||||
.unwrap();
|
||||
if max_anisotropy > limit {
|
||||
return Err(SamplerCreationError::AnisotropyLimitExceeded {
|
||||
requested: max_anisotropy,
|
||||
@ -277,7 +281,11 @@ impl Sampler {
|
||||
|
||||
// Check mip_lod_bias value.
|
||||
{
|
||||
let limit = device.physical_device().limits().max_sampler_lod_bias();
|
||||
let limit = device
|
||||
.physical_device()
|
||||
.properties()
|
||||
.max_sampler_lod_bias
|
||||
.unwrap();
|
||||
if mip_lod_bias > limit {
|
||||
return Err(SamplerCreationError::MipLodBiasLimitExceeded {
|
||||
requested: mip_lod_bias,
|
||||
|
@ -903,7 +903,8 @@ impl<W> SwapchainBuilder<W> {
|
||||
|
||||
/// Sets the dimensions of the images.
|
||||
///
|
||||
/// The default is `None`, which means the value of [`Capabilities::current_extent`] will be
|
||||
/// The default is `None`, which means the value of
|
||||
/// [`Capabilities::current_extent`](crate::swapchain::Capabilities::current_extent) will be
|
||||
/// used. Setting this will override it with a custom `Some` value.
|
||||
#[inline]
|
||||
pub fn dimensions(mut self, dimensions: [u32; 2]) -> Self {
|
||||
|
@ -13,7 +13,7 @@ use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
|
||||
/// Represents an API version of Vulkan.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Version {
|
||||
/// Major version number.
|
||||
pub major: u32,
|
||||
|
Loading…
Reference in New Issue
Block a user