1COPYRIGHT=u""" 2/* Copyright © 2021 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23""" 24 25import argparse 26from collections import OrderedDict 27from dataclasses import dataclass 28import os 29import sys 30import typing 31import xml.etree.ElementTree as et 32 33import mako 34from mako.template import Template 35from vk_extensions import Requirements, get_all_required, filter_api 36 37def str_removeprefix(s, prefix): 38 if s.startswith(prefix): 39 return s[len(prefix):] 40 return s 41 42RENAMED_FEATURES = { 43 # See https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17272#note_1446477 for details 44 ('BufferDeviceAddressFeaturesEXT', 'bufferDeviceAddressCaptureReplay'): 'bufferDeviceAddressCaptureReplayEXT', 45 46 ('MeshShaderFeaturesNV', 'taskShader'): 'taskShaderNV', 47 ('MeshShaderFeaturesNV', 'meshShader'): 'meshShaderNV', 48 49 ('CooperativeMatrixFeaturesNV', 'cooperativeMatrix'): 'cooperativeMatrixNV', 50 ('CooperativeMatrixFeaturesNV', 'cooperativeMatrixRobustBufferAccess'): 'cooperativeMatrixRobustBufferAccessNV', 51 52 ('DeviceGeneratedCommandsFeaturesNV', 'deviceGeneratedCommands'): 'deviceGeneratedCommandsNV', 53} 54 55KNOWN_ALIASES = [ 56 (['Vulkan11Features', '16BitStorageFeatures'], ['storageBuffer16BitAccess', 'uniformAndStorageBuffer16BitAccess', 'storagePushConstant16', 'storageInputOutput16']), 57 (['Vulkan11Features', 'MultiviewFeatures'], ['multiview', 'multiviewGeometryShader', 'multiviewTessellationShader']), 58 (['Vulkan11Features', 'VariablePointersFeatures'], ['variablePointersStorageBuffer', 'variablePointers']), 59 (['Vulkan11Features', 'ProtectedMemoryFeatures'], ['protectedMemory']), 60 (['Vulkan11Features', 'SamplerYcbcrConversionFeatures'], ['samplerYcbcrConversion']), 61 (['Vulkan11Features', 'ShaderDrawParametersFeatures'], ['shaderDrawParameters']), 62 63 (['Vulkan12Features', '8BitStorageFeatures'], ['storageBuffer8BitAccess', 'uniformAndStorageBuffer8BitAccess', 'storagePushConstant8']), 64 (['Vulkan12Features', 'ShaderAtomicInt64Features'], ['shaderBufferInt64Atomics', 'shaderSharedInt64Atomics']), 65 (['Vulkan12Features', 'ShaderFloat16Int8Features'], ['shaderFloat16', 'shaderInt8']), 66 ( 67 ['Vulkan12Features', 'DescriptorIndexingFeatures'], 68 [ 69 'shaderInputAttachmentArrayDynamicIndexing', 70 'shaderUniformTexelBufferArrayDynamicIndexing', 71 'shaderStorageTexelBufferArrayDynamicIndexing', 72 'shaderUniformBufferArrayNonUniformIndexing', 73 'shaderSampledImageArrayNonUniformIndexing', 74 'shaderStorageBufferArrayNonUniformIndexing', 75 'shaderStorageImageArrayNonUniformIndexing', 76 'shaderInputAttachmentArrayNonUniformIndexing', 77 'shaderUniformTexelBufferArrayNonUniformIndexing', 78 'shaderStorageTexelBufferArrayNonUniformIndexing', 79 'descriptorBindingUniformBufferUpdateAfterBind', 80 'descriptorBindingSampledImageUpdateAfterBind', 81 'descriptorBindingStorageImageUpdateAfterBind', 82 'descriptorBindingStorageBufferUpdateAfterBind', 83 'descriptorBindingUniformTexelBufferUpdateAfterBind', 84 'descriptorBindingStorageTexelBufferUpdateAfterBind', 85 'descriptorBindingUpdateUnusedWhilePending', 86 'descriptorBindingPartiallyBound', 87 'descriptorBindingVariableDescriptorCount', 88 'runtimeDescriptorArray', 89 ], 90 ), 91 (['Vulkan12Features', 'ScalarBlockLayoutFeatures'], ['scalarBlockLayout']), 92 (['Vulkan12Features', 'ImagelessFramebufferFeatures'], ['imagelessFramebuffer']), 93 (['Vulkan12Features', 'UniformBufferStandardLayoutFeatures'], ['uniformBufferStandardLayout']), 94 (['Vulkan12Features', 'ShaderSubgroupExtendedTypesFeatures'], ['shaderSubgroupExtendedTypes']), 95 (['Vulkan12Features', 'SeparateDepthStencilLayoutsFeatures'], ['separateDepthStencilLayouts']), 96 (['Vulkan12Features', 'HostQueryResetFeatures'], ['hostQueryReset']), 97 (['Vulkan12Features', 'TimelineSemaphoreFeatures'], ['timelineSemaphore']), 98 (['Vulkan12Features', 'BufferDeviceAddressFeatures', 'BufferDeviceAddressFeaturesEXT'], ['bufferDeviceAddress', 'bufferDeviceAddressMultiDevice']), 99 (['Vulkan12Features', 'BufferDeviceAddressFeatures'], ['bufferDeviceAddressCaptureReplay']), 100 (['Vulkan12Features', 'VulkanMemoryModelFeatures'], ['vulkanMemoryModel', 'vulkanMemoryModelDeviceScope', 'vulkanMemoryModelAvailabilityVisibilityChains']), 101 102 (['Vulkan13Features', 'ImageRobustnessFeatures'], ['robustImageAccess']), 103 (['Vulkan13Features', 'InlineUniformBlockFeatures'], ['inlineUniformBlock', 'descriptorBindingInlineUniformBlockUpdateAfterBind']), 104 (['Vulkan13Features', 'PipelineCreationCacheControlFeatures'], ['pipelineCreationCacheControl']), 105 (['Vulkan13Features', 'PrivateDataFeatures'], ['privateData']), 106 (['Vulkan13Features', 'ShaderDemoteToHelperInvocationFeatures'], ['shaderDemoteToHelperInvocation']), 107 (['Vulkan13Features', 'ShaderTerminateInvocationFeatures'], ['shaderTerminateInvocation']), 108 (['Vulkan13Features', 'SubgroupSizeControlFeatures'], ['subgroupSizeControl', 'computeFullSubgroups']), 109 (['Vulkan13Features', 'Synchronization2Features'], ['synchronization2']), 110 (['Vulkan13Features', 'TextureCompressionASTCHDRFeatures'], ['textureCompressionASTC_HDR']), 111 (['Vulkan13Features', 'ZeroInitializeWorkgroupMemoryFeatures'], ['shaderZeroInitializeWorkgroupMemory']), 112 (['Vulkan13Features', 'DynamicRenderingFeatures'], ['dynamicRendering']), 113 (['Vulkan13Features', 'ShaderIntegerDotProductFeatures'], ['shaderIntegerDotProduct']), 114 (['Vulkan13Features', 'Maintenance4Features'], ['maintenance4']), 115] 116 117for (feature_structs, features) in KNOWN_ALIASES: 118 for flag in features: 119 for f in feature_structs: 120 rename = (f, flag) 121 assert rename not in RENAMED_FEATURES, f"{rename} already exists in RENAMED_FEATURES" 122 RENAMED_FEATURES[rename] = flag 123 124def get_renamed_feature(c_type, feature): 125 return RENAMED_FEATURES.get((str_removeprefix(c_type, 'VkPhysicalDevice'), feature), feature) 126 127@dataclass 128class FeatureStruct: 129 reqs: Requirements 130 c_type: str 131 s_type: str 132 features: typing.List[str] 133 134 def condition(self, physical_dev): 135 conds = [] 136 if self.reqs.core_version: 137 conds.append(physical_dev + '->properties.apiVersion >= ' + 138 self.reqs.core_version.c_vk_version()) 139 for ext in self.reqs.extensions: 140 conds.append(physical_dev + '->supported_extensions.' + 141 ext.name[3:]) 142 if not conds: 143 return None 144 return '(' + ' || '.join(conds) + ')' 145 146TEMPLATE_H = Template(COPYRIGHT + """ 147/* This file generated from ${filename}, don't edit directly. */ 148#ifndef VK_FEATURES_H 149#define VK_FEATURES_H 150 151#ifdef __cplusplus 152extern "C" { 153#endif 154 155struct vk_features { 156% for flag in all_flags: 157 bool ${flag}; 158% endfor 159}; 160 161void 162vk_set_physical_device_features(struct vk_features *all_features, 163 const VkPhysicalDeviceFeatures2 *pFeatures); 164 165void 166vk_set_physical_device_features_1_0(struct vk_features *all_features, 167 const VkPhysicalDeviceFeatures *pFeatures); 168 169#ifdef __cplusplus 170} 171#endif 172 173#endif 174""") 175 176TEMPLATE_C = Template(COPYRIGHT + """ 177/* This file generated from ${filename}, don't edit directly. */ 178 179#include "vk_common_entrypoints.h" 180#include "vk_log.h" 181#include "vk_physical_device.h" 182#include "vk_physical_device_features.h" 183#include "vk_util.h" 184 185static VkResult 186check_physical_device_features(struct vk_physical_device *physical_device, 187 const VkPhysicalDeviceFeatures *supported, 188 const VkPhysicalDeviceFeatures *enabled, 189 const char *struct_name) 190{ 191% for flag in pdev_features: 192 if (enabled->${flag} && !supported->${flag}) 193 return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT, 194 "%s.%s not supported", struct_name, "${flag}"); 195% endfor 196 197 return VK_SUCCESS; 198} 199 200VkResult 201vk_physical_device_check_device_features(struct vk_physical_device *physical_device, 202 const VkDeviceCreateInfo *pCreateInfo) 203{ 204 VkPhysicalDevice vk_physical_device = 205 vk_physical_device_to_handle(physical_device); 206 207 /* Query the device what kind of features are supported. */ 208 VkPhysicalDeviceFeatures2 supported_features2 = { 209 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, 210 }; 211 212% for f in feature_structs: 213 ${f.c_type} supported_${f.c_type} = { .pNext = NULL }; 214% endfor 215 216 vk_foreach_struct_const(features, pCreateInfo->pNext) { 217 VkBaseOutStructure *supported = NULL; 218 switch (features->sType) { 219% for f in feature_structs: 220 case ${f.s_type}: 221% if f.condition("physical_device") is not None: 222 if (!${f.condition("physical_device")}) 223 break; 224% endif 225 supported = (VkBaseOutStructure *) &supported_${f.c_type}; 226 break; 227% endfor 228 default: 229 break; 230 } 231 232 /* Not a feature struct. */ 233 if (!supported) 234 continue; 235 236 /* Check for cycles in the list */ 237 if (supported->pNext != NULL || supported->sType != 0) 238 return VK_ERROR_UNKNOWN; 239 240 supported->sType = features->sType; 241 __vk_append_struct(&supported_features2, supported); 242 } 243 244 physical_device->dispatch_table.GetPhysicalDeviceFeatures2( 245 vk_physical_device, &supported_features2); 246 247 if (pCreateInfo->pEnabledFeatures) { 248 VkResult result = 249 check_physical_device_features(physical_device, 250 &supported_features2.features, 251 pCreateInfo->pEnabledFeatures, 252 "VkPhysicalDeviceFeatures"); 253 if (result != VK_SUCCESS) 254 return result; 255 } 256 257 /* Iterate through additional feature structs */ 258 vk_foreach_struct_const(features, pCreateInfo->pNext) { 259 /* Check each feature boolean for given structure. */ 260 switch (features->sType) { 261 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { 262 const VkPhysicalDeviceFeatures2 *features2 = (const void *)features; 263 VkResult result = 264 check_physical_device_features(physical_device, 265 &supported_features2.features, 266 &features2->features, 267 "VkPhysicalDeviceFeatures2.features"); 268 if (result != VK_SUCCESS) 269 return result; 270 break; 271 } 272% for f in feature_structs: 273 case ${f.s_type}: { 274% if f.condition("physical_device") is not None: 275 if (!${f.condition("physical_device")}) 276 break; 277% endif 278 const ${f.c_type} *a = &supported_${f.c_type}; 279 const ${f.c_type} *b = (const void *) features; 280% for flag in f.features: 281 if (b->${flag} && !a->${flag}) 282 return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT, 283 "%s.%s not supported", "${f.c_type}", "${flag}"); 284% endfor 285 break; 286 } 287% endfor 288 default: 289 break; 290 } 291 } // for each extension structure 292 return VK_SUCCESS; 293} 294 295VKAPI_ATTR void VKAPI_CALL 296vk_common_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, 297 VkPhysicalDeviceFeatures2 *pFeatures) 298{ 299 VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice); 300 301% for flag in pdev_features: 302 pFeatures->features.${flag} = pdevice->supported_features.${flag}; 303% endfor 304 305 vk_foreach_struct(ext, pFeatures) { 306 switch (ext->sType) { 307% for f in feature_structs: 308 case ${f.s_type}: { 309 ${f.c_type} *features = (void *) ext; 310% for flag in f.features: 311 features->${flag} = pdevice->supported_features.${get_renamed_feature(f.c_type, flag)}; 312% endfor 313 break; 314 } 315 316% endfor 317 default: 318 break; 319 } 320 } 321} 322 323void 324vk_set_physical_device_features(struct vk_features *all_features, 325 const VkPhysicalDeviceFeatures2 *pFeatures) 326{ 327 vk_foreach_struct_const(ext, pFeatures) { 328 switch (ext->sType) { 329 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { 330 const VkPhysicalDeviceFeatures2 *features = (const void *) ext; 331 vk_set_physical_device_features_1_0(all_features, &features->features); 332 break; 333 } 334 335% for f in feature_structs: 336 case ${f.s_type}: { 337 const ${f.c_type} *features = (const void *) ext; 338% for flag in f.features: 339 if (features->${flag}) 340 all_features->${get_renamed_feature(f.c_type, flag)} = true; 341% endfor 342 break; 343 } 344 345% endfor 346 default: 347 break; 348 } 349 } 350} 351 352void 353vk_set_physical_device_features_1_0(struct vk_features *all_features, 354 const VkPhysicalDeviceFeatures *pFeatures) 355{ 356% for flag in pdev_features: 357 if (pFeatures->${flag}) 358 all_features->${flag} = true; 359% endfor 360} 361""") 362 363def get_pdev_features(doc): 364 _type = doc.find(".types/type[@name='VkPhysicalDeviceFeatures']") 365 if _type is not None: 366 flags = [] 367 for p in _type.findall('./member'): 368 assert p.find('./type').text == 'VkBool32' 369 flags.append(p.find('./name').text) 370 return flags 371 return None 372 373def filter_api(elem, api): 374 if 'api' not in elem.attrib: 375 return True 376 377 return api in elem.attrib['api'].split(',') 378 379def get_feature_structs(doc, api, beta): 380 feature_structs = OrderedDict() 381 382 required = get_all_required(doc, 'type', api, beta) 383 384 # parse all struct types where structextends VkPhysicalDeviceFeatures2 385 for _type in doc.findall('./types/type[@category="struct"]'): 386 if _type.attrib.get('structextends') != 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo': 387 continue 388 if _type.attrib['name'] not in required: 389 continue 390 391 reqs = required[_type.attrib['name']] 392 # Skip extensions with a define for now 393 guard = reqs.guard 394 if guard is not None and (guard != "VK_ENABLE_BETA_EXTENSIONS" or beta != "true"): 395 continue 396 397 # find Vulkan structure type 398 for elem in _type: 399 if "STRUCTURE_TYPE" in str(elem.attrib): 400 s_type = elem.attrib.get('values') 401 402 # collect a list of feature flags 403 flags = [] 404 405 for p in _type.findall('./member'): 406 if not filter_api(p, api): 407 continue 408 409 m_name = p.find('./name').text 410 if m_name == 'pNext': 411 pass 412 elif m_name == 'sType': 413 s_type = p.attrib.get('values') 414 else: 415 assert p.find('./type').text == 'VkBool32' 416 flags.append(m_name) 417 418 feature_struct = FeatureStruct(reqs=reqs, c_type=_type.attrib.get('name'), s_type=s_type, features=flags) 419 feature_structs[feature_struct.c_type] = feature_struct 420 421 return feature_structs.values() 422 423def get_feature_structs_from_xml(xml_files, beta, api='vulkan'): 424 diagnostics = [] 425 426 pdev_features = None 427 feature_structs = [] 428 429 for filename in xml_files: 430 doc = et.parse(filename) 431 feature_structs += get_feature_structs(doc, api, beta) 432 if not pdev_features: 433 pdev_features = get_pdev_features(doc) 434 435 unused_renames = {**RENAMED_FEATURES} 436 437 features = OrderedDict() 438 439 for flag in pdev_features: 440 features[flag] = 'VkPhysicalDeviceFeatures' 441 442 for f in feature_structs: 443 for flag in f.features: 444 renamed_flag = get_renamed_feature(f.c_type, flag) 445 if renamed_flag not in features: 446 features[renamed_flag] = f.c_type 447 else: 448 a = str_removeprefix(features[renamed_flag], 'VkPhysicalDevice') 449 b = str_removeprefix(f.c_type, 'VkPhysicalDevice') 450 if (a, flag) not in RENAMED_FEATURES or (b, flag) not in RENAMED_FEATURES: 451 diagnostics.append(f'{a} and {b} both define {flag}') 452 453 unused_renames.pop((str_removeprefix(f.c_type, 'VkPhysicalDevice'), flag), None) 454 455 for rename in unused_renames: 456 diagnostics.append(f'unused rename {rename}') 457 458 assert len(diagnostics) == 0, '\n'.join(diagnostics) 459 460 return pdev_features, feature_structs, features 461 462 463def main(): 464 parser = argparse.ArgumentParser() 465 parser.add_argument('--out-c', required=True, help='Output C file.') 466 parser.add_argument('--out-h', required=True, help='Output H file.') 467 parser.add_argument('--beta', required=True, help='Enable beta extensions.') 468 parser.add_argument('--xml', 469 help='Vulkan API XML file.', 470 required=True, action='append', dest='xml_files') 471 args = parser.parse_args() 472 473 pdev_features, feature_structs, all_flags = get_feature_structs_from_xml(args.xml_files, args.beta) 474 475 environment = { 476 'filename': os.path.basename(__file__), 477 'pdev_features': pdev_features, 478 'feature_structs': feature_structs, 479 'all_flags': all_flags, 480 'get_renamed_feature': get_renamed_feature, 481 } 482 483 try: 484 with open(args.out_c, 'w', encoding='utf-8') as f: 485 f.write(TEMPLATE_C.render(**environment)) 486 with open(args.out_h, 'w', encoding='utf-8') as f: 487 f.write(TEMPLATE_H.render(**environment)) 488 except Exception: 489 # In the event there's an error, this uses some helpers from mako 490 # to print a useful stack trace and prints it, then exits with 491 # status 1, if python is run with debug; otherwise it just raises 492 # the exception 493 print(mako.exceptions.text_error_template().render(), file=sys.stderr) 494 sys.exit(1) 495 496if __name__ == '__main__': 497 main() 498