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 std::io::Write;
13 use vk_parse::Extension;
14
15 // This is not included in vk.xml, so it's added here manually
required_if_supported(name: &str) -> bool16 fn required_if_supported(name: &str) -> bool {
17 match name {
18 "VK_KHR_portability_subset" => true,
19 _ => false,
20 }
21 }
22
conflicts_extensions(name: &str) -> &'static [&'static str]23 fn conflicts_extensions(name: &str) -> &'static [&'static str] {
24 match name {
25 "VK_KHR_buffer_device_address" => &["VK_EXT_buffer_device_address"],
26 "VK_EXT_buffer_device_address" => &["VK_KHR_buffer_device_address"],
27 _ => &[],
28 }
29 }
30
write<W: Write>(writer: &mut W, extensions: &IndexMap<&str, &Extension>)31 pub fn write<W: Write>(writer: &mut W, extensions: &IndexMap<&str, &Extension>) {
32 write_device_extensions(writer, make_vulkano_extensions("device", &extensions));
33 write!(writer, "\n\n").unwrap();
34 write_instance_extensions(writer, make_vulkano_extensions("instance", &extensions));
35 }
36
37 #[derive(Clone, Debug)]
38 struct VulkanoExtension {
39 member: String,
40 raw: String,
41 requires_core: (u16, u16),
42 requires_device_extensions: Vec<String>,
43 requires_instance_extensions: Vec<String>,
44 required_if_supported: bool,
45 conflicts_device_extensions: Vec<String>,
46 status: Option<ExtensionStatus>,
47 }
48
49 #[derive(Clone, Debug)]
50 enum Replacement {
51 Core((u16, u16)),
52 DeviceExtension(String),
53 InstanceExtension(String),
54 }
55
56 #[derive(Clone, Debug)]
57 enum ExtensionStatus {
58 Promoted(Replacement),
59 Deprecated(Option<Replacement>),
60 }
61
make_vulkano_extensions( ty: &str, extensions: &IndexMap<&str, &Extension>, ) -> Vec<VulkanoExtension>62 fn make_vulkano_extensions(
63 ty: &str,
64 extensions: &IndexMap<&str, &Extension>,
65 ) -> Vec<VulkanoExtension> {
66 extensions
67 .values()
68 .filter(|ext| ext.ext_type.as_ref().unwrap() == ty)
69 .map(|ext| {
70 let raw = ext.name.to_owned();
71 let member = raw.strip_prefix("VK_").unwrap().to_snake_case();
72 let (major, minor) = ext
73 .requires_core
74 .as_ref()
75 .map(|s| s.as_str())
76 .unwrap_or("1.0")
77 .split_once('.')
78 .unwrap();
79 let requires_extensions: Vec<_> = ext
80 .requires
81 .as_ref()
82 .map(|s| s.split(',').collect())
83 .unwrap_or_default();
84 let conflicts_extensions = conflicts_extensions(&ext.name);
85
86 VulkanoExtension {
87 member: member.clone(),
88 raw,
89 requires_core: (major.parse().unwrap(), minor.parse().unwrap()),
90 requires_device_extensions: requires_extensions
91 .iter()
92 .filter(|&&vk_name| extensions[vk_name].ext_type.as_ref().unwrap() == "device")
93 .map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case())
94 .collect(),
95 requires_instance_extensions: requires_extensions
96 .iter()
97 .filter(|&&vk_name| {
98 extensions[vk_name].ext_type.as_ref().unwrap() == "instance"
99 })
100 .map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case())
101 .collect(),
102 required_if_supported: required_if_supported(ext.name.as_str()),
103 conflicts_device_extensions: conflicts_extensions
104 .iter()
105 .filter(|&&vk_name| extensions[vk_name].ext_type.as_ref().unwrap() == "device")
106 .map(|vk_name| vk_name.strip_prefix("VK_").unwrap().to_snake_case())
107 .collect(),
108 status: ext
109 .promotedto
110 .as_ref()
111 .map(|s| s.as_str())
112 .and_then(|pr| {
113 if let Some(version) = pr.strip_prefix("VK_VERSION_") {
114 let (major, minor) = version.split_once('_').unwrap();
115 Some(ExtensionStatus::Promoted(Replacement::Core((
116 major.parse().unwrap(),
117 minor.parse().unwrap(),
118 ))))
119 } else {
120 let member = pr.strip_prefix("VK_").unwrap().to_snake_case();
121 match extensions[pr].ext_type.as_ref().unwrap().as_str() {
122 "device" => Some(ExtensionStatus::Promoted(
123 Replacement::DeviceExtension(member),
124 )),
125 "instance" => Some(ExtensionStatus::Promoted(
126 Replacement::InstanceExtension(member),
127 )),
128 _ => unreachable!(),
129 }
130 }
131 })
132 .or_else(|| {
133 ext.deprecatedby
134 .as_ref()
135 .map(|s| s.as_str())
136 .and_then(|depr| {
137 if depr.is_empty() {
138 Some(ExtensionStatus::Deprecated(None))
139 } else if let Some(version) = depr.strip_prefix("VK_VERSION_") {
140 let (major, minor) = version.split_once('_').unwrap();
141 Some(ExtensionStatus::Deprecated(Some(Replacement::Core((
142 major.parse().unwrap(),
143 minor.parse().unwrap(),
144 )))))
145 } else {
146 let member = depr.strip_prefix("VK_").unwrap().to_snake_case();
147 match extensions[depr].ext_type.as_ref().unwrap().as_str() {
148 "device" => Some(ExtensionStatus::Deprecated(Some(
149 Replacement::DeviceExtension(member),
150 ))),
151 "instance" => Some(ExtensionStatus::Deprecated(Some(
152 Replacement::InstanceExtension(member),
153 ))),
154 _ => unreachable!(),
155 }
156 }
157 })
158 }),
159 }
160 })
161 .collect()
162 }
163
write_device_extensions<W, I>(writer: &mut W, extensions: I) where W: Write, I: IntoIterator<Item = VulkanoExtension>,164 fn write_device_extensions<W, I>(writer: &mut W, extensions: I)
165 where
166 W: Write,
167 I: IntoIterator<Item = VulkanoExtension>,
168 {
169 write!(writer, "crate::device::extensions::device_extensions! {{").unwrap();
170 for ext in extensions {
171 write!(writer, "\n\t{} => {{", ext.member).unwrap();
172 write_doc(writer, &ext);
173 write!(writer, "\n\t\traw: b\"{}\",", ext.raw).unwrap();
174 write!(
175 writer,
176 "\n\t\trequires_core: crate::Version::V{}_{},",
177 ext.requires_core.0, ext.requires_core.1
178 )
179 .unwrap();
180 write!(
181 writer,
182 "\n\t\trequires_device_extensions: [{}],",
183 ext.requires_device_extensions.join(", ")
184 )
185 .unwrap();
186 write!(
187 writer,
188 "\n\t\trequires_instance_extensions: [{}],",
189 ext.requires_instance_extensions.join(", ")
190 )
191 .unwrap();
192 write!(
193 writer,
194 "\n\t\trequired_if_supported: {},",
195 ext.required_if_supported
196 )
197 .unwrap();
198 write!(
199 writer,
200 "\n\t\tconflicts_device_extensions: [{}],",
201 ext.conflicts_device_extensions.join(", ")
202 )
203 .unwrap();
204
205 /*if let Some(promoted_to_core) = ext.promoted_to_core {
206 write!(
207 writer,
208 "\n\t\tpromoted_to_core: Some(Version::V{}_{}),",
209 promoted_to_core.0, promoted_to_core.1
210 )
211 .unwrap();
212 } else {
213 write!(writer, "\n\t\tpromoted_to_core: None,",).unwrap();
214 }*/
215
216 write!(writer, "\n\t}},").unwrap();
217 }
218 write!(writer, "\n}}").unwrap();
219 }
220
write_instance_extensions<W, I>(writer: &mut W, extensions: I) where W: Write, I: IntoIterator<Item = VulkanoExtension>,221 fn write_instance_extensions<W, I>(writer: &mut W, extensions: I)
222 where
223 W: Write,
224 I: IntoIterator<Item = VulkanoExtension>,
225 {
226 write!(
227 writer,
228 "crate::instance::extensions::instance_extensions! {{"
229 )
230 .unwrap();
231 for ext in extensions {
232 write!(writer, "\n\t{} => {{", ext.member).unwrap();
233 write_doc(writer, &ext);
234 write!(writer, "\n\t\traw: b\"{}\",", ext.raw).unwrap();
235 write!(
236 writer,
237 "\n\t\trequires_core: crate::Version::V{}_{},",
238 ext.requires_core.0, ext.requires_core.1
239 )
240 .unwrap();
241 write!(
242 writer,
243 "\n\t\trequires_instance_extensions: [{}],",
244 ext.requires_instance_extensions.join(", ")
245 )
246 .unwrap();
247
248 /*if let Some(promoted_to_core) = ext.promoted_to_core {
249 write!(
250 writer,
251 "\n\t\tpromoted_to_core: Some(crate::Version::V{}_{}),",
252 promoted_to_core.0, promoted_to_core.1
253 )
254 .unwrap();
255 } else {
256 write!(writer, "\n\t\tpromoted_to_core: None,",).unwrap();
257 }*/
258
259 write!(writer, "\n\t}},").unwrap();
260 }
261 write!(writer, "\n}}").unwrap();
262 }
263
write_doc<W>(writer: &mut W, ext: &VulkanoExtension) where W: Write,264 fn write_doc<W>(writer: &mut W, ext: &VulkanoExtension)
265 where
266 W: Write,
267 {
268 write!(writer, "\n\t\tdoc: \"\n\t\t\t- [Vulkan documentation](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/{}.html)", ext.raw).unwrap();
269
270 if ext.requires_core != (1, 0) {
271 write!(
272 writer,
273 "\n\t\t\t- Requires Vulkan {}.{}",
274 ext.requires_core.0, ext.requires_core.1
275 )
276 .unwrap();
277 }
278
279 if !ext.requires_device_extensions.is_empty() {
280 let links: Vec<_> = ext
281 .requires_device_extensions
282 .iter()
283 .map(|ext| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext))
284 .collect();
285 write!(
286 writer,
287 "\n\t\t\t- Requires device extension{}: {}",
288 if ext.requires_device_extensions.len() > 1 {
289 "s"
290 } else {
291 ""
292 },
293 links.join(", ")
294 )
295 .unwrap();
296 }
297
298 if !ext.requires_instance_extensions.is_empty() {
299 let links: Vec<_> = ext
300 .requires_instance_extensions
301 .iter()
302 .map(|ext| format!("[`{}`](crate::instance::InstanceExtensions::{0})", ext))
303 .collect();
304 write!(
305 writer,
306 "\n\t\t\t- Requires instance extension{}: {}",
307 if ext.requires_instance_extensions.len() > 1 {
308 "s"
309 } else {
310 ""
311 },
312 links.join(", ")
313 )
314 .unwrap();
315 }
316
317 if ext.required_if_supported {
318 write!(
319 writer,
320 "\n\t\t\t- Must be enabled if it is supported by the physical device",
321 )
322 .unwrap();
323 }
324
325 if !ext.conflicts_device_extensions.is_empty() {
326 let links: Vec<_> = ext
327 .conflicts_device_extensions
328 .iter()
329 .map(|ext| format!("[`{}`](crate::device::DeviceExtensions::{0})", ext))
330 .collect();
331 write!(
332 writer,
333 "\n\t\t\t- Conflicts with device extension{}: {}",
334 if ext.conflicts_device_extensions.len() > 1 {
335 "s"
336 } else {
337 ""
338 },
339 links.join(", ")
340 )
341 .unwrap();
342 }
343
344 if let Some(status) = ext.status.as_ref() {
345 match status {
346 ExtensionStatus::Promoted(replacement) => {
347 write!(writer, "\n\t\t\t- Promoted to ",).unwrap();
348
349 match replacement {
350 Replacement::Core(version) => {
351 write!(writer, "Vulkan {}.{}", version.0, version.1).unwrap();
352 }
353 Replacement::DeviceExtension(ext) => {
354 write!(writer, "[`{}`](crate::device::DeviceExtensions::{0})", ext)
355 .unwrap();
356 }
357 Replacement::InstanceExtension(ext) => {
358 write!(
359 writer,
360 "[`{}`](crate::instance::InstanceExtensions::{0})",
361 ext
362 )
363 .unwrap();
364 }
365 }
366 }
367 ExtensionStatus::Deprecated(replacement) => {
368 write!(writer, "\n\t\t\t- Deprecated ",).unwrap();
369
370 match replacement {
371 Some(Replacement::Core(version)) => {
372 write!(writer, "by Vulkan {}.{}", version.0, version.1).unwrap();
373 }
374 Some(Replacement::DeviceExtension(ext)) => {
375 write!(
376 writer,
377 "by [`{}`](crate::device::DeviceExtensions::{0})",
378 ext
379 )
380 .unwrap();
381 }
382 Some(Replacement::InstanceExtension(ext)) => {
383 write!(
384 writer,
385 "by [`{}`](crate::instance::InstanceExtensions::{0})",
386 ext
387 )
388 .unwrap();
389 }
390 None => {
391 write!(writer, "without a replacement").unwrap();
392 }
393 }
394 }
395 }
396 }
397
398 write!(writer, "\n\t\t\",").unwrap();
399 }
400