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 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 53KNOWN_ALIASES = [ 54 (['Vulkan11Features', '16BitStorageFeatures'], ['storageBuffer16BitAccess', 'uniformAndStorageBuffer16BitAccess', 'storagePushConstant16', 'storageInputOutput16']), 55 (['Vulkan11Features', 'MultiviewFeatures'], ['multiview', 'multiviewGeometryShader', 'multiviewTessellationShader']), 56 (['Vulkan11Features', 'VariablePointersFeatures'], ['variablePointersStorageBuffer', 'variablePointers']), 57 (['Vulkan11Features', 'ProtectedMemoryFeatures'], ['protectedMemory']), 58 (['Vulkan11Features', 'SamplerYcbcrConversionFeatures'], ['samplerYcbcrConversion']), 59 (['Vulkan11Features', 'ShaderDrawParametersFeatures'], ['shaderDrawParameters']), 60 61 (['Vulkan12Features', '8BitStorageFeatures'], ['storageBuffer8BitAccess', 'uniformAndStorageBuffer8BitAccess', 'storagePushConstant8']), 62 (['Vulkan12Features', 'ShaderAtomicInt64Features'], ['shaderBufferInt64Atomics', 'shaderSharedInt64Atomics']), 63 (['Vulkan12Features', 'ShaderFloat16Int8Features'], ['shaderFloat16', 'shaderInt8']), 64 ( 65 ['Vulkan12Features', 'DescriptorIndexingFeatures'], 66 [ 67 'shaderInputAttachmentArrayDynamicIndexing', 68 'shaderUniformTexelBufferArrayDynamicIndexing', 69 'shaderStorageTexelBufferArrayDynamicIndexing', 70 'shaderUniformBufferArrayNonUniformIndexing', 71 'shaderSampledImageArrayNonUniformIndexing', 72 'shaderStorageBufferArrayNonUniformIndexing', 73 'shaderStorageImageArrayNonUniformIndexing', 74 'shaderInputAttachmentArrayNonUniformIndexing', 75 'shaderUniformTexelBufferArrayNonUniformIndexing', 76 'shaderStorageTexelBufferArrayNonUniformIndexing', 77 'descriptorBindingUniformBufferUpdateAfterBind', 78 'descriptorBindingSampledImageUpdateAfterBind', 79 'descriptorBindingStorageImageUpdateAfterBind', 80 'descriptorBindingStorageBufferUpdateAfterBind', 81 'descriptorBindingUniformTexelBufferUpdateAfterBind', 82 'descriptorBindingStorageTexelBufferUpdateAfterBind', 83 'descriptorBindingUpdateUnusedWhilePending', 84 'descriptorBindingPartiallyBound', 85 'descriptorBindingVariableDescriptorCount', 86 'runtimeDescriptorArray', 87 ], 88 ), 89 (['Vulkan12Features', 'ScalarBlockLayoutFeatures'], ['scalarBlockLayout']), 90 (['Vulkan12Features', 'ImagelessFramebufferFeatures'], ['imagelessFramebuffer']), 91 (['Vulkan12Features', 'UniformBufferStandardLayoutFeatures'], ['uniformBufferStandardLayout']), 92 (['Vulkan12Features', 'ShaderSubgroupExtendedTypesFeatures'], ['shaderSubgroupExtendedTypes']), 93 (['Vulkan12Features', 'SeparateDepthStencilLayoutsFeatures'], ['separateDepthStencilLayouts']), 94 (['Vulkan12Features', 'HostQueryResetFeatures'], ['hostQueryReset']), 95 (['Vulkan12Features', 'TimelineSemaphoreFeatures'], ['timelineSemaphore']), 96 (['Vulkan12Features', 'BufferDeviceAddressFeatures', 'BufferDeviceAddressFeaturesEXT'], ['bufferDeviceAddress', 'bufferDeviceAddressMultiDevice']), 97 (['Vulkan12Features', 'BufferDeviceAddressFeatures'], ['bufferDeviceAddressCaptureReplay']), 98 (['Vulkan12Features', 'VulkanMemoryModelFeatures'], ['vulkanMemoryModel', 'vulkanMemoryModelDeviceScope', 'vulkanMemoryModelAvailabilityVisibilityChains']), 99 100 (['Vulkan13Features', 'ImageRobustnessFeatures'], ['robustImageAccess']), 101 (['Vulkan13Features', 'InlineUniformBlockFeatures'], ['inlineUniformBlock', 'descriptorBindingInlineUniformBlockUpdateAfterBind']), 102 (['Vulkan13Features', 'PipelineCreationCacheControlFeatures'], ['pipelineCreationCacheControl']), 103 (['Vulkan13Features', 'PrivateDataFeatures'], ['privateData']), 104 (['Vulkan13Features', 'ShaderDemoteToHelperInvocationFeatures'], ['shaderDemoteToHelperInvocation']), 105 (['Vulkan13Features', 'ShaderTerminateInvocationFeatures'], ['shaderTerminateInvocation']), 106 (['Vulkan13Features', 'SubgroupSizeControlFeatures'], ['subgroupSizeControl', 'computeFullSubgroups']), 107 (['Vulkan13Features', 'Synchronization2Features'], ['synchronization2']), 108 (['Vulkan13Features', 'TextureCompressionASTCHDRFeatures'], ['textureCompressionASTC_HDR']), 109 (['Vulkan13Features', 'ZeroInitializeWorkgroupMemoryFeatures'], ['shaderZeroInitializeWorkgroupMemory']), 110 (['Vulkan13Features', 'DynamicRenderingFeatures'], ['dynamicRendering']), 111 (['Vulkan13Features', 'ShaderIntegerDotProductFeatures'], ['shaderIntegerDotProduct']), 112 (['Vulkan13Features', 'Maintenance4Features'], ['maintenance4']), 113] 114 115for (feature_structs, features) in KNOWN_ALIASES: 116 for flag in features: 117 for f in feature_structs: 118 rename = (f, flag) 119 assert rename not in RENAMED_FEATURES, f"{rename} already exists in RENAMED_FEATURES" 120 RENAMED_FEATURES[rename] = flag 121 122def get_renamed_feature(c_type, feature): 123 return RENAMED_FEATURES.get((str_removeprefix(c_type, 'VkPhysicalDevice'), feature), feature) 124 125@dataclass 126class FeatureStruct: 127 c_type: str 128 s_type: str 129 features: typing.List[str] 130 131TEMPLATE_H = Template(COPYRIGHT + """ 132/* This file generated from ${filename}, don't edit directly. */ 133#ifndef VK_FEATURES_H 134#define VK_FEATURES_H 135 136#ifdef __cplusplus 137extern "C" { 138#endif 139 140struct vk_features { 141% for flag in all_flags: 142 bool ${flag}; 143% endfor 144}; 145 146void 147vk_set_physical_device_features(struct vk_features *all_features, 148 const VkPhysicalDeviceFeatures2 *pFeatures); 149 150void 151vk_set_physical_device_features_1_0(struct vk_features *all_features, 152 const VkPhysicalDeviceFeatures *pFeatures); 153 154#ifdef __cplusplus 155} 156#endif 157 158#endif 159""", output_encoding='utf-8') 160 161TEMPLATE_C = Template(COPYRIGHT + """ 162/* This file generated from ${filename}, don't edit directly. */ 163 164#include "vk_common_entrypoints.h" 165#include "vk_log.h" 166#include "vk_physical_device.h" 167#include "vk_physical_device_features.h" 168#include "vk_util.h" 169 170static VkResult 171check_physical_device_features(struct vk_physical_device *physical_device, 172 const VkPhysicalDeviceFeatures *supported, 173 const VkPhysicalDeviceFeatures *enabled, 174 const char *struct_name) 175{ 176% for flag in pdev_features: 177 if (enabled->${flag} && !supported->${flag}) 178 return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT, 179 "%s.%s not supported", struct_name, "${flag}"); 180% endfor 181 182 return VK_SUCCESS; 183} 184 185VkResult 186vk_physical_device_check_device_features(struct vk_physical_device *physical_device, 187 const VkDeviceCreateInfo *pCreateInfo) 188{ 189 VkPhysicalDevice vk_physical_device = 190 vk_physical_device_to_handle(physical_device); 191 192 /* Query the device what kind of features are supported. */ 193 VkPhysicalDeviceFeatures2 supported_features2 = { 194 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, 195 }; 196 197% for f in feature_structs: 198 ${f.c_type} supported_${f.c_type} = { .pNext = NULL }; 199% endfor 200 201 vk_foreach_struct_const(features, pCreateInfo->pNext) { 202 VkBaseOutStructure *supported = NULL; 203 switch (features->sType) { 204% for f in feature_structs: 205 case ${f.s_type}: 206 supported = (VkBaseOutStructure *) &supported_${f.c_type}; 207 break; 208% endfor 209 default: 210 break; 211 } 212 213 /* Not a feature struct. */ 214 if (!supported) 215 continue; 216 217 /* Check for cycles in the list */ 218 if (supported->pNext != NULL || supported->sType != 0) 219 return VK_ERROR_UNKNOWN; 220 221 supported->sType = features->sType; 222 __vk_append_struct(&supported_features2, supported); 223 } 224 225 physical_device->dispatch_table.GetPhysicalDeviceFeatures2( 226 vk_physical_device, &supported_features2); 227 228 if (pCreateInfo->pEnabledFeatures) { 229 VkResult result = 230 check_physical_device_features(physical_device, 231 &supported_features2.features, 232 pCreateInfo->pEnabledFeatures, 233 "VkPhysicalDeviceFeatures"); 234 if (result != VK_SUCCESS) 235 return result; 236 } 237 238 /* Iterate through additional feature structs */ 239 vk_foreach_struct_const(features, pCreateInfo->pNext) { 240 /* Check each feature boolean for given structure. */ 241 switch (features->sType) { 242 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { 243 const VkPhysicalDeviceFeatures2 *features2 = (const void *)features; 244 VkResult result = 245 check_physical_device_features(physical_device, 246 &supported_features2.features, 247 &features2->features, 248 "VkPhysicalDeviceFeatures2.features"); 249 if (result != VK_SUCCESS) 250 return result; 251 break; 252 } 253% for f in feature_structs: 254 case ${f.s_type}: { 255 const ${f.c_type} *a = &supported_${f.c_type}; 256 const ${f.c_type} *b = (const void *) features; 257% for flag in f.features: 258 if (b->${flag} && !a->${flag}) 259 return vk_errorf(physical_device, VK_ERROR_FEATURE_NOT_PRESENT, 260 "%s.%s not supported", "${f.c_type}", "${flag}"); 261% endfor 262 break; 263 } 264% endfor 265 default: 266 break; 267 } 268 } // for each extension structure 269 return VK_SUCCESS; 270} 271 272VKAPI_ATTR void VKAPI_CALL 273vk_common_GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, 274 VkPhysicalDeviceFeatures2 *pFeatures) 275{ 276 VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice); 277 278% for flag in pdev_features: 279 pFeatures->features.${flag} = pdevice->supported_features.${flag}; 280% endfor 281 282 vk_foreach_struct(ext, pFeatures) { 283 switch (ext->sType) { 284% for f in feature_structs: 285 case ${f.s_type}: { 286 ${f.c_type} *features = (void *) ext; 287% for flag in f.features: 288 features->${flag} = pdevice->supported_features.${get_renamed_feature(f.c_type, flag)}; 289% endfor 290 break; 291 } 292 293% endfor 294 default: 295 break; 296 } 297 } 298} 299 300void 301vk_set_physical_device_features(struct vk_features *all_features, 302 const VkPhysicalDeviceFeatures2 *pFeatures) 303{ 304 vk_foreach_struct_const(ext, pFeatures) { 305 switch (ext->sType) { 306 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: { 307 const VkPhysicalDeviceFeatures2 *features = (const void *) ext; 308 vk_set_physical_device_features_1_0(all_features, &features->features); 309 break; 310 } 311 312% for f in feature_structs: 313 case ${f.s_type}: { 314 const ${f.c_type} *features = (const void *) ext; 315% for flag in f.features: 316 if (features->${flag}) 317 all_features->${get_renamed_feature(f.c_type, flag)} = true; 318% endfor 319 break; 320 } 321 322% endfor 323 default: 324 break; 325 } 326 } 327} 328 329void 330vk_set_physical_device_features_1_0(struct vk_features *all_features, 331 const VkPhysicalDeviceFeatures *pFeatures) 332{ 333% for flag in pdev_features: 334 if (pFeatures->${flag}) 335 all_features->${flag} = true; 336% endfor 337} 338""", output_encoding='utf-8') 339 340def get_pdev_features(doc): 341 _type = doc.find(".types/type[@name='VkPhysicalDeviceFeatures']") 342 if _type is not None: 343 flags = [] 344 for p in _type.findall('./member'): 345 assert p.find('./type').text == 'VkBool32' 346 flags.append(p.find('./name').text) 347 return flags 348 return None 349 350def filter_api(elem, api): 351 if 'api' not in elem.attrib: 352 return True 353 354 return api in elem.attrib['api'].split(',') 355 356def get_feature_structs(doc, api, beta): 357 feature_structs = OrderedDict() 358 359 required = get_all_required(doc, 'type', api, beta) 360 361 # parse all struct types where structextends VkPhysicalDeviceFeatures2 362 for _type in doc.findall('./types/type[@category="struct"]'): 363 if _type.attrib.get('structextends') != 'VkPhysicalDeviceFeatures2,VkDeviceCreateInfo': 364 continue 365 if _type.attrib['name'] not in required: 366 continue 367 368 # Skip extensions with a define for now 369 guard = required[_type.attrib['name']].guard 370 if guard is not None and (guard != "VK_ENABLE_BETA_EXTENSIONS" or not beta): 371 continue 372 373 # find Vulkan structure type 374 for elem in _type: 375 if "STRUCTURE_TYPE" in str(elem.attrib): 376 s_type = elem.attrib.get('values') 377 378 # collect a list of feature flags 379 flags = [] 380 381 for p in _type.findall('./member'): 382 if not filter_api(p, api): 383 continue 384 385 m_name = p.find('./name').text 386 if m_name == 'pNext': 387 pass 388 elif m_name == 'sType': 389 s_type = p.attrib.get('values') 390 else: 391 assert p.find('./type').text == 'VkBool32' 392 flags.append(m_name) 393 394 feature_struct = FeatureStruct(c_type=_type.attrib.get('name'), s_type=s_type, features=flags) 395 feature_structs[feature_struct.c_type] = feature_struct 396 397 return feature_structs.values() 398 399def get_feature_structs_from_xml(xml_files, beta, api='vulkan'): 400 diagnostics = [] 401 402 pdev_features = None 403 feature_structs = [] 404 405 for filename in xml_files: 406 doc = et.parse(filename) 407 feature_structs += get_feature_structs(doc, api, beta) 408 if not pdev_features: 409 pdev_features = get_pdev_features(doc) 410 411 unused_renames = {**RENAMED_FEATURES} 412 413 features = OrderedDict() 414 415 for flag in pdev_features: 416 features[flag] = 'VkPhysicalDeviceFeatures' 417 418 for f in feature_structs: 419 for flag in f.features: 420 renamed_flag = get_renamed_feature(f.c_type, flag) 421 if renamed_flag not in features: 422 features[renamed_flag] = f.c_type 423 else: 424 a = str_removeprefix(features[renamed_flag], 'VkPhysicalDevice') 425 b = str_removeprefix(f.c_type, 'VkPhysicalDevice') 426 if (a, flag) not in RENAMED_FEATURES or (b, flag) not in RENAMED_FEATURES: 427 diagnostics.append(f'{a} and {b} both define {flag}') 428 429 unused_renames.pop((str_removeprefix(f.c_type, 'VkPhysicalDevice'), flag), None) 430 431 for rename in unused_renames: 432 diagnostics.append(f'unused rename {rename}') 433 434 assert len(diagnostics) == 0, '\n'.join(diagnostics) 435 436 return pdev_features, feature_structs, features 437 438 439def main(): 440 parser = argparse.ArgumentParser() 441 parser.add_argument('--out-c', required=True, help='Output C file.') 442 parser.add_argument('--out-h', required=True, help='Output H file.') 443 parser.add_argument('--beta', required=True, help='Enable beta extensions.') 444 parser.add_argument('--xml', 445 help='Vulkan API XML file.', 446 required=True, action='append', dest='xml_files') 447 args = parser.parse_args() 448 449 pdev_features, feature_structs, all_flags = get_feature_structs_from_xml(args.xml_files, args.beta) 450 451 environment = { 452 'filename': os.path.basename(__file__), 453 'pdev_features': pdev_features, 454 'feature_structs': feature_structs, 455 'all_flags': all_flags, 456 'get_renamed_feature': get_renamed_feature, 457 } 458 459 try: 460 with open(args.out_c, 'wb') as f: 461 f.write(TEMPLATE_C.render(**environment)) 462 with open(args.out_h, 'wb') as f: 463 f.write(TEMPLATE_H.render(**environment)) 464 except Exception: 465 # In the event there's an error, this uses some helpers from mako 466 # to print a useful stack trace and prints it, then exits with 467 # status 1, if python is run with debug; otherwise it just raises 468 # the exception 469 print(mako.exceptions.text_error_template().render(), file=sys.stderr) 470 sys.exit(1) 471 472if __name__ == '__main__': 473 main() 474