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 (['Vulkan14Features', 'GlobalPriorityQueryFeatures'], ['globalPriorityQuery']), 116 ( 117 ['Vulkan14Features', 'ShaderSubgroupRotateFeatures'], 118 ['shaderSubgroupRotate', 'shaderSubgroupRotateClustered'], 119 ), 120 (['Vulkan14Features', 'ShaderFloatControls2Features'], ['shaderFloatControls2']), 121 (['Vulkan14Features', 'ShaderExpectAssumeFeatures'], ['shaderExpectAssume']), 122 ( 123 ['Vulkan14Features', 'LineRasterizationFeatures'], 124 [ 125 'rectangularLines', 126 'bresenhamLines', 127 'smoothLines', 128 'stippledRectangularLines', 129 'stippledBresenhamLines', 130 'stippledSmoothLines', 131 ], 132 ), 133 ( 134 ['Vulkan14Features', 'VertexAttributeDivisorFeatures'], 135 [ 136 'vertexAttributeInstanceRateDivisor', 137 'vertexAttributeInstanceRateZeroDivisor', 138 ], 139 ), 140 (['Vulkan14Features', 'IndexTypeUint8Features'], ['indexTypeUint8']), 141 (['Vulkan14Features', 'DynamicRenderingLocalReadFeatures'], ['dynamicRenderingLocalRead']), 142 (['Vulkan14Features', 'Maintenance5Features'], ['maintenance5']), 143 (['Vulkan14Features', 'Maintenance6Features'], ['maintenance6']), 144 (['Vulkan14Features', 'PipelineProtectedAccessFeatures'], ['pipelineProtectedAccess']), 145 (['Vulkan14Features', 'PipelineRobustnessFeatures'], ['pipelineRobustness']), 146 (['Vulkan14Features', 'HostImageCopyFeatures'], ['hostImageCopy']), 147] 148 149for (feature_structs, features) in KNOWN_ALIASES: 150 for flag in features: 151 for f in feature_structs: 152 rename = (f, flag) 153 assert rename not in RENAMED_FEATURES, f"{rename} already exists in RENAMED_FEATURES" 154 RENAMED_FEATURES[rename] = flag 155 156def get_renamed_feature(c_type, feature): 157 return RENAMED_FEATURES.get((str_removeprefix(c_type, 'VkPhysicalDevice'), feature), feature) 158 159@dataclass 160class FeatureStruct: 161 reqs: Requirements 162 c_type: str 163 s_type: str 164 features: typing.List[str] 165 166 def condition(self, physical_dev): 167 conds = [] 168 if self.reqs.core_version: 169 conds.append(physical_dev + '->properties.apiVersion >= ' + 170 self.reqs.core_version.c_vk_version()) 171 for ext in self.reqs.extensions: 172 conds.append(physical_dev + '->supported_extensions.' + 173 ext.name[3:]) 174 if not conds: 175 return None 176 return '(' + ' || '.join(conds) + ')' 177 178TEMPLATE_H = Template(COPYRIGHT + """ 179/* This file generated from ${filename}, don't edit directly. */ 180#ifndef VK_FEATURES_H 181#define VK_FEATURES_H 182 183#ifdef __cplusplus 184extern "C" { 185#endif 186 187struct vk_features { 188% for flag in all_flags: 189 bool ${flag}; 190% endfor 191}; 192 193void 194vk_set_physical_device_features(struct vk_features *all_features, 195 const VkPhysicalDeviceFeatures2 *pFeatures); 196 197void 198vk_set_physical_device_features_1_0(struct vk_features *all_features, 199 const VkPhysicalDeviceFeatures *pFeatures); 200 201#ifdef __cplusplus 202} 203#endif 204 205#endif 206""") 207 208TEMPLATE_C = Template(COPYRIGHT + """ 209/* This file generated from ${filename}, don't edit directly. */ 210 211#include "vk_common_entrypoints.h" 212#include "vk_log.h" 213#include "vk_physical_device.h" 214#include "vk_physical_device_features.h" 215#include "vk_util.h" 216 217static VkResult 218check_physical_device_features(struct vk_physical_device *physical_device, 219 const VkPhysicalDeviceFeatures *supported, 220 const VkPhysicalDeviceFeatures *enabled, 221 const char *struct_name) 222{ 223% for flag in pdev_features: 224 if (enabled->${flag} && !supported->${flag}) 225 return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT, 226 "%s.%s not supported", struct_name, "${flag}"); 227% endfor 228 229 return VK_SUCCESS; 230} 231 232VkResult 233vk_physical_device_check_device_features(struct vk_physical_device *physical_device, 234 const VkDeviceCreateInfo *pCreateInfo) 235{ 236 VkPhysicalDevice vk_physical_device = 237 vk_physical_device_to_handle(physical_device); 238 239 /* Query the device what kind of features are supported. */ 240 VkPhysicalDeviceFeatures2 supported_features2 = { 241 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, 242 }; 243 244% for f in feature_structs: 245 ${f.c_type} supported_${f.c_type} = { .pNext = NULL }; 246% endfor 247 248 vk_foreach_struct_const(features, pCreateInfo->pNext) { 249 VkBaseOutStructure *supported = NULL; 250 switch (features->sType) { 251% for f in feature_structs: 252 case ${f.s_type}: 253% if f.condition("physical_device") is not None: 254 if (!${f.condition("physical_device")}) 255 break; 256% endif 257 supported = (VkBaseOutStructure *) &supported_${f.c_type}; 258 break; 259% endfor 260 default: 261 break; 262 } 263 264 /* Not a feature struct. */ 265 if (!supported) 266 continue; 267 268 /* Check for cycles in the list */ 269 if (supported->pNext != NULL || supported->sType != 0) 270 return VK_ERROR_UNKNOWN; 271 272 supported->sType = features->sType; 273 __vk_append_struct(&supported_features2, supported); 274 } 275 276 physical_device->dispatch_table.GetPhysicalDeviceFeatures2( 277 vk_physical_device, &supported_features2); 278 279 if (pCreateInfo->pEnabledFeatures) { 280 VkResult result = 281 check_physical_device_features(physical_device, 282 &supported_features2.features, 283 pCreateInfo->pEnabledFeatures, 284 "VkPhysicalDeviceFeatures"); 285 if (result != VK_SUCCESS) 286 return result; 287 } 288 289 /* Iterate through additional feature structs */ 290 vk_foreach_struct_const(features, pCreateInfo->pNext) { 291 /* Check each feature boolean for given structure. */ 292 switch (features->sType) { 293 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { 294 const VkPhysicalDeviceFeatures2 *features2 = (const void *)features; 295 VkResult result = 296 check_physical_device_features(physical_device, 297 &supported_features2.features, 298 &features2->features, 299 "VkPhysicalDeviceFeatures2.features"); 300 if (result != VK_SUCCESS) 301 return result; 302 break; 303 } 304% for f in feature_structs: 305 case ${f.s_type}: { 306% if f.condition("physical_device") is not None: 307 if (!${f.condition("physical_device")}) 308 break; 309% endif 310 const ${f.c_type} *a = &supported_${f.c_type}; 311 const ${f.c_type} *b = (const void *) features; 312% for flag in f.features: 313 if (b->${flag} && !a->${flag}) 314 return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT, 315 "%s.%s not supported", "${f.c_type}", "${flag}"); 316% endfor 317 break; 318 } 319% endfor 320 default: 321 break; 322 } 323 } // for each extension structure 324 return VK_SUCCESS; 325} 326 327VKAPI_ATTR void VKAPI_CALL 328vk_common_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, 329 VkPhysicalDeviceFeatures2 *pFeatures) 330{ 331 VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice); 332 333% for flag in pdev_features: 334 pFeatures->features.${flag} = pdevice->supported_features.${flag}; 335% endfor 336 337 vk_foreach_struct(ext, pFeatures) { 338 switch (ext->sType) { 339% for f in feature_structs: 340 case ${f.s_type}: { 341 ${f.c_type} *features = (void *) ext; 342% for flag in f.features: 343 features->${flag} = pdevice->supported_features.${get_renamed_feature(f.c_type, flag)}; 344% endfor 345 break; 346 } 347 348% endfor 349 default: 350 break; 351 } 352 } 353} 354 355void 356vk_set_physical_device_features(struct vk_features *all_features, 357 const VkPhysicalDeviceFeatures2 *pFeatures) 358{ 359 vk_foreach_struct_const(ext, pFeatures) { 360 switch (ext->sType) { 361 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { 362 const VkPhysicalDeviceFeatures2 *features = (const void *) ext; 363 vk_set_physical_device_features_1_0(all_features, &features->features); 364 break; 365 } 366 367% for f in feature_structs: 368 case ${f.s_type}: { 369 const ${f.c_type} *features = (const void *) ext; 370% for flag in f.features: 371 if (features->${flag}) 372 all_features->${get_renamed_feature(f.c_type, flag)} = true; 373% endfor 374 break; 375 } 376 377% endfor 378 default: 379 break; 380 } 381 } 382} 383 384void 385vk_set_physical_device_features_1_0(struct vk_features *all_features, 386 const VkPhysicalDeviceFeatures *pFeatures) 387{ 388% for flag in pdev_features: 389 if (pFeatures->${flag}) 390 all_features->${flag} = true; 391% endfor 392} 393""") 394 395def get_pdev_features(doc): 396 _type = doc.find(".types/type[@name='VkPhysicalDeviceFeatures']") 397 if _type is not None: 398 flags = [] 399 for p in _type.findall('./member'): 400 assert p.find('./type').text == 'VkBool32' 401 flags.append(p.find('./name').text) 402 return flags 403 return None 404 405def filter_api(elem, api): 406 if 'api' not in elem.attrib: 407 return True 408 409 return api in elem.attrib['api'].split(',') 410 411def get_feature_structs(doc, api, beta): 412 feature_structs = OrderedDict() 413 414 required = get_all_required(doc, 'type', api, beta) 415 416 # parse all struct types where structextends VkPhysicalDeviceFeatures2 417 for _type in doc.findall('./types/type[@category="struct"]'): 418 if _type.attrib.get('structextends') != 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo': 419 continue 420 if _type.attrib['name'] not in required: 421 continue 422 423 reqs = required[_type.attrib['name']] 424 # Skip extensions with a define for now 425 guard = reqs.guard 426 if guard is not None and (guard != "VK_ENABLE_BETA_EXTENSIONS" or beta != "true"): 427 continue 428 429 # find Vulkan structure type 430 for elem in _type: 431 if "STRUCTURE_TYPE" in str(elem.attrib): 432 s_type = elem.attrib.get('values') 433 434 # collect a list of feature flags 435 flags = [] 436 437 for p in _type.findall('./member'): 438 if not filter_api(p, api): 439 continue 440 441 m_name = p.find('./name').text 442 if m_name == 'pNext': 443 pass 444 elif m_name == 'sType': 445 s_type = p.attrib.get('values') 446 else: 447 assert p.find('./type').text == 'VkBool32' 448 flags.append(m_name) 449 450 feature_struct = FeatureStruct(reqs=reqs, c_type=_type.attrib.get('name'), s_type=s_type, features=flags) 451 feature_structs[feature_struct.c_type] = feature_struct 452 453 return feature_structs.values() 454 455def get_feature_structs_from_xml(xml_files, beta, api='vulkan'): 456 diagnostics = [] 457 458 pdev_features = None 459 feature_structs = [] 460 461 for filename in xml_files: 462 doc = et.parse(filename) 463 feature_structs += get_feature_structs(doc, api, beta) 464 if not pdev_features: 465 pdev_features = get_pdev_features(doc) 466 467 unused_renames = {**RENAMED_FEATURES} 468 469 features = OrderedDict() 470 471 for flag in pdev_features: 472 features[flag] = 'VkPhysicalDeviceFeatures' 473 474 for f in feature_structs: 475 for flag in f.features: 476 renamed_flag = get_renamed_feature(f.c_type, flag) 477 if renamed_flag not in features: 478 features[renamed_flag] = f.c_type 479 else: 480 a = str_removeprefix(features[renamed_flag], 'VkPhysicalDevice') 481 b = str_removeprefix(f.c_type, 'VkPhysicalDevice') 482 if (a, flag) not in RENAMED_FEATURES or (b, flag) not in RENAMED_FEATURES: 483 diagnostics.append(f'{a} and {b} both define {flag}') 484 485 unused_renames.pop((str_removeprefix(f.c_type, 'VkPhysicalDevice'), flag), None) 486 487 for rename in unused_renames: 488 diagnostics.append(f'unused rename {rename}') 489 490 assert len(diagnostics) == 0, '\n'.join(diagnostics) 491 492 return pdev_features, feature_structs, features 493 494 495def main(): 496 parser = argparse.ArgumentParser() 497 parser.add_argument('--out-c', required=True, help='Output C file.') 498 parser.add_argument('--out-h', required=True, help='Output H file.') 499 parser.add_argument('--beta', required=True, help='Enable beta extensions.') 500 parser.add_argument('--xml', 501 help='Vulkan API XML file.', 502 required=True, action='append', dest='xml_files') 503 args = parser.parse_args() 504 505 pdev_features, feature_structs, all_flags = get_feature_structs_from_xml(args.xml_files, args.beta) 506 507 environment = { 508 'filename': os.path.basename(__file__), 509 'pdev_features': pdev_features, 510 'feature_structs': feature_structs, 511 'all_flags': all_flags, 512 'get_renamed_feature': get_renamed_feature, 513 } 514 515 try: 516 with open(args.out_c, 'w', encoding='utf-8') as f: 517 f.write(TEMPLATE_C.render(**environment)) 518 with open(args.out_h, 'w', encoding='utf-8') as f: 519 f.write(TEMPLATE_H.render(**environment)) 520 except Exception: 521 # In the event there's an error, this uses some helpers from mako 522 # to print a useful stack trace and prints it, then exits with 523 # status 1, if python is run with debug; otherwise it just raises 524 # the exception 525 print(mako.exceptions.text_error_template().render(), file=sys.stderr) 526 sys.exit(1) 527 528if __name__ == '__main__': 529 main() 530