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