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 32import re 33 34import mako 35from mako.template import Template 36 37from vk_extensions import get_all_required, filter_api 38 39def str_removeprefix(s, prefix): 40 if s.startswith(prefix): 41 return s[len(prefix):] 42 return s 43 44# Some extensions have been promoted to core, their properties are renamed 45# in the following hashtable. 46# The hashtable takes the form: 47# (VkPhysicalDevice{PropertyStruct}, PropertyName): RenamedPropertyName 48# Drivers just have to fill the RenamedPropertyName field in their struct 49# vk_properties, the runtime will expose the data with the original/right 50# name to consumers. 51RENAMED_PROPERTIES = { 52 ("DrmPropertiesEXT", "hasPrimary"): "drmHasPrimary", 53 ("DrmPropertiesEXT", "primaryMajor"): "drmPrimaryMajor", 54 ("DrmPropertiesEXT", "primaryMinor"): "drmPrimaryMinor", 55 ("DrmPropertiesEXT", "hasRender"): "drmHasRender", 56 ("DrmPropertiesEXT", "renderMajor"): "drmRenderMajor", 57 ("DrmPropertiesEXT", "renderMinor"): "drmRenderMinor", 58 ("SparseProperties", "residencyStandard2DBlockShape"): "sparseResidencyStandard2DBlockShape", 59 ("SparseProperties", "residencyStandard2DMultisampleBlockShape"): "sparseResidencyStandard2DMultisampleBlockShape", 60 ("SparseProperties", "residencyStandard3DBlockShape"): "sparseResidencyStandard3DBlockShape", 61 ("SparseProperties", "residencyAlignedMipSize"): "sparseResidencyAlignedMipSize", 62 ("SparseProperties", "residencyNonResidentStrict"): "sparseResidencyNonResidentStrict", 63 ("SubgroupProperties", "supportedStages"): "subgroupSupportedStages", 64 ("SubgroupProperties", "supportedOperations"): "subgroupSupportedOperations", 65 ("SubgroupProperties", "quadOperationsInAllStages"): "subgroupQuadOperationsInAllStages", 66} 67 68OUT_ARRAYS = { 69 'pCopySrcLayouts': 'copySrcLayoutCount', 70 'pCopyDstLayouts': 'copyDstLayoutCount', 71 'pLayeredApis': 'layeredApiCount', 72} 73OUT_ARRAY_COUNTS = OUT_ARRAYS.values() 74 75SPECIALIZED_PROPERTY_STRUCTS = [ 76] 77 78# Properties not extending VkPhysicalDeviceProperties2 in the XML, 79# but which might still be present (in Android for instance) 80ANDROID_PROPERTIES = [ 81 "VkPhysicalDevicePresentationPropertiesANDROID", 82] 83 84@dataclass 85class Property: 86 decl: str 87 name: str 88 actual_name: str 89 length: str 90 is_android: bool 91 92 def __init__(self, p, property_struct_name, is_android=False): 93 self.decl = "" 94 for element in p: 95 if element.tag != "comment": 96 self.decl += "".join(element.itertext()) 97 if element.tail: 98 self.decl += re.sub(" +", " ", element.tail) 99 100 self.name = p.find("./name").text 101 self.actual_name = RENAMED_PROPERTIES.get((property_struct_name, self.name), self.name) 102 103 length = p.attrib.get("len", "1") 104 self.length = RENAMED_PROPERTIES.get((property_struct_name, length), length) 105 106 self.decl = self.decl.replace(self.name, self.actual_name) 107 108 self.is_android = is_android 109 110@dataclass 111class PropertyStruct: 112 c_type: str 113 s_type: str 114 name: str 115 is_android: bool 116 properties: typing.List[Property] 117 118ARRAY_COPY_TEMPLATE = Template(""" 119 if (${dst_ptr} != NULL) { 120 uint32_t count = MIN2(${dst_count}, ${src_count}); 121 for (uint32_t i = 0; i < count; i++) 122 ${dst_ptr}[i] = ${src_ptr}[i]; 123 ${dst_count} = count; 124 } else { 125 ${dst_count} = ${src_count}; 126 } 127""") 128 129def copy_property(dst_prefix, dst_name, src_prefix, src_name, decl, length="1"): 130 if src_name in OUT_ARRAY_COUNTS: 131 assert dst_name in OUT_ARRAY_COUNTS 132 # Skip these as we'll fill them out along with the data 133 return "" 134 elif src_name in OUT_ARRAYS: 135 assert dst_name in OUT_ARRAYS 136 137 return ARRAY_COPY_TEMPLATE.render( 138 dst_ptr=dst_prefix + dst_name, 139 dst_count=dst_prefix + OUT_ARRAYS[dst_name], 140 src_ptr=src_prefix + src_name, 141 src_count=src_prefix + OUT_ARRAYS[src_name] 142 ) 143 144 assert "*" not in decl 145 dst = dst_prefix + dst_name 146 src = src_prefix + src_name 147 148 if "[" in decl: 149 return "memcpy(%s, %s, sizeof(%s));" % (dst, src, dst) 150 else: 151 return "%s = %s;" % (dst, src) 152 153TEMPLATE_H = Template(COPYRIGHT + """ 154/* This file generated from ${filename}, don"t edit directly. */ 155#ifndef VK_PROPERTIES_H 156#define VK_PROPERTIES_H 157 158#if DETECT_OS_ANDROID 159#include "vulkan/vk_android_native_buffer.h" 160#endif /* DETECT_OS_ANDROID */ 161 162#ifdef __cplusplus 163extern "C" { 164#endif 165 166struct vk_properties { 167% for prop in all_properties: 168% if prop.is_android: 169#if DETECT_OS_ANDROID 170% endif 171 ${prop.decl}; 172% if prop.is_android: 173#endif /* DETECT_OS_ANDROID */ 174% endif 175% endfor 176}; 177 178void 179vk_set_physical_device_properties_struct(struct vk_properties *all_properties, 180 const VkBaseInStructure *pProperties); 181 182#ifdef __cplusplus 183} 184#endif 185 186#endif 187""") 188 189TEMPLATE_C = Template(COPYRIGHT + """ 190/* This file generated from ${filename}, don"t edit directly. */ 191 192#include "vk_common_entrypoints.h" 193#include "vk_log.h" 194#include "vk_physical_device.h" 195#include "vk_physical_device_properties.h" 196#include "vk_util.h" 197 198VKAPI_ATTR void VKAPI_CALL 199vk_common_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, 200 VkPhysicalDeviceProperties2 *pProperties) 201{ 202 VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice); 203 204% for prop in pdev_properties: 205 ${copy_property("pProperties->properties.", prop.name, "pdevice->properties.", prop.actual_name, prop.decl)} 206% endfor 207 208 vk_foreach_struct(ext, pProperties->pNext) { 209 switch ((int32_t)ext->sType) { 210% for property_struct in property_structs: 211% if property_struct.is_android: 212#if DETECT_OS_ANDROID 213% endif 214% if property_struct.name not in SPECIALIZED_PROPERTY_STRUCTS: 215 case ${property_struct.s_type}: { 216 ${property_struct.c_type} *properties = (void *)ext; 217% for prop in property_struct.properties: 218 ${copy_property("properties->", prop.name, "pdevice->properties.", prop.actual_name, prop.decl, "pdevice->properties." + prop.length)} 219% endfor 220 break; 221 } 222% if property_struct.is_android: 223#endif /* DETECT_OS_ANDROID */ 224% endif 225% endif 226% endfor 227 228 /* Specialized propery handling defined in vk_physical_device_properties_gen.py */ 229 230 default: 231 break; 232 } 233 } 234} 235 236void 237vk_set_physical_device_properties_struct(struct vk_properties *all_properties, 238 const VkBaseInStructure *pProperties) 239{ 240 switch ((int32_t)pProperties->sType) { 241 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2: { 242 const VkPhysicalDeviceProperties *properties = &((const VkPhysicalDeviceProperties2 *)pProperties)->properties; 243% for prop in pdev_properties: 244 ${copy_property("all_properties->", prop.actual_name, "properties->", prop.name, prop.decl)} 245% endfor 246 break; 247 } 248 249% for property_struct in property_structs: 250% if property_struct.is_android: 251#if DETECT_OS_ANDROID 252% endif 253% if property_struct.name not in SPECIALIZED_PROPERTY_STRUCTS: 254 case ${property_struct.s_type}: { 255 const ${property_struct.c_type} *properties = (const ${property_struct.c_type} *)pProperties; 256% for prop in property_struct.properties: 257 ${copy_property("all_properties->", prop.actual_name, "properties->", prop.name, prop.decl, "properties." + prop.length)} 258% endfor 259 break; 260 } 261% if property_struct.is_android: 262#endif /* DETECT_OS_ANDROID */ 263% endif 264% endif 265% endfor 266 267 /* Don't assume anything with this struct type, and just copy things over */ 268 269 default: 270 break; 271 } 272} 273 274""") 275 276def get_pdev_properties(doc, struct_name): 277 _type = doc.find(".types/type[@name=\"VkPhysicalDevice%s\"]" % struct_name) 278 if _type is not None: 279 properties = [] 280 for p in _type.findall("./member"): 281 properties.append(Property(p, struct_name)) 282 return properties 283 return None 284 285def filter_api(elem, api): 286 if "api" not in elem.attrib: 287 return True 288 289 return api in elem.attrib["api"].split(",") 290 291def get_property_structs(doc, api, beta): 292 property_structs = OrderedDict() 293 294 required = get_all_required(doc, "type", api, beta) 295 296 # parse all struct types where structextends VkPhysicalDeviceProperties2 297 for _type in doc.findall("./types/type[@category=\"struct\"]"): 298 full_name = _type.attrib.get("name") 299 300 if _type.attrib.get("structextends") != "VkPhysicalDeviceProperties2": 301 if full_name not in ANDROID_PROPERTIES: 302 continue 303 304 if full_name not in required: 305 continue 306 307 guard = required[full_name].guard 308 is_android = full_name in ANDROID_PROPERTIES 309 310 if (guard is not None 311 # Skip beta extensions if not enabled 312 and (guard != "VK_ENABLE_BETA_EXTENSIONS" or beta != "true") 313 # Include android properties if included in ANDROID_PROPERTIES 314 and not is_android): 315 continue 316 317 # find Vulkan structure type 318 for elem in _type: 319 if "STRUCTURE_TYPE" in str(elem.attrib): 320 s_type = elem.attrib.get("values") 321 322 name = str_removeprefix(full_name, "VkPhysicalDevice") 323 324 # collect a list of properties 325 properties = [] 326 327 for p in _type.findall("./member"): 328 if not filter_api(p, api): 329 continue 330 331 m_name = p.find("./name").text 332 if m_name == "pNext": 333 pass 334 elif m_name == "sType": 335 s_type = p.attrib.get("values") 336 else: 337 properties.append(Property(p, name, is_android)) 338 339 property_struct = PropertyStruct(c_type=full_name, s_type=s_type, 340 name=name, properties=properties, is_android=is_android) 341 property_structs[property_struct.c_type] = property_struct 342 343 return property_structs.values() 344 345def get_property_structs_from_xml(xml_files, beta, api="vulkan"): 346 diagnostics = [] 347 348 pdev_properties = None 349 property_structs = [] 350 351 for filename in xml_files: 352 doc = et.parse(filename) 353 property_structs += get_property_structs(doc, api, beta) 354 if not pdev_properties: 355 pdev_properties = get_pdev_properties(doc, "Properties") 356 pdev_properties = [prop for prop in pdev_properties if prop.name != "limits" and prop.name != "sparseProperties"] 357 358 limits = get_pdev_properties(doc, "Limits") 359 for limit in limits: 360 limit.name = "limits." + limit.name 361 pdev_properties += limits 362 363 sparse_properties = get_pdev_properties(doc, "SparseProperties") 364 for prop in sparse_properties: 365 prop.name = "sparseProperties." + prop.name 366 pdev_properties += sparse_properties 367 368 # Gather all properties, make sure that aliased declarations match up. 369 property_names = OrderedDict() 370 all_properties = [] 371 for prop in pdev_properties: 372 property_names[prop.actual_name] = prop 373 all_properties.append(prop) 374 375 for property_struct in property_structs: 376 for prop in property_struct.properties: 377 if prop.actual_name not in property_names: 378 property_names[prop.actual_name] = prop 379 all_properties.append(prop) 380 elif prop.decl != property_names[prop.actual_name].decl: 381 diagnostics.append("Declaration mismatch ('%s' vs. '%s')" % (prop.decl, property_names[prop.actual_name].decl)) 382 383 return pdev_properties, property_structs, all_properties 384 385 386def main(): 387 parser = argparse.ArgumentParser() 388 parser.add_argument("--out-c", required=True, help="Output C file.") 389 parser.add_argument("--out-h", required=True, help="Output H file.") 390 parser.add_argument("--beta", required=True, help="Enable beta extensions.") 391 parser.add_argument("--xml", 392 help="Vulkan API XML file.", 393 required=True, action="append", dest="xml_files") 394 args = parser.parse_args() 395 396 pdev_properties, property_structs, all_properties = get_property_structs_from_xml(args.xml_files, args.beta) 397 398 environment = { 399 "filename": os.path.basename(__file__), 400 "pdev_properties": pdev_properties, 401 "property_structs": property_structs, 402 "all_properties": all_properties, 403 "copy_property": copy_property, 404 "SPECIALIZED_PROPERTY_STRUCTS": SPECIALIZED_PROPERTY_STRUCTS, 405 } 406 407 try: 408 with open(args.out_c, "w", encoding='utf-8') as f: 409 f.write(TEMPLATE_C.render(**environment)) 410 with open(args.out_h, "w", encoding='utf-8') as f: 411 f.write(TEMPLATE_H.render(**environment)) 412 except Exception: 413 # In the event there"s an error, this uses some helpers from mako 414 # to print a useful stack trace and prints it, then exits with 415 # status 1, if python is run with debug; otherwise it just raises 416 # the exception 417 print(mako.exceptions.text_error_template().render(), file=sys.stderr) 418 sys.exit(1) 419 420if __name__ == "__main__": 421 main() 422