1 // Copyright (c) 2021 The Vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9
10 use heck::SnakeCase;
11 use indexmap::IndexMap;
12 use regex::Regex;
13 use std::{
14 collections::{hash_map::Entry, HashMap},
15 io::Write,
16 };
17 use vk_parse::{Extension, Type, TypeMember, TypeMemberMarkup, TypeSpec};
18
write<W: Write>( writer: &mut W, types: &HashMap<&str, (&Type, Vec<&str>)>, extensions: &IndexMap<&str, &Extension>, )19 pub fn write<W: Write>(
20 writer: &mut W,
21 types: &HashMap<&str, (&Type, Vec<&str>)>,
22 extensions: &IndexMap<&str, &Extension>,
23 ) {
24 write!(writer, "crate::device::properties::properties! {{").unwrap();
25
26 for feat in make_vulkano_properties(&types) {
27 write!(writer, "\n\t{} => {{", feat.member).unwrap();
28 write_doc(writer, &feat);
29 write!(writer, "\n\t\tty: {},", feat.ty).unwrap();
30 write!(writer, "\n\t\tffi_name: {},", feat.ffi_name).unwrap();
31 write!(
32 writer,
33 "\n\t\tffi_members: [{}],",
34 feat.ffi_members.join(", ")
35 )
36 .unwrap();
37 write!(writer, "\n\t\trequired: {},", feat.required).unwrap();
38 write!(writer, "\n\t}},").unwrap();
39 }
40
41 write!(
42 writer,
43 "\n}}\n\ncrate::device::properties::properties_ffi! {{\n\tapi_version,\n\tdevice_extensions,\n\tinstance_extensions,"
44 )
45 .unwrap();
46
47 for ffi in make_vulkano_properties_ffi(types, extensions) {
48 write!(writer, "\n\t{} => {{", ffi.member).unwrap();
49 write!(writer, "\n\t\tty: {},", ffi.ty).unwrap();
50 write!(
51 writer,
52 "\n\t\tprovided_by: [{}],",
53 ffi.provided_by.join(", ")
54 )
55 .unwrap();
56 write!(writer, "\n\t\tconflicts: [{}],", ffi.conflicts.join(", ")).unwrap();
57 write!(writer, "\n\t}},").unwrap();
58 }
59
60 write!(writer, "\n}}").unwrap();
61 }
62
63 #[derive(Clone, Debug)]
64 struct VulkanoProperty {
65 member: String,
66 ty: String,
67 vulkan_doc: String,
68 ffi_name: String,
69 ffi_members: Vec<String>,
70 required: bool,
71 }
72
make_vulkano_properties(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<VulkanoProperty>73 fn make_vulkano_properties(types: &HashMap<&str, (&Type, Vec<&str>)>) -> Vec<VulkanoProperty> {
74 let mut properties = HashMap::new();
75 std::array::IntoIter::new([
76 &types["VkPhysicalDeviceProperties"],
77 &types["VkPhysicalDeviceLimits"],
78 &types["VkPhysicalDeviceSparseProperties"],
79 ])
80 .chain(sorted_structs(types).into_iter())
81 .filter(|(ty, _)| {
82 let name = ty.name.as_ref().map(|s| s.as_str());
83 name == Some("VkPhysicalDeviceProperties")
84 || name == Some("VkPhysicalDeviceLimits")
85 || name == Some("VkPhysicalDeviceSparseProperties")
86 || ty.structextends.as_ref().map(|s| s.as_str()) == Some("VkPhysicalDeviceProperties2")
87 })
88 .for_each(|(ty, _)| {
89 let vulkan_ty_name = ty.name.as_ref().unwrap();
90 let required = vulkan_ty_name == "VkPhysicalDeviceProperties"
91 || vulkan_ty_name == "VkPhysicalDeviceLimits"
92 || vulkan_ty_name == "VkPhysicalDeviceSparseProperties"
93 ;
94
95 let ty_name = if vulkan_ty_name == "VkPhysicalDeviceProperties" {
96 "properties_vulkan10.properties".to_owned()
97 } else if vulkan_ty_name == "VkPhysicalDeviceLimits" {
98 "properties_vulkan10.properties.limits".to_owned()
99 } else if vulkan_ty_name == "VkPhysicalDeviceSparseProperties" {
100 "properties_vulkan10.properties.sparse_properties".to_owned()
101 } else {
102 ffi_member(vulkan_ty_name)
103 };
104
105 members(ty)
106 .into_iter()
107 .for_each(|Member { name, ty, len }| {
108 if ty == "VkPhysicalDeviceLimits" || ty == "VkPhysicalDeviceSparseProperties" {
109 return;
110 }
111
112 let vulkano_member = name.to_snake_case();
113 let vulkano_ty = match name {
114 "apiVersion" => "crate::Version",
115 _ => vulkano_type(ty, len),
116 };
117 match properties.entry(vulkano_member.clone()) {
118 Entry::Vacant(entry) => {
119 entry.insert(VulkanoProperty {
120 member: vulkano_member.clone(),
121 ty: vulkano_ty.to_owned(),
122 vulkan_doc: format!("{}.html#limits-{}", vulkan_ty_name, name),
123 ffi_name: vulkano_member,
124 ffi_members: vec![ty_name.to_owned()],
125 required: required,
126 });
127 }
128 Entry::Occupied(entry) => {
129 entry.into_mut().ffi_members.push(ty_name.to_owned());
130 }
131 };
132 });
133 });
134
135 let mut names: Vec<_> = properties
136 .values()
137 .map(|feat| feat.member.clone())
138 .collect();
139 names.sort_unstable();
140 names
141 .into_iter()
142 .map(|name| properties.remove(&name).unwrap())
143 .collect()
144 }
145
write_doc<W>(writer: &mut W, feat: &VulkanoProperty) where W: Write,146 fn write_doc<W>(writer: &mut W, feat: &VulkanoProperty)
147 where
148 W: Write,
149 {
150 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();
151 write!(writer, "\n\t\t\",").unwrap();
152 }
153
154 #[derive(Clone, Debug)]
155 struct VulkanoPropertyFfi {
156 member: String,
157 ty: String,
158 provided_by: Vec<String>,
159 conflicts: Vec<String>,
160 }
161
make_vulkano_properties_ffi<'a>( types: &'a HashMap<&str, (&Type, Vec<&str>)>, extensions: &IndexMap<&'a str, &Extension>, ) -> Vec<VulkanoPropertyFfi>162 fn make_vulkano_properties_ffi<'a>(
163 types: &'a HashMap<&str, (&Type, Vec<&str>)>,
164 extensions: &IndexMap<&'a str, &Extension>,
165 ) -> Vec<VulkanoPropertyFfi> {
166 let mut property_included_in: HashMap<&str, Vec<&str>> = HashMap::new();
167 sorted_structs(types)
168 .into_iter()
169 .map(|(ty, provided_by)| {
170 let ty_name = ty.name.as_ref().unwrap();
171 let provided_by = provided_by
172 .iter()
173 .map(|provided_by| {
174 if let Some(version) = provided_by.strip_prefix("VK_VERSION_") {
175 format!("api_version >= crate::Version::V{}", version)
176 } else {
177 format!(
178 "{}_extensions.{}",
179 extensions[provided_by].ext_type.as_ref().unwrap().as_str(),
180 provided_by
181 .strip_prefix("VK_")
182 .unwrap()
183 .to_ascii_lowercase()
184 )
185 }
186 })
187 .collect();
188 let mut conflicts = vec![];
189 members(ty).into_iter().for_each(|Member { name, .. }| {
190 match property_included_in.entry(name) {
191 Entry::Vacant(entry) => {
192 entry.insert(vec![ty_name]);
193 }
194 Entry::Occupied(entry) => {
195 let conflicters = entry.into_mut();
196 conflicters.iter().for_each(|conflicter| {
197 let conflicter = ffi_member(conflicter);
198 if !conflicts.contains(&conflicter) {
199 conflicts.push(conflicter);
200 }
201 });
202 conflicters.push(ty_name);
203 }
204 }
205 });
206
207 VulkanoPropertyFfi {
208 member: ffi_member(ty_name),
209 ty: ty_name.strip_prefix("Vk").unwrap().to_owned(),
210 provided_by,
211 conflicts,
212 }
213 })
214 .collect()
215 }
216
sorted_structs<'a>( types: &'a HashMap<&str, (&'a Type, Vec<&'a str>)>, ) -> Vec<&'a (&'a Type, Vec<&'a str>)>217 fn sorted_structs<'a>(
218 types: &'a HashMap<&str, (&'a Type, Vec<&'a str>)>,
219 ) -> Vec<&'a (&'a Type, Vec<&'a str>)> {
220 let mut structs: Vec<_> = types
221 .values()
222 .filter(|(ty, _)| {
223 ty.structextends.as_ref().map(|s| s.as_str()) == Some("VkPhysicalDeviceProperties2")
224 })
225 .collect();
226 let regex = Regex::new(r"^VkPhysicalDeviceVulkan\d+Properties$").unwrap();
227 structs.sort_unstable_by_key(|&(ty, provided_by)| {
228 let name = ty.name.as_ref().unwrap();
229 (
230 !regex.is_match(name),
231 if let Some(version) = provided_by
232 .iter()
233 .find_map(|s| s.strip_prefix("VK_VERSION_"))
234 {
235 let (major, minor) = version.split_once('_').unwrap();
236 major.parse::<i32>().unwrap() << 22 | minor.parse::<i32>().unwrap() << 12
237 } else if provided_by
238 .iter()
239 .find(|s| s.starts_with("VK_KHR_"))
240 .is_some()
241 {
242 i32::MAX - 2
243 } else if provided_by
244 .iter()
245 .find(|s| s.starts_with("VK_EXT_"))
246 .is_some()
247 {
248 i32::MAX - 1
249 } else {
250 i32::MAX
251 },
252 name,
253 )
254 });
255
256 structs
257 }
258
ffi_member(ty_name: &str) -> String259 fn ffi_member(ty_name: &str) -> String {
260 let ty_name = ty_name
261 .strip_prefix("VkPhysicalDevice")
262 .unwrap()
263 .to_snake_case();
264 let (base, suffix) = ty_name.rsplit_once("_properties").unwrap();
265 format!("properties_{}{}", base, suffix)
266 }
267
268 struct Member<'a> {
269 name: &'a str,
270 ty: &'a str,
271 len: Option<&'a str>,
272 }
273
members(ty: &Type) -> Vec<Member>274 fn members(ty: &Type) -> Vec<Member> {
275 let regex = Regex::new(r"\[([A-Za-z0-9_]+)\]\s*$").unwrap();
276 if let TypeSpec::Members(members) = &ty.spec {
277 members
278 .iter()
279 .filter_map(|member| {
280 if let TypeMember::Definition(def) = member {
281 let name = def.markup.iter().find_map(|markup| match markup {
282 TypeMemberMarkup::Name(name) => Some(name.as_str()),
283 _ => None,
284 });
285 let ty = def.markup.iter().find_map(|markup| match markup {
286 TypeMemberMarkup::Type(ty) => Some(ty.as_str()),
287 _ => None,
288 });
289 let len = def
290 .markup
291 .iter()
292 .find_map(|markup| match markup {
293 TypeMemberMarkup::Enum(len) => Some(len.as_str()),
294 _ => None,
295 })
296 .or_else(|| {
297 regex
298 .captures(&def.code)
299 .and_then(|cap| cap.get(1))
300 .map(|m| m.as_str())
301 });
302 if name != Some("sType") && name != Some("pNext") {
303 return name.map(|name| Member {
304 name,
305 ty: ty.unwrap(),
306 len,
307 });
308 }
309 }
310 None
311 })
312 .collect()
313 } else {
314 vec![]
315 }
316 }
317
vulkano_type(ty: &str, len: Option<&str>) -> &'static str318 fn vulkano_type(ty: &str, len: Option<&str>) -> &'static str {
319 if let Some(len) = len {
320 match ty {
321 "char" => "String",
322 "uint8_t" if len == "VK_LUID_SIZE" => "[u8; 8]",
323 "uint8_t" if len == "VK_UUID_SIZE" => "[u8; 16]",
324 "uint32_t" if len == "2" => "[u32; 2]",
325 "uint32_t" if len == "3" => "[u32; 3]",
326 "float" if len == "2" => "[f32; 2]",
327 _ => unimplemented!("{}[{}]", ty, len),
328 }
329 } else {
330 match ty {
331 "float" => "f32",
332 "int32_t" => "i32",
333 "int64_t" => "i64",
334 "size_t" => "usize",
335 "uint8_t" => "u8",
336 "uint32_t" => "u32",
337 "uint64_t" => "u64",
338 "VkBool32" => "bool",
339 "VkConformanceVersion" => "crate::device::physical::ConformanceVersion",
340 "VkDeviceSize" => "crate::DeviceSize",
341 "VkDriverId" => "crate::device::physical::DriverId",
342 "VkExtent2D" => "[u32; 2]",
343 "VkPhysicalDeviceType" => "crate::device::physical::PhysicalDeviceType",
344 "VkPointClippingBehavior" => "crate::device::physical::PointClippingBehavior",
345 "VkResolveModeFlags" => "crate::render_pass::ResolveModes",
346 "VkSampleCountFlags" => "crate::image::SampleCounts",
347 "VkSampleCountFlagBits" => "crate::image::SampleCount",
348 "VkShaderCorePropertiesFlagsAMD" => "crate::device::physical::ShaderCoreProperties",
349 "VkShaderFloatControlsIndependence" => {
350 "crate::device::physical::ShaderFloatControlsIndependence"
351 }
352 "VkShaderStageFlags" => "crate::pipeline::shader::ShaderStages",
353 "VkSubgroupFeatureFlags" => "crate::device::physical::SubgroupFeatures",
354 _ => unimplemented!("{}", ty),
355 }
356 }
357 }
358