xref: /aosp_15_r20/external/mesa3d/src/vulkan/util/vk_physical_device_properties_gen.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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