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 44RENAMED_PROPERTIES = { 45 ("DrmPropertiesEXT", "hasPrimary"): "drmHasPrimary", 46 ("DrmPropertiesEXT", "primaryMajor"): "drmPrimaryMajor", 47 ("DrmPropertiesEXT", "primaryMinor"): "drmPrimaryMinor", 48 ("DrmPropertiesEXT", "hasRender"): "drmHasRender", 49 ("DrmPropertiesEXT", "renderMajor"): "drmRenderMajor", 50 ("DrmPropertiesEXT", "renderMinor"): "drmRenderMinor", 51 ("SparseProperties", "residencyStandard2DBlockShape"): "sparseResidencyStandard2DBlockShape", 52 ("SparseProperties", "residencyStandard2DMultisampleBlockShape"): "sparseResidencyStandard2DMultisampleBlockShape", 53 ("SparseProperties", "residencyStandard3DBlockShape"): "sparseResidencyStandard3DBlockShape", 54 ("SparseProperties", "residencyAlignedMipSize"): "sparseResidencyAlignedMipSize", 55 ("SparseProperties", "residencyNonResidentStrict"): "sparseResidencyNonResidentStrict", 56 ("SubgroupProperties", "supportedStages"): "subgroupSupportedStages", 57 ("SubgroupProperties", "supportedOperations"): "subgroupSupportedOperations", 58 ("SubgroupProperties", "quadOperationsInAllStages"): "subgroupQuadOperationsInAllStages", 59} 60 61SPECIALIZED_PROPERTY_STRUCTS = [ 62 "HostImageCopyPropertiesEXT", 63] 64 65@dataclass 66class Property: 67 decl: str 68 name: str 69 actual_name: str 70 length: str 71 72 def __init__(self, p, property_struct_name): 73 self.decl = "" 74 for element in p: 75 if element.tag != "comment": 76 self.decl += "".join(element.itertext()) 77 if element.tail: 78 self.decl += re.sub(" +", " ", element.tail) 79 80 self.name = p.find("./name").text 81 self.actual_name = RENAMED_PROPERTIES.get((property_struct_name, self.name), self.name) 82 83 length = p.attrib.get("len", "1") 84 self.length = RENAMED_PROPERTIES.get((property_struct_name, length), length) 85 86 self.decl = self.decl.replace(self.name, self.actual_name) 87 88@dataclass 89class PropertyStruct: 90 c_type: str 91 s_type: str 92 name: str 93 properties: typing.List[Property] 94 95def copy_property(dst, src, decl, length="1"): 96 assert "*" not in decl 97 98 if "[" in decl: 99 return "memcpy(%s, %s, sizeof(%s));" % (dst, src, dst) 100 else: 101 return "%s = %s;" % (dst, src) 102 103TEMPLATE_H = Template(COPYRIGHT + """ 104/* This file generated from ${filename}, don"t edit directly. */ 105#ifndef VK_PROPERTIES_H 106#define VK_PROPERTIES_H 107 108#ifdef __cplusplus 109extern "C" { 110#endif 111 112struct vk_properties { 113% for prop in all_properties: 114 ${prop.decl}; 115% endfor 116}; 117 118#ifdef __cplusplus 119} 120#endif 121 122#endif 123""", output_encoding="utf-8") 124 125TEMPLATE_C = Template(COPYRIGHT + """ 126/* This file generated from ${filename}, don"t edit directly. */ 127 128#include "vk_common_entrypoints.h" 129#include "vk_log.h" 130#include "vk_physical_device.h" 131#include "vk_physical_device_properties.h" 132#include "vk_util.h" 133 134VKAPI_ATTR void VKAPI_CALL 135vk_common_GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, 136 VkPhysicalDeviceProperties2 *pProperties) 137{ 138 VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice); 139 140% for prop in pdev_properties: 141 ${copy_property("pProperties->properties." + prop.name, "pdevice->properties." + prop.actual_name, prop.decl)} 142% endfor 143 144 vk_foreach_struct(ext, pProperties) { 145 switch (ext->sType) { 146% for property_struct in property_structs: 147% if property_struct.name not in SPECIALIZED_PROPERTY_STRUCTS: 148 case ${property_struct.s_type}: { 149 ${property_struct.c_type} *properties = (void *)ext; 150% for prop in property_struct.properties: 151 ${copy_property("properties->" + prop.name, "pdevice->properties." + prop.actual_name, prop.decl, "pdevice->properties." + prop.length)} 152% endfor 153 break; 154 } 155% endif 156% endfor 157 158 /* Specialized propery handling defined in vk_physical_device_properties_gen.py */ 159 160 case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_PROPERTIES_EXT: { 161 VkPhysicalDeviceHostImageCopyPropertiesEXT *properties = (void *)ext; 162 163 if (properties->pCopySrcLayouts) { 164 uint32_t written_layout_count = MIN2(properties->copySrcLayoutCount, 165 pdevice->properties.copySrcLayoutCount); 166 memcpy(properties->pCopySrcLayouts, pdevice->properties.pCopySrcLayouts, 167 sizeof(VkImageLayout) * written_layout_count); 168 properties->copySrcLayoutCount = written_layout_count; 169 } else { 170 properties->copySrcLayoutCount = pdevice->properties.copySrcLayoutCount; 171 } 172 173 if (properties->pCopyDstLayouts) { 174 uint32_t written_layout_count = MIN2(properties->copyDstLayoutCount, 175 pdevice->properties.copyDstLayoutCount); 176 memcpy(properties->pCopyDstLayouts, pdevice->properties.pCopyDstLayouts, 177 sizeof(VkImageLayout) * written_layout_count); 178 properties->copyDstLayoutCount = written_layout_count; 179 } else { 180 properties->copyDstLayoutCount = pdevice->properties.copyDstLayoutCount; 181 } 182 183 memcpy(properties->optimalTilingLayoutUUID, pdevice->properties.optimalTilingLayoutUUID, VK_UUID_SIZE); 184 properties->identicalMemoryTypeRequirements = pdevice->properties.identicalMemoryTypeRequirements; 185 break; 186 } 187 188 default: 189 break; 190 } 191 } 192} 193""", output_encoding="utf-8") 194 195def get_pdev_properties(doc, struct_name): 196 _type = doc.find(".types/type[@name=\"VkPhysicalDevice%s\"]" % struct_name) 197 if _type is not None: 198 properties = [] 199 for p in _type.findall("./member"): 200 properties.append(Property(p, struct_name)) 201 return properties 202 return None 203 204def filter_api(elem, api): 205 if "api" not in elem.attrib: 206 return True 207 208 return api in elem.attrib["api"].split(",") 209 210def get_property_structs(doc, api, beta): 211 property_structs = OrderedDict() 212 213 required = get_all_required(doc, "type", api, beta) 214 215 # parse all struct types where structextends VkPhysicalDeviceProperties2 216 for _type in doc.findall("./types/type[@category=\"struct\"]"): 217 if _type.attrib.get("structextends") != "VkPhysicalDeviceProperties2": 218 continue 219 220 full_name = _type.attrib["name"] 221 if full_name not in required: 222 continue 223 224 # Skip extensions with a define for now 225 guard = required[full_name].guard 226 if guard is not None and (guard != "VK_ENABLE_BETA_EXTENSIONS" or not beta): 227 continue 228 229 # find Vulkan structure type 230 for elem in _type: 231 if "STRUCTURE_TYPE" in str(elem.attrib): 232 s_type = elem.attrib.get("values") 233 234 name = str_removeprefix(full_name, "VkPhysicalDevice") 235 236 # collect a list of properties 237 properties = [] 238 239 for p in _type.findall("./member"): 240 if not filter_api(p, api): 241 continue 242 243 m_name = p.find("./name").text 244 if m_name == "pNext": 245 pass 246 elif m_name == "sType": 247 s_type = p.attrib.get("values") 248 else: 249 properties.append(Property(p, name)) 250 251 property_struct = PropertyStruct(c_type=full_name, s_type=s_type, name=name, properties=properties) 252 property_structs[property_struct.c_type] = property_struct 253 254 return property_structs.values() 255 256def get_property_structs_from_xml(xml_files, beta, api="vulkan"): 257 diagnostics = [] 258 259 pdev_properties = None 260 property_structs = [] 261 262 for filename in xml_files: 263 doc = et.parse(filename) 264 property_structs += get_property_structs(doc, api, beta) 265 if not pdev_properties: 266 pdev_properties = get_pdev_properties(doc, "Properties") 267 pdev_properties = [prop for prop in pdev_properties if prop.name != "limits" and prop.name != "sparseProperties"] 268 269 limits = get_pdev_properties(doc, "Limits") 270 for limit in limits: 271 limit.name = "limits." + limit.name 272 pdev_properties += limits 273 274 sparse_properties = get_pdev_properties(doc, "SparseProperties") 275 for prop in sparse_properties: 276 prop.name = "sparseProperties." + prop.name 277 pdev_properties += sparse_properties 278 279 # Gather all properties, make sure that aliased declarations match up. 280 property_names = OrderedDict() 281 all_properties = [] 282 for prop in pdev_properties: 283 property_names[prop.actual_name] = prop 284 all_properties.append(prop) 285 286 for property_struct in property_structs: 287 for prop in property_struct.properties: 288 if prop.actual_name not in property_names: 289 property_names[prop.actual_name] = prop 290 all_properties.append(prop) 291 elif prop.decl != property_names[prop.actual_name].decl: 292 diagnostics.append("Declaration mismatch ('%s' vs. '%s')" % (prop.decl, property_names[prop.actual_name].decl)) 293 294 return pdev_properties, property_structs, all_properties 295 296 297def main(): 298 parser = argparse.ArgumentParser() 299 parser.add_argument("--out-c", required=True, help="Output C file.") 300 parser.add_argument("--out-h", required=True, help="Output H file.") 301 parser.add_argument("--beta", required=True, help="Enable beta extensions.") 302 parser.add_argument("--xml", 303 help="Vulkan API XML file.", 304 required=True, action="append", dest="xml_files") 305 args = parser.parse_args() 306 307 pdev_properties, property_structs, all_properties = get_property_structs_from_xml(args.xml_files, args.beta) 308 309 environment = { 310 "filename": os.path.basename(__file__), 311 "pdev_properties": pdev_properties, 312 "property_structs": property_structs, 313 "all_properties": all_properties, 314 "copy_property": copy_property, 315 "SPECIALIZED_PROPERTY_STRUCTS": SPECIALIZED_PROPERTY_STRUCTS, 316 } 317 318 try: 319 with open(args.out_c, "wb") as f: 320 f.write(TEMPLATE_C.render(**environment)) 321 with open(args.out_h, "wb") as f: 322 f.write(TEMPLATE_H.render(**environment)) 323 except Exception: 324 # In the event there"s an error, this uses some helpers from mako 325 # to print a useful stack trace and prints it, then exits with 326 # status 1, if python is run with debug; otherwise it just raises 327 # the exception 328 print(mako.exceptions.text_error_template().render(), file=sys.stderr) 329 sys.exit(1) 330 331if __name__ == "__main__": 332 main() 333