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 super::{
11 spirv_grammar::{SpirvGrammar, SpirvKindEnumerant},
12 write_file, IndexMap, VkRegistryData,
13 };
14 use heck::ToSnakeCase;
15 use indexmap::map::Entry;
16 use once_cell::sync::Lazy;
17 use proc_macro2::{Ident, TokenStream};
18 use quote::{format_ident, quote};
19 use regex::Regex;
20 use vk_parse::SpirvExtOrCap;
21
write(vk_data: &VkRegistryData, grammar: &SpirvGrammar)22 pub fn write(vk_data: &VkRegistryData, grammar: &SpirvGrammar) {
23 let grammar_enumerants = grammar
24 .operand_kinds
25 .iter()
26 .find(|operand_kind| operand_kind.kind == "Capability")
27 .unwrap()
28 .enumerants
29 .as_slice();
30 let spirv_capabilities_output = spirv_reqs_output(
31 &spirv_capabilities_members(&vk_data.spirv_capabilities, grammar_enumerants),
32 false,
33 );
34 let spirv_extensions_output =
35 spirv_reqs_output(&spirv_extensions_members(&vk_data.spirv_extensions), true);
36 write_file(
37 "spirv_reqs.rs",
38 format!(
39 "vk.xml header version {}.{}.{}",
40 vk_data.header_version.0, vk_data.header_version.1, vk_data.header_version.2
41 ),
42 quote! {
43 #spirv_capabilities_output
44 #spirv_extensions_output
45 },
46 );
47 }
48
49 #[derive(Clone, Debug)]
50 struct SpirvReqsMember {
51 name: String,
52 enables: Vec<(Enable, String)>,
53 }
54
55 #[derive(Clone, Debug, PartialEq)]
56 enum Enable {
57 Core((String, String)),
58 Extension(Ident),
59 Feature(Ident),
60 Property((Ident, PropertyValue)),
61 }
62
63 #[derive(Clone, Debug, PartialEq)]
64 enum PropertyValue {
65 Bool,
66 BoolMember(Vec<Ident>),
67 }
68
spirv_reqs_output(members: &[SpirvReqsMember], extension: bool) -> TokenStream69 fn spirv_reqs_output(members: &[SpirvReqsMember], extension: bool) -> TokenStream {
70 let items = members.iter().map(|SpirvReqsMember { name, enables }| {
71 let arm = if extension {
72 quote! { #name }
73 } else {
74 let name = format_ident!("{}", name);
75 quote! { Capability::#name }
76 };
77
78 if enables.is_empty() {
79 quote! {
80 #arm => (),
81 }
82 } else {
83 let enables_items = enables.iter().map(|(enable, _description)| match enable {
84 Enable::Core((major, minor)) => {
85 let version = format_ident!("V{}_{}", major, minor);
86 quote! {
87 device.api_version() >= Version::#version
88 }
89 }
90 Enable::Extension(extension) => quote! {
91 device.enabled_extensions().#extension
92 },
93 Enable::Feature(feature) => quote! {
94 device.enabled_features().#feature
95 },
96 Enable::Property((name, value)) => {
97 let access = match value {
98 PropertyValue::Bool => quote! {},
99 PropertyValue::BoolMember(member) => quote! {
100 .map(|x| x.intersects(#(#member)::*))
101 },
102 };
103
104 quote! {
105 device.physical_device().properties().#name #access .unwrap_or(false)
106 }
107 }
108 });
109
110 let description_items = enables.iter().map(|(_enable, description)| description);
111
112 quote! {
113 #arm => {
114 if !(#(#enables_items)||*) {
115 return Err(ShaderSupportError::RequirementsNotMet(&[
116 #(#description_items),*
117 ]));
118 }
119 },
120 }
121 }
122 });
123
124 if extension {
125 quote! {
126 fn check_spirv_extension(device: &Device, extension: &str) -> Result<(), ShaderSupportError> {
127 match extension {
128 #(#items)*
129 _ => return Err(ShaderSupportError::NotSupportedByVulkan),
130 }
131 Ok(())
132 }
133 }
134 } else {
135 quote! {
136 fn check_spirv_capability(device: &Device, capability: Capability) -> Result<(), ShaderSupportError> {
137 match capability {
138 #(#items)*
139 _ => return Err(ShaderSupportError::NotSupportedByVulkan),
140 }
141 Ok(())
142 }
143 }
144 }
145 }
146
spirv_capabilities_members( capabilities: &[&SpirvExtOrCap], grammar_enumerants: &[SpirvKindEnumerant], ) -> Vec<SpirvReqsMember>147 fn spirv_capabilities_members(
148 capabilities: &[&SpirvExtOrCap],
149 grammar_enumerants: &[SpirvKindEnumerant],
150 ) -> Vec<SpirvReqsMember> {
151 let mut members: IndexMap<String, SpirvReqsMember> = IndexMap::default();
152
153 for ext_or_cap in capabilities {
154 let mut enables: Vec<_> = ext_or_cap.enables.iter().filter_map(make_enable).collect();
155 enables.dedup();
156
157 // Find the capability in the list of enumerants, then go backwards through the list to find
158 // the first enumerant with the same value.
159 let enumerant_pos = match grammar_enumerants
160 .iter()
161 .position(|enumerant| enumerant.enumerant == ext_or_cap.name)
162 {
163 Some(pos) => pos,
164 // This can happen if the grammar file is behind on the vk.xml file.
165 None => continue,
166 };
167 let enumerant_value = &grammar_enumerants[enumerant_pos].value;
168
169 let name = if let Some(enumerant) = grammar_enumerants[..enumerant_pos]
170 .iter()
171 .rev()
172 .take_while(|enumerant| &enumerant.value == enumerant_value)
173 .last()
174 {
175 // Another enumerant was found with the same value, so this one is an alias.
176 &enumerant.enumerant
177 } else {
178 // No other enumerant was found, so this is its canonical name.
179 &ext_or_cap.name
180 };
181
182 match members.entry(name.clone()) {
183 Entry::Occupied(entry) => {
184 entry.into_mut().enables.extend(enables);
185 }
186 Entry::Vacant(entry) => {
187 entry.insert(SpirvReqsMember {
188 name: name.clone(),
189 enables,
190 });
191 }
192 }
193 }
194
195 members.into_iter().map(|(_, v)| v).collect()
196 }
197
spirv_extensions_members(extensions: &[&SpirvExtOrCap]) -> Vec<SpirvReqsMember>198 fn spirv_extensions_members(extensions: &[&SpirvExtOrCap]) -> Vec<SpirvReqsMember> {
199 extensions
200 .iter()
201 .map(|ext_or_cap| {
202 let enables: Vec<_> = ext_or_cap.enables.iter().filter_map(make_enable).collect();
203
204 SpirvReqsMember {
205 name: ext_or_cap.name.clone(),
206 enables,
207 }
208 })
209 .collect()
210 }
211
make_enable(enable: &vk_parse::Enable) -> Option<(Enable, String)>212 fn make_enable(enable: &vk_parse::Enable) -> Option<(Enable, String)> {
213 static VK_API_VERSION: Lazy<Regex> =
214 Lazy::new(|| Regex::new(r"^VK_(?:API_)?VERSION_(\d+)_(\d+)$").unwrap());
215 static BIT: Lazy<Regex> = Lazy::new(|| Regex::new(r"_BIT(?:_NV)?$").unwrap());
216
217 if matches!(enable, vk_parse::Enable::Version(version) if version == "VK_VERSION_1_0") {
218 return None;
219 }
220
221 Some(match enable {
222 vk_parse::Enable::Version(version) => {
223 let captures = VK_API_VERSION.captures(version).unwrap();
224 let major = captures.get(1).unwrap().as_str();
225 let minor = captures.get(1).unwrap().as_str();
226
227 (
228 Enable::Core((major.parse().unwrap(), minor.parse().unwrap())),
229 format!("Vulkan API version {}.{}", major, minor),
230 )
231 }
232 vk_parse::Enable::Extension(extension) => {
233 let extension_name = extension.strip_prefix("VK_").unwrap().to_snake_case();
234
235 (
236 Enable::Extension(format_ident!("{}", extension_name)),
237 format!("device extension `{}`", extension_name),
238 )
239 }
240 vk_parse::Enable::Feature(feature) => {
241 let feature_name = feature.feature.to_snake_case();
242
243 (
244 Enable::Feature(format_ident!("{}", feature_name)),
245 format!("feature `{}`", feature_name),
246 )
247 }
248 vk_parse::Enable::Property(property) => {
249 let property_name = property.member.to_snake_case();
250
251 let (value, description) = if property.value == "VK_TRUE" {
252 (PropertyValue::Bool, format!("property `{}`", property_name))
253 } else if let Some(member) = property.value.strip_prefix("VK_SUBGROUP_FEATURE_") {
254 let member = BIT.replace(member, "");
255 (
256 PropertyValue::BoolMember(
257 ["crate", "device", "physical", "SubgroupFeatures", &member]
258 .into_iter()
259 .map(|s| format_ident!("{}", s))
260 .collect(),
261 ),
262 format!("property `{}.{}`", property_name, member),
263 )
264 } else {
265 unimplemented!()
266 };
267
268 (
269 Enable::Property((format_ident!("{}", property_name), value)),
270 description,
271 )
272 }
273 _ => unimplemented!(),
274 })
275 }
276