|
1501
|
1 #!/usr/bin/env python3 -i
|
|
|
2 #
|
|
|
3 # Copyright 2013-2025 The Khronos Group Inc.
|
|
|
4 #
|
|
|
5 # SPDX-License-Identifier: Apache-2.0
|
|
|
6
|
|
|
7 # Working-group-specific style conventions,
|
|
|
8 # used in generation.
|
|
|
9
|
|
|
10 import re
|
|
|
11 import os
|
|
|
12
|
|
|
13 from spec_tools.conventions import ConventionsBase
|
|
|
14
|
|
|
15 # Modified from default implementation - see category_requires_validation() below
|
|
|
16 CATEGORIES_REQUIRING_VALIDATION = set(('handle', 'enum', 'bitmask'))
|
|
|
17
|
|
|
18 # Tokenize into "words" for structure types, approximately per spec "Implicit Valid Usage" section 2.7.2
|
|
|
19 # This first set is for things we recognize explicitly as words,
|
|
|
20 # as exceptions to the general regex.
|
|
|
21 # Ideally these would be listed in the spec as exceptions, as OpenXR does.
|
|
|
22 SPECIAL_WORDS = set((
|
|
|
23 '16Bit', # VkPhysicalDevice16BitStorageFeatures
|
|
|
24 '2D', # VkPhysicalDeviceImage2DViewOf3DFeaturesEXT
|
|
|
25 '3D', # VkPhysicalDeviceImage2DViewOf3DFeaturesEXT
|
|
|
26 '8Bit', # VkPhysicalDevice8BitStorageFeaturesKHR
|
|
|
27 'AABB', # VkGeometryAABBNV
|
|
|
28 'ASTC', # VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT
|
|
|
29 'D3D12', # VkD3D12FenceSubmitInfoKHR
|
|
|
30 'Float16', # VkPhysicalDeviceShaderFloat16Int8FeaturesKHR
|
|
|
31 'Bfloat16', # VkPhysicalDeviceShaderBfloat16FeaturesKHR
|
|
|
32 'Float8', # VkPhysicalDeviceShaderFloat8FeaturesEXT
|
|
|
33 'ImagePipe', # VkImagePipeSurfaceCreateInfoFUCHSIA
|
|
|
34 'Int64', # VkPhysicalDeviceShaderAtomicInt64FeaturesKHR
|
|
|
35 'Int8', # VkPhysicalDeviceShaderFloat16Int8FeaturesKHR
|
|
|
36 'MacOS', # VkMacOSSurfaceCreateInfoMVK
|
|
|
37 'RGBA10X6', # VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT
|
|
|
38 'Uint8', # VkPhysicalDeviceIndexTypeUint8FeaturesEXT
|
|
|
39 'Win32', # VkWin32SurfaceCreateInfoKHR
|
|
|
40 ))
|
|
|
41 # A regex to match any of the SPECIAL_WORDS
|
|
|
42 EXCEPTION_PATTERN = r'(?P<exception>{})'.format(
|
|
|
43 '|'.join(f'({re.escape(w)})' for w in SPECIAL_WORDS))
|
|
|
44 MAIN_RE = re.compile(
|
|
|
45 # the negative lookahead is to prevent the all-caps pattern from being too greedy.
|
|
|
46 r'({}|([0-9]+)|([A-Z][a-z]+)|([A-Z][A-Z]*(?![a-z])))'.format(EXCEPTION_PATTERN))
|
|
|
47
|
|
|
48
|
|
|
49 class VulkanConventions(ConventionsBase):
|
|
|
50 @property
|
|
|
51 def null(self):
|
|
|
52 """Preferred spelling of NULL."""
|
|
|
53 return '`NULL`'
|
|
|
54
|
|
|
55 def formatVersion(self, name, apivariant, major, minor):
|
|
|
56 """Mark up an API version name as a link in the spec."""
|
|
|
57 version = f'{major}.{minor}'
|
|
|
58 if apivariant == 'VKSC':
|
|
|
59 # Vulkan SC has a different anchor pattern for version appendices
|
|
|
60 if version == '1.0':
|
|
|
61 return 'Vulkan SC 1.0'
|
|
|
62 else:
|
|
|
63 return f'<<versions-sc-{version}, Vulkan SC Version {version}>>'
|
|
|
64 else:
|
|
|
65 return f'<<versions-{version}, Vulkan Version {version}>>'
|
|
|
66
|
|
|
67 def formatExtension(self, name):
|
|
|
68 """Mark up an extension name as a link in the spec."""
|
|
|
69 return f'apiext:{name}'
|
|
|
70
|
|
|
71 @property
|
|
|
72 def struct_macro(self):
|
|
|
73 """Get the appropriate format macro for a structure.
|
|
|
74
|
|
|
75 Primarily affects generated valid usage statements.
|
|
|
76 """
|
|
|
77
|
|
|
78 return 'slink:'
|
|
|
79
|
|
|
80 @property
|
|
|
81 def constFlagBits(self):
|
|
|
82 """Returns True if static const flag bits should be generated, False if an enumerated type should be generated."""
|
|
|
83 return False
|
|
|
84
|
|
|
85 @property
|
|
|
86 def structtype_member_name(self):
|
|
|
87 """Return name of the structure type member"""
|
|
|
88 return 'sType'
|
|
|
89
|
|
|
90 @property
|
|
|
91 def nextpointer_member_name(self):
|
|
|
92 """Return name of the structure pointer chain member"""
|
|
|
93 return 'pNext'
|
|
|
94
|
|
|
95 @property
|
|
|
96 def valid_pointer_prefix(self):
|
|
|
97 """Return prefix to pointers which must themselves be valid"""
|
|
|
98 return 'valid'
|
|
|
99
|
|
|
100 def is_structure_type_member(self, paramtype, paramname):
|
|
|
101 """Determine if member type and name match the structure type member."""
|
|
|
102 return paramtype == 'VkStructureType' and paramname == self.structtype_member_name
|
|
|
103
|
|
|
104 def is_nextpointer_member(self, paramtype, paramname):
|
|
|
105 """Determine if member type and name match the next pointer chain member."""
|
|
|
106 return paramtype == 'void' and paramname == self.nextpointer_member_name
|
|
|
107
|
|
|
108 def generate_structure_type_from_name(self, structname):
|
|
|
109 """Generate a structure type name, like VK_STRUCTURE_TYPE_CREATE_INSTANCE_INFO"""
|
|
|
110
|
|
|
111 structure_type_parts = []
|
|
|
112 # Tokenize into "words"
|
|
|
113 for elem in MAIN_RE.findall(structname):
|
|
|
114 word = elem[0]
|
|
|
115 if word == 'Vk':
|
|
|
116 structure_type_parts.append('VK_STRUCTURE_TYPE')
|
|
|
117 else:
|
|
|
118 structure_type_parts.append(word.upper())
|
|
|
119 name = '_'.join(structure_type_parts)
|
|
|
120
|
|
|
121 # The simple-minded rules need modification for some structure names
|
|
|
122 subpats = [
|
|
|
123 [ r'_H_(26[45])_', r'_H\1_' ],
|
|
|
124 [ r'_VP_9_', r'_VP9_' ],
|
|
|
125 [ r'_AV_1_', r'_AV1_' ],
|
|
|
126 [ r'_VULKAN_([0-9])([0-9])_', r'_VULKAN_\1_\2_' ],
|
|
|
127 [ r'_VULKAN_SC_([0-9])([0-9])_',r'_VULKAN_SC_\1_\2_' ],
|
|
|
128 [ r'_DIRECT_FB_', r'_DIRECTFB_' ],
|
|
|
129 [ r'_VULKAN_SC_10', r'_VULKAN_SC_1_0' ],
|
|
|
130
|
|
|
131 ]
|
|
|
132
|
|
|
133 for subpat in subpats:
|
|
|
134 name = re.sub(subpat[0], subpat[1], name)
|
|
|
135 return name
|
|
|
136
|
|
|
137 @property
|
|
|
138 def warning_comment(self):
|
|
|
139 """Return warning comment to be placed in header of generated Asciidoctor files"""
|
|
|
140 return '// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry'
|
|
|
141
|
|
|
142 @property
|
|
|
143 def file_suffix(self):
|
|
|
144 """Return suffix of generated Asciidoctor files"""
|
|
|
145 return '.adoc'
|
|
|
146
|
|
|
147 def api_name(self, spectype='api'):
|
|
|
148 """Return API or specification name for citations in ref pages.ref
|
|
|
149 pages should link to for
|
|
|
150
|
|
|
151 spectype is the spec this refpage is for: 'api' is the Vulkan API
|
|
|
152 Specification. Defaults to 'api'. If an unrecognized spectype is
|
|
|
153 given, returns None.
|
|
|
154 """
|
|
|
155 if spectype == 'api' or spectype is None:
|
|
|
156 return 'Vulkan'
|
|
|
157 else:
|
|
|
158 return None
|
|
|
159
|
|
|
160 @property
|
|
|
161 def api_prefix(self):
|
|
|
162 """Return API token prefix"""
|
|
|
163 return 'VK_'
|
|
|
164
|
|
|
165 @property
|
|
|
166 def write_contacts(self):
|
|
|
167 """Return whether contact list should be written to extension appendices"""
|
|
|
168 return True
|
|
|
169
|
|
|
170 @property
|
|
|
171 def write_refpage_include(self):
|
|
|
172 """Return whether refpage include should be written to extension appendices"""
|
|
|
173 return True
|
|
|
174
|
|
|
175 @property
|
|
|
176 def member_used_for_unique_vuid(self):
|
|
|
177 """Return the member name used in the VUID-...-...-unique ID."""
|
|
|
178 return self.structtype_member_name
|
|
|
179
|
|
|
180 def is_externsync_command(self, protoname):
|
|
|
181 """Returns True if the protoname element is an API command requiring
|
|
|
182 external synchronization
|
|
|
183 """
|
|
|
184 return protoname is not None and 'vkCmd' in protoname
|
|
|
185
|
|
|
186 def is_api_name(self, name):
|
|
|
187 """Returns True if name is in the reserved API namespace.
|
|
|
188 For Vulkan, these are names with a case-insensitive 'vk' prefix, or
|
|
|
189 a 'PFN_vk' function pointer type prefix.
|
|
|
190 """
|
|
|
191 return name[0:2].lower() == 'vk' or name.startswith('PFN_vk')
|
|
|
192
|
|
|
193 def specURL(self, spectype='api'):
|
|
|
194 """Return public registry URL which ref pages should link to for the
|
|
|
195 current all-extensions HTML specification, so xrefs in the
|
|
|
196 asciidoc source that are not to ref pages can link into it
|
|
|
197 instead. N.b. this may need to change on a per-refpage basis if
|
|
|
198 there are multiple documents involved.
|
|
|
199 """
|
|
|
200 return 'https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html'
|
|
|
201
|
|
|
202 @property
|
|
|
203 def xml_api_name(self):
|
|
|
204 """Return the name used in the default API XML registry for the default API"""
|
|
|
205 return 'vulkan'
|
|
|
206
|
|
|
207 @property
|
|
|
208 def registry_path(self):
|
|
|
209 """Return relpath to the default API XML registry in this project."""
|
|
|
210 return 'xml/vk.xml'
|
|
|
211
|
|
|
212 @property
|
|
|
213 def specification_path(self):
|
|
|
214 """Return relpath to the Asciidoctor specification sources in this project."""
|
|
|
215 return '{generated}/meta'
|
|
|
216
|
|
|
217 @property
|
|
|
218 def special_use_section_anchor(self):
|
|
|
219 """Return asciidoctor anchor name in the API Specification of the
|
|
|
220 section describing extension special uses in detail."""
|
|
|
221 return 'extendingvulkan-compatibility-specialuse'
|
|
|
222
|
|
|
223 @property
|
|
|
224 def extension_index_prefixes(self):
|
|
|
225 """Return a list of extension prefixes used to group extension refpages."""
|
|
|
226 return ['VK_KHR', 'VK_EXT', 'VK']
|
|
|
227
|
|
|
228 @property
|
|
|
229 def unified_flag_refpages(self):
|
|
|
230 """Return True if Flags/FlagBits refpages are unified, False if
|
|
|
231 they are separate.
|
|
|
232 """
|
|
|
233 return False
|
|
|
234
|
|
|
235 @property
|
|
|
236 def spec_reflow_path(self):
|
|
|
237 """Return the path to the spec source folder to reflow"""
|
|
|
238 return os.getcwd()
|
|
|
239
|
|
|
240 @property
|
|
|
241 def spec_no_reflow_dirs(self):
|
|
|
242 """Return a set of directories not to automatically descend into
|
|
|
243 when reflowing spec text
|
|
|
244 """
|
|
|
245 return ('scripts', 'style')
|
|
|
246
|
|
|
247 @property
|
|
|
248 def zero(self):
|
|
|
249 return '`0`'
|
|
|
250
|
|
|
251 def category_requires_validation(self, category):
|
|
|
252 """Return True if the given type 'category' always requires validation.
|
|
|
253
|
|
|
254 Overridden because Vulkan does not require "valid" text for basetype
|
|
|
255 in the spec right now."""
|
|
|
256 return category in CATEGORIES_REQUIRING_VALIDATION
|
|
|
257
|
|
|
258 @property
|
|
|
259 def should_skip_checking_codes(self):
|
|
|
260 """Return True if more than the basic validation of return codes should
|
|
|
261 be skipped for a command.
|
|
|
262
|
|
|
263 Vulkan mostly relies on the validation layers rather than API
|
|
|
264 builtin error checking, so these checks are not appropriate.
|
|
|
265
|
|
|
266 For example, passing in a VkFormat parameter will not potentially
|
|
|
267 generate a VK_ERROR_FORMAT_NOT_SUPPORTED code."""
|
|
|
268
|
|
|
269 return True
|
|
|
270
|
|
|
271 def extension_file_path(self, name):
|
|
|
272 """Return file path to an extension appendix relative to a directory
|
|
|
273 containing all such appendices.
|
|
|
274 - name - extension name"""
|
|
|
275
|
|
|
276 return f'{name}{self.file_suffix}'
|
|
|
277
|
|
|
278 def valid_flag_bit(self, bitpos):
|
|
|
279 """Return True if bitpos is an allowed numeric bit position for
|
|
|
280 an API flag bit.
|
|
|
281
|
|
|
282 Vulkan uses 32 bit Vk*Flags types, and assumes C compilers may
|
|
|
283 cause Vk*FlagBits values with bit 31 set to result in a 64 bit
|
|
|
284 enumerated type, so disallows such flags."""
|
|
|
285 return bitpos >= 0 and bitpos < 31
|
|
|
286
|
|
|
287 @property
|
|
|
288 def extra_refpage_headers(self):
|
|
|
289 """Return any extra text to add to refpage headers."""
|
|
|
290 return 'include::{config}/attribs.adoc[]'
|
|
|
291
|
|
|
292 @property
|
|
|
293 def extra_refpage_body(self):
|
|
|
294 """Return any extra text (following the title) for generated
|
|
|
295 reference pages."""
|
|
|
296 return 'include::{generated}/specattribs.adoc[]'
|
|
|
297
|
|
|
298
|
|
|
299 class VulkanSCConventions(VulkanConventions):
|
|
|
300
|
|
|
301 def specURL(self, spectype='api'):
|
|
|
302 """Return public registry URL which ref pages should link to for the
|
|
|
303 current all-extensions HTML specification, so xrefs in the
|
|
|
304 asciidoc source that are not to ref pages can link into it
|
|
|
305 instead. N.b. this may need to change on a per-refpage basis if
|
|
|
306 there are multiple documents involved.
|
|
|
307 """
|
|
|
308 return 'https://registry.khronos.org/vulkansc/specs/1.0-extensions/html/vkspec.html'
|
|
|
309
|
|
|
310 @property
|
|
|
311 def xml_api_name(self):
|
|
|
312 """Return the name used in the default API XML registry for the default API"""
|
|
|
313 return 'vulkansc'
|
|
|
314
|
|
|
315 class VulkanBaseConventions(VulkanConventions):
|
|
|
316
|
|
|
317 def specURL(self, spectype='api'):
|
|
|
318 """Return public registry URL which ref pages should link to for the
|
|
|
319 current all-extensions HTML specification, so xrefs in the
|
|
|
320 asciidoc source that are not to ref pages can link into it
|
|
|
321 instead. N.b. this may need to change on a per-refpage basis if
|
|
|
322 there are multiple documents involved.
|
|
|
323 """
|
|
|
324 return 'https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html'
|
|
|
325
|
|
|
326 @property
|
|
|
327 def xml_api_name(self):
|
|
|
328 """Return the name used in the default API XML registry for the default API"""
|
|
|
329 return 'vulkanbase'
|
|
|
330
|