• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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