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