xref: /aosp_15_r20/external/mesa3d/src/gfxstream/codegen/scripts/genvk.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1#!/usr/bin/python3
2#
3# Copyright 2013-2023 The Khronos Group Inc.
4# Copyright 2023-2024 Google Inc.
5#
6# SPDX-License-Identifier: Apache-2.0
7
8import argparse
9import os
10import re
11import sys
12import xml.etree.ElementTree as etree
13
14sys.path.append(os.path.abspath(os.path.dirname(__file__)))
15
16from cgenerator import CGeneratorOptions, COutputGenerator
17
18from generator import write
19from reg import Registry
20
21# gfxstream + cereal modules
22from cerealgenerator import CerealGenerator
23
24from typing import Optional
25
26def makeREstring(strings, default=None, strings_are_regex=False):
27    """Turn a list of strings into a regexp string matching exactly those strings."""
28    if strings or default is None:
29        if not strings_are_regex:
30            strings = (re.escape(s) for s in strings)
31        return '^(' + '|'.join(strings) + ')$'
32    return default
33
34
35def makeGenOpts(args):
36    """Returns a directory of [ generator function, generator options ] indexed
37    by specified short names. The generator options incorporate the following
38    parameters:
39
40    args is an parsed argument object; see below for the fields that are used."""
41    global genOpts
42    genOpts = {}
43
44    # Output target directory
45    directory = args.directory
46
47    # Descriptive names for various regexp patterns used to select
48    # versions and extensions
49    allFormats = allFeatures = allExtensions = r'.*'
50
51    # Turn lists of names/patterns into matching regular expressions
52    emitExtensionsPat    = makeREstring([], allExtensions)
53    emitFormatsPat       = makeREstring([], allFormats)
54    featuresPat          = makeREstring([], allFeatures)
55
56    # Copyright text prefixing all headers (list of strings).
57    # The SPDX formatting below works around constraints of the 'reuse' tool
58    prefixStrings = [
59        '/*',
60        '** Copyright 2015-2023 The Khronos Group Inc.',
61        '**',
62        '** SPDX-License-Identifier' + ': Apache-2.0',
63        '*/',
64        ''
65    ]
66
67    # Text specific to Vulkan headers
68    vkPrefixStrings = [
69        '/*',
70        '** This header is generated from the Khronos Vulkan XML API Registry.',
71        '**',
72        '*/',
73        ''
74    ]
75
76    genOpts['cereal'] = [
77            CerealGenerator,
78            CGeneratorOptions(
79                directory         = directory,
80                versions          = featuresPat,
81                emitversions      = featuresPat,
82                addExtensions     = None,
83                emitExtensions    = emitExtensionsPat,
84                prefixText        = prefixStrings + vkPrefixStrings,
85                apientry          = 'VKAPI_CALL ',
86                apientryp         = 'VKAPI_PTR *',
87                alignFuncParam    = 48)
88        ]
89
90    gfxstreamPrefixStrings = [
91        '#pragma once',
92        '#ifdef VK_GFXSTREAM_STRUCTURE_TYPE_EXT',
93        '#include "vulkan_gfxstream_structure_type.h"',
94        '#endif',
95    ]
96
97    # gfxstream specific header
98    genOpts['vulkan_gfxstream.h'] = [
99          COutputGenerator,
100          CGeneratorOptions(
101            filename          = 'vulkan_gfxstream.h',
102            directory         = directory,
103            versions          = featuresPat,
104            emitversions      = None,
105            addExtensions     = makeREstring(['VK_GOOGLE_gfxstream'], None),
106            emitExtensions    = makeREstring(['VK_GOOGLE_gfxstream'], None),
107            prefixText        = prefixStrings + vkPrefixStrings + gfxstreamPrefixStrings,
108            # Use #pragma once in the prefixText instead, so that we can put the copyright comments
109            # at the beginning of the file.
110            apientry          = 'VKAPI_CALL ',
111            apientryp         = 'VKAPI_PTR *',
112            alignFuncParam    = 48)
113        ]
114
115def genTarget(args):
116    """Create an API generator and corresponding generator options based on
117    the requested target and command line options.
118
119    This is encapsulated in a function so it can be profiled and/or timed.
120    The args parameter is an parsed argument object containing the following
121    fields that are used:
122
123    - target - target to generate
124    - directory - directory to generate it in
125    - extensions - list of additional extensions to include in generated interfaces"""
126
127    # Create generator options with parameters specified on command line
128    makeGenOpts(args)
129
130    # Select a generator matching the requested target
131    if args.target in genOpts:
132        createGenerator = genOpts[args.target][0]
133        options = genOpts[args.target][1]
134
135        gen = createGenerator(errFile=errWarn,
136                              warnFile=errWarn,
137                              diagFile=diag)
138        return (gen, options)
139    else:
140        return None
141
142
143# -feature name
144# -extension name
145# For both, "name" may be a single name, or a space-separated list
146# of names, or a regular expression.
147if __name__ == '__main__':
148    parser = argparse.ArgumentParser()
149    parser.add_argument('-registry', action='store',
150                        default='vk.xml',
151                        help='Use specified registry file instead of vk.xml')
152    parser.add_argument('-registryGfxstream', action='store',
153                        default=None,
154                        help='Use specified gfxstream registry file')
155    parser.add_argument('-o', action='store', dest='directory',
156                        default='.',
157                        help='Create target and related files in specified directory')
158    parser.add_argument('target', metavar='target', nargs='?',
159                        help='Specify target')
160
161    args = parser.parse_args()
162
163    errWarn = sys.stderr
164    diag = None
165
166    # Create the API generator & generator options
167    (gen, options) = genTarget(args)
168
169    # Create the registry object with the specified generator and generator
170    # options. The options are set before XML loading as they may affect it.
171    reg = Registry(gen, options)
172
173    # Parse the specified registry XML into an ElementTree object
174    tree = etree.parse(args.registry)
175
176    # Merge the gfxstream registry with the official Vulkan registry if the
177    # target is the cereal generator
178    if args.registryGfxstream is not None and args.target == 'cereal':
179        treeGfxstream = etree.parse(args.registryGfxstream)
180        treeRoot = tree.getroot()
181        treeGfxstreamRoot = treeGfxstream.getroot()
182
183        def getEntryName(entry) -> Optional[str]:
184            name = entry.get("name")
185            if name is not None:
186                return name
187            try:
188                return entry.find("proto").find("name").text
189            except AttributeError:
190                return None
191
192        for entriesName in ['types', 'commands', 'extensions']:
193            treeEntries = treeRoot.find(entriesName)
194
195            originalEntryDict = {}
196            for entry in treeEntries:
197                name = getEntryName(entry)
198                if name is not None:
199                    originalEntryDict[name] = entry
200
201            for entry in treeGfxstreamRoot.find(entriesName):
202                name = getEntryName(entry)
203                # New entry, just append to entry list
204                if name not in originalEntryDict.keys():
205                    treeEntries.append(entry)
206                    continue
207
208                originalEntry = originalEntryDict[name]
209
210                # Extending an existing entry. This happens for MVK.
211                if entriesName == "extensions":
212                    for key, value in entry.attrib.items():
213                        originalEntry.set(key, value)
214                    require = entry.find("require")
215                    if require is not None:
216                        for child in require:
217                            originalEntry.find("require").append(child)
218                    continue
219
220                # Overwriting an existing entry. This happen for
221                # VkNativeBufferANDROID
222                if entriesName == "types" or entriesName == "commands":
223                    originalEntry.clear()
224                    originalEntry.attrib = entry.attrib
225                    for child in entry:
226                        originalEntry.append(child)
227
228    # Load the XML tree into the registry object
229    reg.loadElementTree(tree)
230    reg.apiGen()
231