• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- coding: utf-8 -*-
2
3#-------------------------------------------------------------------------
4# Vulkan CTS
5# ----------
6#
7# Copyright (c) 2015 Google Inc.
8#
9# Licensed under the Apache License, Version 2.0 (the "License");
10# you may not use this file except in compliance with the License.
11# You may obtain a copy of the License at
12#
13#      http://www.apache.org/licenses/LICENSE-2.0
14#
15# Unless required by applicable law or agreed to in writing, software
16# distributed under the License is distributed on an "AS IS" BASIS,
17# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18# See the License for the specific language governing permissions and
19# limitations under the License.
20#
21#-------------------------------------------------------------------------
22
23import os
24import re
25import sys
26import glob
27import json
28import argparse
29import datetime
30import collections
31import ast
32import logging
33from lxml import etree
34
35scriptPath = os.path.join(os.path.dirname(__file__), "..", "..", "..", "scripts")
36sys.path.insert(0, scriptPath)
37
38from ctsbuild.common import *
39from khr_util.format import indentLines, writeInlFile
40
41VULKAN_XML_DIR = os.path.join(os.path.dirname(__file__), "..", "..", "vulkan-docs", "src", "xml")
42SCRIPTS_SRC_DIR = os.path.join(os.path.dirname(__file__), "src")
43DEFAULT_OUTPUT_DIR = { "" : os.path.join(os.path.dirname(__file__), "..", "framework", "vulkan", "generated", "vulkan"),
44                       "SC" : os.path.join(os.path.dirname(__file__), "..", "framework", "vulkan", "generated", "vulkansc") }
45
46EXTENSIONS_TO_READ_FROM_XML_NOT_JSON = """
47VK_KHR_video_encode_av1
48VK_KHR_video_encode_quantization_map
49""".split()
50
51EXTENSIONS_TO_READ_FROM_XML_NOT_JSON = [s for s in EXTENSIONS_TO_READ_FROM_XML_NOT_JSON if not s.startswith('#')]
52
53INL_HEADER = """\
54/* WARNING: This is auto-generated file. Do not modify, since changes will
55 * be lost! Modify the generating script instead.
56 * This file was generated by /scripts/gen_framework.py
57 */\
58
59"""
60
61DEFINITIONS = {
62    "VK_MAX_PHYSICAL_DEVICE_NAME_SIZE": "size_t",
63    "VK_MAX_EXTENSION_NAME_SIZE": "size_t",
64    "VK_MAX_DRIVER_NAME_SIZE": "size_t",
65    "VK_MAX_DRIVER_INFO_SIZE": "size_t",
66    "VK_UUID_SIZE": "size_t",
67    "VK_LUID_SIZE": "size_t",
68    "VK_MAX_MEMORY_TYPES": "size_t",
69    "VK_MAX_MEMORY_HEAPS": "size_t",
70    "VK_MAX_DESCRIPTION_SIZE": "size_t",
71    "VK_MAX_DEVICE_GROUP_SIZE": "size_t",
72    "VK_ATTACHMENT_UNUSED": "uint32_t",
73    "VK_SUBPASS_EXTERNAL": "uint32_t",
74    "VK_QUEUE_FAMILY_IGNORED": "uint32_t",
75    "VK_QUEUE_FAMILY_EXTERNAL": "uint32_t",
76    "VK_REMAINING_MIP_LEVELS": "uint32_t",
77    "VK_REMAINING_ARRAY_LAYERS": "uint32_t",
78    "VK_WHOLE_SIZE": "vk::VkDeviceSize",
79    "VK_TRUE": "vk::VkBool32",
80    "VK_FALSE": "vk::VkBool32",
81}
82
83PLATFORM_TYPES = [
84    # VK_KHR_xlib_surface
85    (["Display","*"], ["XlibDisplayPtr"], "void*"),
86    (["Window"], ["XlibWindow"], "uintptr_t",),
87    (["VisualID"], ["XlibVisualID"], "uint32_t"),
88
89    # VK_KHR_xcb_surface
90    (["xcb_connection_t", "*"], ["XcbConnectionPtr"], "void*"),
91    (["xcb_window_t"], ["XcbWindow"], "uintptr_t"),
92    (["xcb_visualid_t"], ["XcbVisualid"], "uint32_t"),
93
94    # VK_KHR_wayland_surface
95    (["struct", "wl_display","*"], ["WaylandDisplayPtr"], "void*"),
96    (["struct", "wl_surface", "*"], ["WaylandSurfacePtr"], "void*"),
97
98    # VK_KHR_mir_surface
99    (["MirConnection", "*"], ["MirConnectionPtr"], "void*"),
100    (["MirSurface", "*"], ["MirSurfacePtr"], "void*"),
101
102    # VK_KHR_android_surface
103    (["ANativeWindow", "*"], ["AndroidNativeWindowPtr"], "void*"),
104
105    # VK_KHR_win32_surface
106    (["HINSTANCE"], ["Win32InstanceHandle"], "void*"),
107    (["HWND"], ["Win32WindowHandle"], "void*"),
108    (["HANDLE"], ["Win32Handle"], "void*"),
109    (["const", "SECURITY_ATTRIBUTES", "*"], ["Win32SecurityAttributesPtr"], "const void*"),
110    (["AHardwareBuffer", "*"], ["AndroidHardwareBufferPtr"], "void*"),
111    (["HMONITOR"], ["Win32MonitorHandle"], "void*"),
112    (["LPCWSTR"], ["Win32LPCWSTR"], "const void*"),
113
114    # VK_EXT_acquire_xlib_display
115    (["RROutput"], ["RROutput"], "void*"),
116
117    (["zx_handle_t"], ["zx_handle_t"], "uint32_t"),
118    (["GgpFrameToken"], ["GgpFrameToken"], "int32_t"),
119    (["GgpStreamDescriptor"], ["GgpStreamDescriptor"], "int32_t"),
120    (["CAMetalLayer"], ["CAMetalLayer"], "void*"),
121    (["struct", "_screen_context", "*"], ["QNXScreenContextPtr"], "void*"),
122    (["struct", "_screen_window", "*"], ["QNXScreenWindowPtr"], "void*"),
123
124    # VK_EXT_metal_objects
125    (["MTLDevice_id"], ["MTLDevice_id"], "void*"),
126    (["MTLCommandQueue_id"], ["MTLCommandQueue_id"], "void*"),
127    (["MTLBuffer_id"], ["MTLBuffer_id"], "void*"),
128    (["MTLTexture_id"], ["MTLTexture_id"], "void*"),
129    (["IOSurfaceRef"], ["IOSurfaceRef"], "void*"),
130    (["MTLSharedEvent_id"], ["MTLSharedEvent_id"], "void*"),
131
132    # VK_NV_external_sci_sync
133    (["NvSciBufObj"], ["NvSciBufObj"], "int"),
134    (["NvSciSyncObj"], ["NvSciSyncObj"], "int"),
135    (["NvSciSyncFence"], ["NvSciSyncFence"], "int"),
136    (["NvSciBufAttrList"], ["NvSciBufAttrList"], "int"),
137    (["NvSciSyncAttrList"], ["NvSciSyncAttrList"], "int"),
138]
139
140PLATFORM_TYPE_NAMESPACE = "pt"
141
142TYPE_SUBSTITUTIONS = [
143    # Platform-specific
144    ("DWORD", "uint32_t"),
145    ("HANDLE*", PLATFORM_TYPE_NAMESPACE + "::" + "Win32Handle*"),
146]
147
148EXTENSION_POSTFIXES_STANDARD = ["KHR", "EXT"]
149EXTENSION_POSTFIXES_VENDOR = ["AMD", "ARM", "NV", 'INTEL', "NVX", "KHX", "NN", "MVK", "FUCHSIA", 'QCOM', "GGP", "QNX", "ANDROID", 'VALVE', 'HUAWEI']
150EXTENSION_POSTFIXES = EXTENSION_POSTFIXES_STANDARD + EXTENSION_POSTFIXES_VENDOR
151
152def printObjectAttributes(obj, indent=0):
153    indent_str = '    ' * indent
154    if isinstance(obj, dict):
155        for key, value in obj.items():
156            print(f"{indent_str}{key}:")
157            printObjectAttributes(value, indent + 1)
158    elif isinstance(obj, list):
159        for i, item in enumerate(obj):
160            print(f"{indent_str}[{i}]:")
161            printObjectAttributes(item, indent + 1)
162    elif hasattr(obj, '__dict__'):  # Check if the object has a __dict__ attribute
163        for key, value in obj.__dict__.items():
164            print(f"{indent_str}{key}:")
165            printObjectAttributes(value, indent + 1)
166    else:
167        print(f"{indent_str}{repr(obj)}")
168
169def printAttributesToFile(obj, file, indent=0):
170    try:
171        json_str = json.dumps(obj, indent=4)
172        file.write(json_str)
173    except TypeError:
174        # If serialization fails, fall back to custom printing and write to the file
175        indent_str = '    ' * indent
176        file.write(f"{indent_str}Object could not be serialized to JSON\n")
177        if isinstance(obj, dict):
178            for key, value in obj.items():
179                file.write(f"{indent_str}{key}:\n")
180                printAttributesToFile(value, file, indent + 1)
181        elif isinstance(obj, list):
182            for i, item in enumerate(obj):
183                file.write(f"{indent_str}[{i}]:\n")
184                printAttributesToFile(item, file, indent + 1)
185        elif hasattr(obj, '__dict__'):
186            for key, value in obj.__dict__.items():
187                file.write(f"{indent_str}{key}:\n")
188                printAttributesToFile(value, file, indent + 1)
189        else:
190            file.write(f"{indent_str}{repr(obj)}\n")
191
192# Converts the dependecies expression into an Abstract Syntax Tree that uses boolean operators
193def parseDependsEpression(string):
194    try:
195        # Parse the input string into an abstract syntax tree (AST)
196        tree = ast.parse(string.replace('+', ' and ').replace(',', ' or '), mode='eval')
197        expression = tree.body
198        return expression
199    except SyntaxError as e:
200        print(f"Syntax error in the input string: {e}")
201        return None
202
203# Checks the dependencies AST against the passed extensions
204def checkDependencyAST(node, extensions):
205    if isinstance(node, ast.BoolOp):
206        assert(len(node.values) >= 2)
207        value = checkDependencyAST(node.values.pop(), extensions)
208        while node.values:
209            nextValue = checkDependencyAST(node.values.pop(), extensions)
210            if isinstance(node.op, ast.And):
211                value = value and nextValue
212            if isinstance(node.op, ast.Or):
213                value = value or nextValue
214        return value
215    elif isinstance(node, ast.Name):
216        if '_VERSION_' in node.id:
217            return True
218        for ext in extensions:
219            if node.id == ext.name:
220                return True
221        return False
222    elif isinstance(node, ast.Constant):
223        return node.value
224
225# helper function that check if dependency is in list of extension
226def isDependencyMet(dependsExpression, extensionList):
227    if dependsExpression is None:
228        return True
229    tree = parseDependsEpression(dependsExpression)
230    # check if requirement dependencies are meet; if not then struct/function is not used
231    ret = checkDependencyAST(tree, extensionList)
232    return ret
233
234def substituteType(object): # both CompositeMember and FunctionArgument can be passed to this function
235    for src, dst in TYPE_SUBSTITUTIONS:
236        object.type = object.type.replace(src, dst)
237    for platformType, substitute, _ in PLATFORM_TYPES:
238        platformTypeName = platformType[0]
239        platformTypeName = platformType[-2] if "*" in platformType else platformType[0]
240        if object.type == platformTypeName:
241            object.type = PLATFORM_TYPE_NAMESPACE + '::' + substitute[0]
242            object.qualifiers = None if 'struct' in platformType else object.qualifiers
243            object.qualifiers = None if 'const' in platformType else object.qualifiers
244            if "*" in platformType:
245                object.pointer = "*" if object.pointer == "**" else None
246
247class Define:
248    def __init__ (self, name, aType, alias, value):
249        self.name = name
250        self.type = aType
251        self.alias = alias
252        self.value = value
253
254class Handle:
255    def __init__ (self, name, aType, alias, parent, objtypeenum):
256        self.name = name
257        self.type = aType
258        self.alias = alias
259        self.parent = parent
260        self.objtypeenum = objtypeenum
261
262class Bitmask:
263    def __init__ (self, name, aType, requires, bitvalues):
264        self.name = name
265        self.type = aType
266        self.alias = None           # initialy None but may be filled while parsing next tag
267        self.requires = requires
268        self.bitvalues = bitvalues
269
270class Enumerator:
271    def __init__ (self, name, value, bitpos):
272        self.name = name
273        self.aliasList = []         # list of strings
274        self.value = value          # some enums specify value and some bitpos
275        self.bitpos = bitpos
276        self.extension = None       # name of extension that added this enumerator
277
278class Enum:
279    def __init__ (self, name):
280        self.name = name
281        self.alias = None           # name of enum alias or None
282        self.type = None            # enum or bitmask
283        self.bitwidth = "32"
284        self.enumeratorList = []    # list of Enumerator objects
285
286    def areValuesLinear (self):
287        if self.type == 'bitmask':
288            return False
289        curIndex = 0
290        for enumerator in self.enumeratorList:
291            intValue = parseInt(enumerator.value)
292            if intValue != curIndex:
293                return False
294            curIndex += 1
295        return True
296
297class CompositeMember:
298    def __init__ (self, name, aType, pointer, qualifiers, arraySizeList, optional, limittype, values, fieldWidth):
299        self.name = name
300        self.type = aType                   # member type
301        self.pointer = pointer              # None, '*' or '**'
302        self.qualifiers = qualifiers        # 'const' or 'struct' or None
303        self.arraySizeList = arraySizeList  # can contain digits or enums
304        self.optional = optional
305        self.limittype = limittype
306        self.values = values                # allowed member values
307        self.fieldWidth = fieldWidth        # ':' followed by number of bits
308
309        # check if type should be swaped
310        substituteType(self)
311
312class Composite:
313    def __init__ (self, name, category, allowduplicate, structextends, returnedonly, members):
314        self.name = name
315        self.category = category            # is it struct or union
316        self.aliasList = []                 # most composite types have single alias but there are cases like VkPhysicalDeviceVariablePointersFeatures that have 3
317        self.allowduplicate = allowduplicate
318        self.structextends = structextends
319        self.returnedonly = returnedonly
320        self.members = members              # list of CompositeMember objects
321        self.notSupportedAlias = None       # alias used in not supported api e.g. VkPhysicalDeviceLineRasterizationFeaturesKHR is alias available only in vulkan but not in SC
322
323class FunctionArgument:
324    def __init__ (self, name, qualifiers, aType, pointer = None, secondPointerIsConst = False, arraySize = None, len = None):
325        self.name = name
326        self.qualifiers = qualifiers
327        self.type = aType
328        self.pointer = pointer            # None, '*' or '**'
329        self.secondPointerIsConst = secondPointerIsConst
330        self.arraySize = arraySize
331        self.len = len
332
333        # check if type should be swaped
334        substituteType(self)
335
336class Function:
337    TYPE_PLATFORM = 0 # Not bound to anything
338    TYPE_INSTANCE = 1 # Bound to VkInstance
339    TYPE_DEVICE = 2   # Bound to VkDevice
340
341    def __init__ (self, name, returnType = None, arguments = None):
342        self.name = name
343        self.aliasList = []
344        self.queuesList = []
345        self.returnType = returnType
346        self.arguments = arguments                # list of FunctionArgument objects
347        self.functionType = Function.TYPE_PLATFORM
348
349        # Determine function type based on first argument but use TYPE_PLATFORM for vkGetInstanceProcAddr
350        if self.name == "vkGetInstanceProcAddr":
351            return
352        assert len(self.arguments) > 0
353        firstArgType = self.arguments[0].type
354        if firstArgType in ["VkInstance", "VkPhysicalDevice"]:
355            self.functionType = Function.TYPE_INSTANCE
356        elif firstArgType in ["VkDevice", "VkCommandBuffer", "VkQueue"]:
357            self.functionType = Function.TYPE_DEVICE
358
359    def getType (self):
360        return self.functionType
361
362class FeatureEnumerator:
363    def __init__ (self, name, extends, offset, extnumber):
364        self.name = name
365        self.extends = extends
366        self.offset = offset
367        self.extnumber = extnumber
368
369class FeatureRequirement:
370    def __init__ (self, operation, comment, enumList, typeList, commandList, featureList):
371        self.operation = operation      # "require" or "remove"
372        self.comment = comment
373        self.enumList = enumList        # list of FeatureEnumerator objects
374        self.typeList = typeList        # list of strings, each representing required structure name
375        self.commandList = commandList  # list of strings, each representing required function name
376        self.features = featureList        # list of ExtensionFeature objects
377
378class Feature:
379    def __init__ (self, api, name, number, requirementsList):
380        self.api = api
381        self.name = name
382        self.number = number
383        self.requirementsList = requirementsList  # list of FeatureRequirement objects
384
385class ExtensionEnumerator:
386    def __init__ (self, name, extends, alias, value, extnumber, offset, bitpos, vdir, comment):
387        self.name = name
388        self.extends = extends
389        self.alias = alias
390        self.value = value
391        self.extnumber = extnumber
392        self.offset = offset
393        self.bitpos = bitpos
394        self.dir = vdir
395        self.comment = comment  # note: comment is used to mark not promoted features for partially promoted extensions
396
397class ExtensionCommand:
398    def __init__ (self, name, comment):
399        self.name = name
400        self.comment = comment
401
402class ExtensionType:
403    def __init__ (self, name, comment):
404        self.name = name
405        self.comment = comment
406
407class ExtensionFeature:
408    def __init__ (self, name, struct):
409        self.name = name
410        self.struct = struct
411
412class ExtensionRequirements:
413    def __init__ (self, depends, extendedEnums, newCommands, newTypes, featureList):
414        self.depends = depends                      # None when requirement apply to all implementations of extension or string with dependencies
415                                                    # string with extension name when requirements apply to implementations that also support given extension
416        self.extendedEnums = extendedEnums          # list of ExtensionEnumerator objects
417        self.newCommands = newCommands              # list of ExtensionCommand objects
418        self.newTypes = newTypes                    # list of ExtensionType objects
419        self.features = featureList                 # list of ExtensionFeature objects
420
421class Extension:
422    def __init__ (self, name, number, type, depends, platform, promotedto, partiallyPromoted, requirementsList, supportedList):
423        self.name = name                            # extension name
424        self.number = number                        # extension version
425        self.type = type                            # extension type - "device" or "instance"
426        self.depends = depends                      # string containig grammar for required core vulkan version and/or other extensions
427        self.platform = platform                    # None, "win32", "ios", "android" etc.
428        self.promotedto = promotedto                # vulkan version, other extension or None
429        self.partiallyPromoted = partiallyPromoted  # when True then some of requirements were not promoted
430        self.requirementsList = requirementsList    # list of ExtensionRequirements objects
431        self.supported = supportedList              # list of supported APIs
432
433class API:
434    def __init__ (self, apiName):
435        self.apiName = apiName           # string "vulkan" or "vulkansc"
436        self.basetypes = {}              # dictionary, e.g. one of keys is VkFlags and its value is uint32_t
437        self.defines = []
438        self.handles = []                # list of Handle objects
439        self.bitmasks = []               # list of Bitmask objects
440        self.enums = []                  # list of Enum objects - each contains individual enum definition (including extension enums)
441        self.compositeTypes = []         # list of Composite objects - each contains individual structure/union definition (including extension structures)
442        self.functions = []              # list of Function objects - each contains individual command definition (including extension functions)
443        self.features = []               # list of Feature objects
444        self.extensions = []             # list of Extension objects - each contains individual, supported extension definition
445        self.notSupportedExtensions = [] # list of Extension objects - it contains NOT supported extensions; this is filled and needed only for SC
446        self.basicCTypes = []            # list of basic C types e.g. 'void', 'int8_t'
447        self.tempAliasesList = []        # list of aliases for enums that could not be added because enum is defined later than its alias; this is needed for SC
448
449        # read all files from extensions directory
450        additionalExtensionData = {}
451        for fileName in glob.glob(os.path.join(SCRIPTS_SRC_DIR, "extensions", "*.json")):
452            if "schema.json" in fileName:
453                continue
454            extensionName = os.path.basename(fileName)[:-5]
455            if extensionName in EXTENSIONS_TO_READ_FROM_XML_NOT_JSON:
456                continue
457            fileContent = readFile(fileName)
458            try:
459                additionalExtensionData[extensionName] = json.loads(fileContent)
460                with open(fileName, 'w') as file:
461                    file.write(json.dumps(additionalExtensionData[extensionName], indent=4))
462            except ValueError as err:
463                print("Error in %s: %s" % (os.path.basename(fileName), str(err)))
464                sys.exit(-1)
465        self.additionalExtensionData = sorted(additionalExtensionData.items(), key=lambda e: e[0])
466
467    def addEnumerator(self, targetEnum, name, value, offset, extnumber, bitpos, dir = None):
468        # calculate enumerator value if offset attribute is present
469        if value is None and offset is not None:
470            value = 1000000000 + (int(extnumber) - 1) * 1000 + int(offset)
471            # check if value should be negative
472            value = -value if dir == "-" else value
473            # convert to string so that type matches the type in which values
474            # are stored for enums that were read from enums xml section
475            value = str(value)
476        # add new enumerator
477        targetEnum.enumeratorList.append(Enumerator(name, value, bitpos))
478
479    def addAliasToEnumerator (self, targetEnum, name, alias):
480        assert(alias is not None)
481        for e in reversed(targetEnum.enumeratorList):
482            if alias == e.name or alias in e.aliasList:
483                # make sure same alias is not already on the list; this handles special case like
484                # VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR alais which is defined in three places
485                if name not in e.aliasList:
486                    e.aliasList.append(name)
487                return True
488        return False
489
490    def readEnum (self, enumsNode):
491        enumName = enumsNode.get("name")
492        # special case for vulkan hardcoded constants that are specified as enum in vk.xml
493        if enumName == "API Constants":
494            for enumItem in enumsNode:
495                self.defines.append(Define(
496                    enumItem.get("name"),
497                    enumItem.get("type"),
498                    enumItem.get("alias"),
499                    enumItem.get("value")
500                ))
501            return
502        # initial enum definition is read while processing types section;
503        # we need to find this enum definition and add data to it
504        enumDefinition = [enumDef for enumDef in self.enums if enumName == enumDef.name][0]
505        # add type and bitwidth to enum definition
506        enumDefinition.type = enumsNode.get("type")
507        enumDefinition.bitwidth = enumsNode.get("bitwidth")
508        if enumDefinition.bitwidth is None:
509            enumDefinition.bitwidth = "32"
510        # add components to enum definition
511        for enumeratorItem in enumsNode:
512            # skip comment tags
513            if enumeratorItem.tag != "enum":
514                continue
515            # when api attribute is present it limits param to specific api
516            supportedApi = enumeratorItem.get("api")
517            if supportedApi != None and supportedApi != self.apiName:
518                continue
519            name = enumeratorItem.get("name")
520            alias = enumeratorItem.get("alias")
521            if alias is None:
522                self.addEnumerator(
523                    enumDefinition,
524                    name,
525                    enumeratorItem.get("value"),
526                    enumeratorItem.get("offset"),
527                    enumeratorItem.get("extnumber"),
528                    enumeratorItem.get("bitpos"),
529                    enumeratorItem.get("dir"))
530            else:
531                self.addAliasToEnumerator(enumDefinition, name, alias)
532
533    def readCommand (self, commandNode):
534        # when api attribute is present it limits command just to specified api
535        dedicatedForApi = commandNode.get("api")
536        if dedicatedForApi is not None and dedicatedForApi != self.apiName:
537            # we dont need to parse this command
538            return
539        # check if this is alias
540        alias = commandNode.get("alias")
541        # if node is alias then use the fact that alias definition follows aliased structure
542        if alias is not None:
543            # aliased command has usually been added recently, so we iterate in reverse order
544            found = False
545            for f in reversed(self.functions):
546                found = (f.name == alias)
547                if found:
548                    f.aliasList.append(commandNode.get("name"))
549                    break
550            assert found
551            # go to next node
552            return
553        # memorize all parameters
554        functionParams = []
555        queuesList = []
556
557        protoNode = None
558        for paramNode in commandNode:
559            # memorize prototype node
560            if paramNode.tag == "proto":
561                protoNode = paramNode
562                continue
563            # skip implicitexternsyncparams
564            if paramNode.tag != "param":
565                continue
566            # when api attribute is present it limits param to specific api
567            supportedApi = paramNode.get("api")
568            if supportedApi != None and supportedApi != self.apiName:
569                continue
570            nameNode = paramNode.find("name")
571            typeNode = paramNode.find("type")
572            starCount = typeNode.tail.count('*')
573            lenAttr = paramNode.get("len")
574            functionParams.append(FunctionArgument(
575                nameNode.text,
576                paramNode.text,
577                paramNode.find("type").text,
578                '*' * starCount if starCount > 0 else None,
579                'const' in typeNode.tail,
580                nameNode.tail,
581                lenAttr
582            ))
583
584        queuesAttr = commandNode.get("queues")
585        if queuesAttr:
586            queuesList = queuesAttr.split(",")
587
588        # memorize whole function
589        func = Function(
590            protoNode.find("name").text,
591            protoNode.find("type").text,
592            functionParams,
593        )
594
595        func.queuesList = queuesList
596        self.functions.append(func)
597
598    def readExtension (self, extensionNode):
599        # check to which list this extension should be added
600        supportedList = extensionNode.get("supported")
601        isExtensionSupported = self.apiName in supportedList.split(',')
602        promotedto = extensionNode.get("promotedto")
603        # read extension definition to proper list
604        extensionName = extensionNode.get("name")
605        extensionNumber = extensionNode.get("number")
606        if promotedto is not None and 'VK_VERSION' in promotedto:
607            p = promotedto
608            major = int(p[-3])
609            minor = int(p[-1])
610            # if self.apiName == "vulkansc" and "vulkan" in supportedList and major == 1 and minor <= 2 and extensionName in EXTENSIONS_TO_READ_FROM_XML_NOT_JSON:
611            #     isExtensionSupported = True
612        targetExtensionList = self.extensions if isExtensionSupported else self.notSupportedExtensions
613        partiallyPromoted = False
614        # before reading extension data first read extension
615        # requirements by iterating over all require tags
616        requirementsList = []
617        for requireItem in extensionNode.findall('require'):
618            extendedEnums = []
619            newCommands = []
620            newTypes = []
621            featureList = []
622            # iterate over all children in current require tag
623            # and add them to proper list
624            for individualRequirement in requireItem:
625                requirementName = individualRequirement.get("name")
626                requirementComment = individualRequirement.get("comment")
627                # check if this requirement was not promoted and mark
628                # this extension as not fully promoted
629                if requirementComment is not None and "Not promoted to" in requirementComment:
630                    partiallyPromoted = True
631                # check if this requirement describes enum, command or type
632                if individualRequirement.tag == "enum":
633                    # when api attribute is present it limits enumerator to specific api
634                    supportedApi = individualRequirement.get("api")
635                    if supportedApi != None and supportedApi != self.apiName:
636                        continue
637                    extendedEnumName = individualRequirement.get("extends")
638                    extendedEnums.append(ExtensionEnumerator(
639                        requirementName,
640                        extendedEnumName,
641                        individualRequirement.get("alias"),
642                        individualRequirement.get("value"),
643                        individualRequirement.get("extnumber"),
644                        individualRequirement.get("offset"),
645                        individualRequirement.get("bitpos"),
646                        individualRequirement.get("dir"),
647                        requirementComment))
648                elif individualRequirement.tag == "command":
649                    newCommands.append(ExtensionCommand(requirementName, requirementComment))
650                elif individualRequirement.tag == "type":
651                    newTypes.append(ExtensionType(requirementName, requirementComment))
652                elif individualRequirement.tag == "feature":
653                    featureList.append(ExtensionFeature(requirementName, individualRequirement.get("struct")))
654                elif individualRequirement.tag == "comment" and "not promoted to" in individualRequirement.text:
655                    # partial promotion of VK_EXT_ycbcr_2plane_444_formats and VK_EXT_4444_formats
656                    # is marked with comment tag in first require section
657                    partiallyPromoted = True
658            # construct requirement object and add it to the list
659            requirementsList.append(ExtensionRequirements(
660                requireItem.get("depends"),  # dependencies that can include "and/or" grammar
661                extendedEnums,               # extendedEnums
662                newCommands,                 # newCommands
663                newTypes,                    # newTypes
664                featureList                     # features
665            ))
666
667        supportedList = extensionNode.get("supported").split(",")
668        # add extension definition to proper api object
669        targetExtensionList.append(Extension(
670            extensionName,                   # name
671            extensionNumber,                 # number
672            extensionNode.get("type"),       # type
673            extensionNode.get("depends"),    # depends
674            extensionNode.get("platform"),   # platform
675            extensionNode.get("promotedto"), # promotedto
676            partiallyPromoted,               # partiallyPromoted
677            requirementsList,                # requirementsList
678            supportedList                    # supportedList
679        ))
680
681    def readFeature (self, featureNode):
682        # api attribute limits feature just to specified api
683        supportedApis = featureNode.get("api")
684        isFeatureSupportedByApi = self.apiName in supportedApis.split(',')
685        requirementsList = []
686        for requirementGroup in featureNode:
687            enumList = []
688            typeList = []
689            featureList = []
690            commandList = []
691            for requirement in requirementGroup:
692                requirementName = requirement.get("name")
693                if requirement.tag == "enum":
694                    extendedEnumName = requirement.get("extends")
695                    offset = requirement.get("offset")
696                    extnumber = requirement.get("extnumber")
697                    enumList.append(FeatureEnumerator(requirementName, extendedEnumName, offset, extnumber))
698                    if isFeatureSupportedByApi and extendedEnumName is not None:
699                        # find extended enum in api.enums list
700                        for e in self.enums:
701                            if extendedEnumName == e.name:
702                                # read enumerator and add it to enum
703                                alias = requirement.get("alias")
704                                if alias is None:
705                                    self.addEnumerator(
706                                        e,
707                                        requirementName,
708                                        requirement.get("value"),
709                                        offset,
710                                        extnumber,
711                                        requirement.get("bitpos"),
712                                        requirement.get("dir"))
713                                elif not self.addAliasToEnumerator(e, requirementName, alias):
714                                    self.tempAliasesList.append((e, requirementName, alias))
715                                break
716                elif requirement.tag == "type":
717                    typeList.append(requirementName)
718                elif requirement.tag == "feature":
719                    featureList.append(ExtensionFeature(requirementName, requirement.get("struct")))
720                elif requirement.tag == "command":
721                    commandList.append(requirementName)
722            requirementsList.append(FeatureRequirement(
723                requirementGroup.tag,
724                requirementGroup.get("comment"),
725                enumList,
726                typeList,
727                commandList,
728                featureList
729            ))
730        self.features.append(Feature(
731            supportedApis,
732            featureNode.get("name"),
733            featureNode.get("number"),
734            requirementsList
735        ))
736
737    def readType (self, typeNode):
738        name = typeNode.get("name")
739        alias = typeNode.get("alias")
740        category = typeNode.get("category")
741        if category == "enum":
742            if alias is None:
743                self.enums.append(Enum(name))
744            else:
745                for e in reversed(self.enums):
746                    if alias == e.name:
747                        e.alias = name
748                        break
749        elif category == "handle":
750            type = None
751            if alias is None:
752                name = typeNode.find("name").text
753                type = typeNode.find("type").text
754                self.handles.append(Handle(
755                    name,
756                    type,
757                    alias,
758                    typeNode.get("parent"),
759                    typeNode.get("objtypeenum"),
760                ))
761            else:
762                for h in reversed(self.handles):
763                    if alias == h.name:
764                        h.alias = name
765                        break
766        elif category == "basetype":
767            # processing only those basetypes that have type child
768            type = typeNode.find("type")
769            if type is not None:
770                self.basetypes[typeNode.find("name").text] = type.text
771        elif category == "bitmask":
772            # when api attribute is present it limits bitmask to specific api
773            supportedApi = typeNode.get("api")
774            if supportedApi != None and supportedApi != self.apiName:
775                # go to next node
776                return
777            # if node is alias then use the fact that alias definition follows aliased bitmasks;
778            # in majoriti of cases it follows directly aliased bitmasks but in some cases there
779            # is a unrelated bitmasks definition in between - to handle this traverse in reverse order
780            if alias is not None:
781                for bm in reversed(self.bitmasks):
782                    if alias == bm.name:
783                        bm.alias = name
784                        break
785            else:
786                self.bitmasks.append(Bitmask(
787                    typeNode.find("name").text,
788                    typeNode.find("type").text,
789                    typeNode.get("requires"),
790                    typeNode.get("bitvalues")
791                ))
792        elif category in ["struct", "union"]:
793            # if node is alias then use the fact that alias definition follows aliased structure;
794            # in majoriti of cases it follows directly aliased structure but in some cases there
795            # is a unrelated structure definition in between - to handle this traverse in reverse order
796            if alias is not None:
797                for ct in reversed(self.compositeTypes):
798                    if alias == ct.name or alias in ct.aliasList:
799                        ct.aliasList.append(name)
800                        break
801                # go to next node
802                return
803            # read structure members
804            structMembers = []
805            for memberNode in typeNode:
806                if memberNode.tag != "member":
807                    continue
808                # when api attribute is present it limits bitmask to specific api
809                supportedApi = memberNode.get("api")
810                if supportedApi != None and supportedApi != self.apiName:
811                    # go to next member
812                    continue
813                # handle enum nodes that can be used for array dimensions
814                arraySizeList = []
815                for node in memberNode:
816                    if node.tag == "enum":
817                        arraySizeList.append(node.text)
818                        # check if there are array dimension that are not enums
819                        if '[' in node.tail and len(node.tail) > 2:
820                            arraySizeList += node.tail.replace(']', ' ').replace('[', ' ').split()
821                # handle additional text after name tag; it can represent array
822                # size like in VkPipelineFragmentShadingRateEnumStateCreateInfoNV
823                # or number of bits like in VkAccelerationStructureInstanceKHR
824                nameNode = memberNode.find("name")
825                nameTail = nameNode.tail
826                fieldWidth = None
827                if nameTail:
828                    if ':' in nameTail:
829                        fieldWidth = nameTail.replace(':', '').replace(' ', '')
830                    elif '[' in nameTail and ']' in nameTail:
831                        nameTail = nameTail.replace(']', ' ').replace('[', ' ')
832                        arraySizeList = nameTail.split() + arraySizeList
833                # handle additional text after type tag; it can represent pointers like *pNext
834                memberTypeNode = memberNode.find("type")
835                pointer = memberTypeNode.tail.strip() if memberTypeNode.tail is not None else None
836                structMembers.append(CompositeMember(
837                    nameNode.text,               # name
838                    memberTypeNode.text,         # type
839                    pointer,                     # pointer
840                    memberNode.text,             # qualifiers
841                    arraySizeList,               # arraySizeList
842                    memberNode.get("optional"),  # optional
843                    memberNode.get("limittype"), # limittype
844                    memberNode.get("values"),    # values
845                    fieldWidth                   # fieldWidth
846                ))
847            # create structure definition
848            self.compositeTypes.append(Composite(
849                name,
850                category,
851                typeNode.get("allowduplicate"),
852                typeNode.get("structextends"),
853                typeNode.get("returnedonly"),
854                structMembers
855            ))
856        elif category == "define":
857            nNode = typeNode.find("name")
858            tNode = typeNode.find("type")
859            if nNode == None or tNode == None:
860                return
861            requires = typeNode.get("requires")
862            name = nNode.text
863            if "API_VERSION_" in name or requires == "VK_MAKE_VIDEO_STD_VERSION":
864                value = tNode.tail
865                value = tNode.text + value[:value.find(')')+1]
866                value = value.replace('VKSC_API_VARIANT', '1')
867                self.defines.append(Define(
868                    name,
869                    "uint32_t",
870                    None,
871                    value
872                ))
873        else:
874            requires = typeNode.get("requires")
875            if requires == 'vk_platform':
876                self.basicCTypes.append(name)
877
878    def build (self, rawVkXml):
879        # iterate over all *.xml root children
880        for rootChild in rawVkXml.getroot():
881
882            # each enum is defined in separate enums node directly under root node
883            if rootChild.tag == "enums":
884                self.readEnum(rootChild)
885
886            # read function definitions
887            if rootChild.tag == "commands":
888                commandsNode = rootChild
889                for commandItem in commandsNode:
890                    self.readCommand(commandItem)
891
892            # read vulkan versions
893            if rootChild.tag == "feature":
894                self.readFeature(rootChild)
895
896            # read extensions
897            if rootChild.tag == "extensions":
898                extensionsNode = rootChild
899                for extensionItem in extensionsNode:
900                    self.readExtension(extensionItem)
901
902            # "types" is a first child of root so it's optimal to check for it
903            # last and don't repeat this check for all other iterations
904            if rootChild.tag == "types":
905                typesNode = rootChild
906                for typeItem in typesNode:
907                    self.readType(typeItem)
908
909    def postProcess (self):
910
911        # verify that promotedto extensions are supported by the api
912        for ext in self.extensions:
913            if ext.promotedto is not None and "VK_VERSION" not in ext.promotedto:
914                if not any(x.name == ext.promotedto for x in self.extensions):
915                    ext.promotedto = None
916
917        if self.apiName == "vulkansc":
918            # temporary workaround for extensions that are marked only for vulkan api in xml while
919            # they are need by vulkan_json_data.hpp and vulkan_json_parser.hpp in vulkansc
920            workAroundList = [
921                    "VK_NV_device_diagnostic_checkpoints",
922                    "VK_KHR_format_feature_flags2",
923                    "VK_EXT_vertex_attribute_divisor",
924                    "VK_EXT_global_priority",
925                    "VK_EXT_calibrated_timestamps",
926            ]
927            for extName in workAroundList:
928                extData = [e for e in self.notSupportedExtensions if e.name == extName]
929                if len(extData):
930                    extData = extData[0]
931                    self.extensions.append(extData)
932                    self.notSupportedExtensions.remove(extData)
933            # temporary workaround for enums needed by vulkan_json_parser.hpp that are marked only for vulkan api in xml
934            scFeatures = [f for f in self.features if f.api == "vulkansc"][0]
935            for enum in self.enums:
936                if enum.name == "VkPipelineLayoutCreateFlagBits":
937                    self.addEnumerator(enum, "VK_PIPELINE_LAYOUT_CREATE_RESERVED_0_BIT_AMD", None, None, None, 0, None)
938                    self.addEnumerator(enum, "VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT", None, None, None, 1, None)
939                    scFeatures.requirementsList[0].typeList.append(enum.name)
940                    break
941        # add new enumerators that were added by extensions to api.enums
942        # we have to do it at the end for SC because some enums are dependent from extensions/api versions
943        # and those dependencies can be checked only after all extensions were read
944        for ext in self.extensions:
945            logging.debug("Considering extension %s for API %s" % (ext.name, apiName))
946            for requirement in ext.requirementsList:
947                # check if this requirement is supported by current implementation
948                isRequirementSupported = isDependencyMet(requirement.depends, self.extensions)
949                # add enumerator to proper enum from api.enums
950                if isRequirementSupported:
951                    for enumerator in requirement.extendedEnums:
952                        if enumerator.extends is None:
953                            continue
954                        # find enum in api.enums
955                        matchedEnums = [enum for enum in self.enums if enumerator.extends == enum.name or enumerator.extends == enum.alias]
956                        if len(matchedEnums) == 0:
957                            logging.error("Could not find enum %s extends %s in %s " % (enumerator.name, enumerator.extends, self.apiName))
958                        matchedEnum = matchedEnums[0]
959                        # add enumerator only when it is not already in enum
960                        if len([e for e in matchedEnum.enumeratorList if e.name == enumerator.name]) == 0:
961                            if enumerator.alias == None:
962                                logging.debug("Adding enum value %s for extension %s in API %s" % (enumerator.name, ext.name, apiName))
963                                self.addEnumerator(
964                                        matchedEnum,
965                                        enumerator.name,
966                                        enumerator.value,
967                                        enumerator.offset,
968                                        enumerator.extnumber if enumerator.extnumber else ext.number,
969                                        enumerator.bitpos,
970                                        enumerator.dir)
971                            elif not self.addAliasToEnumerator(matchedEnum, enumerator.name, enumerator.alias):
972                                # we might not be able to add alias as we might be missing what we are aliasing
973                                # this will happen when aliased enum is added later then definition of alias
974                                logging.debug("Adding alias %s to enum %s for extension %s in API %s" % (enumerator.name, matchedEnum.name, ext.name, apiName))
975                                self.tempAliasesList.append((matchedEnum, enumerator.name, enumerator.alias))
976                else:
977                    logging.warning("Skipping requirement in extension %s because dependencies are not met: %s" % (ext.name, requirement.depends))
978
979        # add aliases to enumerators that were defined after alias definition
980        for enum, name, alias in self.tempAliasesList:
981            if not self.addAliasToEnumerator(enum, name, alias):
982                # if enumerator that should be aliased was not found then try to insert it without alias
983                # (this happens for vulkansc as in xml enumerator might be defined in extension that is
984                # not supported by sc or in normal vulkan version)
985                def tryToFindEnumValueInNotSupportedExtensions(searchedName):
986                    for nsExt in self.notSupportedExtensions:
987                        for r in nsExt.requirementsList:
988                            for enumerator in r.extendedEnums:
989                                if enumerator.name == searchedName:
990                                    self.addEnumerator(
991                                        enum,
992                                        name,
993                                        enumerator.value,
994                                        enumerator.offset,
995                                        enumerator.extnumber if enumerator.extnumber else ext.number,
996                                        enumerator.bitpos,
997                                        enumerator.dir)
998                                    # there is still 1 case where alias that is not part of SC needs to be added for SC
999                                    self.addAliasToEnumerator(enum, alias, name)
1000                                    return True
1001                    return False
1002                def tryToFindEnumValueInNotSupportedVersions(searchedName):
1003                    for f in self.features:
1004                        # check only not supported features
1005                        if self.apiName in f.api.split(','):
1006                            continue
1007                        for r in f.requirementsList:
1008                            for enumerator in r.enumList:
1009                                if enumerator.name == searchedName:
1010                                    assert enumerator.extnumber is not None
1011                                    self.addEnumerator(
1012                                        enum,
1013                                        name,
1014                                        None,
1015                                        enumerator.offset,
1016                                        enumerator.extnumber,
1017                                        None,
1018                                        None)
1019                                    self.addAliasToEnumerator(enum, alias, name)
1020                                    return True
1021                    return False
1022                # using functions for fast stack unwinding
1023                if tryToFindEnumValueInNotSupportedExtensions(alias):
1024                    continue
1025                if tryToFindEnumValueInNotSupportedVersions(alias):
1026                    continue
1027        self.tempAliasesList = None
1028
1029        if self.apiName == "vulkan":
1030            def removeExtensionFromApi(extName, structureNameList, commandNameList):
1031                extObjectList = [e for e in api.extensions if e.name == extName]
1032                if len(extObjectList) > 0:
1033                    api.extensions.remove(extObjectList[0])
1034                structObjectList = [ct for ct in api.compositeTypes if ct.name in structureNameList]
1035                for s in structObjectList:
1036                    api.compositeTypes.remove(s)
1037                commandObjectList = [f for f in api.functions if f.name in commandNameList]
1038                for f in commandObjectList:
1039                    api.functions.remove(f)
1040
1041            # remove structures and commands added by VK_EXT_directfb_surface extension
1042            removeExtensionFromApi("VK_EXT_directfb_surface",
1043                                   ["VkDirectFBSurfaceCreateFlagsEXT", "VkDirectFBSurfaceCreateInfoEXT"],
1044                                   ["vkCreateDirectFBSurfaceEXT", "vkGetPhysicalDeviceDirectFBPresentationSupportEXT"])
1045
1046            # remove structures and commands added by disabled VK_ANDROID_native_buffer extension;
1047            # disabled extensions aren't read but their structures and commands will be in types and commands sections in vk.xml
1048            removeExtensionFromApi("VK_ANDROID_native_buffer",
1049                                   ["VkNativeBufferANDROID", "VkSwapchainImageCreateInfoANDROID",
1050                                    "VkPhysicalDevicePresentationPropertiesANDROID", "VkNativeBufferUsage2ANDROID",
1051                                    "VkSwapchainImageUsageFlagBitsANDROID", "VkSwapchainImageUsageFlagsANDROID"],
1052                                   ["vkGetSwapchainGrallocUsageANDROID", "vkAcquireImageANDROID",
1053                                    "vkQueueSignalReleaseImageANDROID", "vkGetSwapchainGrallocUsage2ANDROID"])
1054
1055            # remove empty enums e.g. VkQueryPoolCreateFlagBits, VkDeviceCreateFlagBits
1056            enumsToRemove = [enum for enum in self.enums if len(enum.enumeratorList) == 0]
1057            for er in enumsToRemove:
1058                self.enums.remove(er)
1059
1060            # add alias for VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR (in vk.xml for this struct alias is defined before struct
1061            # where in all other cases it is defined after structure definition)
1062            barycentricFeaturesStruct = [c for c in api.compositeTypes if c.name == 'VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR'][0]
1063            barycentricFeaturesStruct.aliasList.append('VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV')
1064
1065        elif self.apiName == "vulkansc":
1066            # remove commands that are marked with <remove> tag in SC feature specification;
1067            # e.g. there is no vkCreateShaderModule in SC
1068            functionsToRemove = []
1069            scFeatures = [f for f in self.features if f.api == "vulkansc"][0]
1070            for featureRequirement in scFeatures.requirementsList:
1071                if featureRequirement.operation == "remove":
1072                    # find function in the list of all functions
1073                    for fun in self.functions:
1074                        if fun.name in featureRequirement.commandList:
1075                            functionsToRemove.append(fun)
1076            for fun in functionsToRemove:
1077                self.functions.remove(fun)
1078
1079            # sc is based on vk1.2 so we need to check features of vk1.3+
1080            # and rename functions and structures that were promoted in
1081            # those versions to their previous names (aliases)
1082            renamedStructuresDict = {}
1083            for feature in self.features:
1084                # skip vk versions smaller than 1.3
1085                if int(feature.number[-1]) < 3:
1086                    continue
1087                # iterate over all requirements and enums/commands/structs added in them
1088                for featureRequirement in feature.requirementsList:
1089                    renamedFunctionsList = []
1090                    # find promotedFun in list of all functions
1091                    for fun in self.functions:
1092                        if fun.name not in featureRequirement.commandList:
1093                            continue
1094                        # replace function name with its last alias
1095                        fun.name = fun.aliasList[-1]
1096                        fun.aliasList = fun.aliasList[:-1]
1097                        # memorize renamed functions
1098                        renamedFunctionsList.append(fun)
1099                    # skip renaming enums and structures for extensions that are available for SC
1100                    if featureRequirement.comment is not None:
1101                        matchedExtension = re.search(r'Promoted from (\w+) ', featureRequirement.comment, re.IGNORECASE)
1102                        if matchedExtension is not None:
1103                            promotedExtensionName = matchedExtension.group(1)
1104                            extensionList = [e for e in self.extensions if e.name == promotedExtensionName]
1105                            if len(extensionList) > 0:
1106                                continue
1107                    for promotedEnumerator in featureRequirement.enumList:
1108                        # iterate over all enums and find one that was extended
1109                        for enum in self.enums:
1110                            logging.debug("Considering enum %s for API %s" % (enum.name, api.apiName))
1111                            if enum.name != promotedEnumerator.extends:
1112                                continue
1113                            enumeratorReplaced = False
1114                            # find enumerator that should have changed name
1115                            for enumerator in enum.enumeratorList:
1116                                if enumerator.name != promotedEnumerator.name or len(enumerator.aliasList) == 0:
1117                                    continue
1118                                # replace enumerator name with its first alias
1119                                enumerator.name = enumerator.aliasList[0]
1120                                enumerator.aliasList = enumerator.aliasList[1:]
1121                                # first member of almost all structures is VkStructureType and in xml that member
1122                                # has defined value - we need to change those values to versions supported by SC
1123                                if "STRUCTURE_TYPE" in enumerator.name:
1124                                    for struct in self.compositeTypes:
1125                                        if struct.members[0].values == promotedEnumerator.name:
1126                                            struct.members[0].values = enumerator.name
1127                                            break
1128                                enumeratorReplaced = True
1129                                break
1130                            if enumeratorReplaced:
1131                                break
1132                    structsToRemove = []
1133                    # find promotedStruct in list of all structures
1134                    for struct in self.compositeTypes:
1135                        promotedStruct = struct.name
1136                        if promotedStruct not in featureRequirement.typeList:
1137                            continue
1138                        # skip structures without alias
1139                        if len(struct.aliasList) == 0:
1140                            # remove VkPhysicalDeviceVulkan13Features/Properties
1141                            if "VkPhysicalDeviceVulkan" in promotedStruct:
1142                                structsToRemove.append(struct)
1143                            continue
1144                        # replace struct name with its last alias
1145                        struct.notSupportedAlias = struct.name
1146                        struct.name = struct.aliasList[-1]
1147                        struct.aliasList = struct.aliasList[:-1]
1148                        # memorize all renamed structures
1149                        renamedStructuresDict[promotedStruct] = struct
1150                        # check all renamed functions and make sure that argument types are also renamed
1151                        for renamedFun in renamedFunctionsList:
1152                            for arg in renamedFun.arguments:
1153                                if arg.type == promotedStruct:
1154                                    arg.type = struct.name
1155                    # remove structures that were marked for removal
1156                    for st in structsToRemove:
1157                        self.compositeTypes.remove(st)
1158
1159            # iterate over all renamed structures and make sure that all their attributes are also renamed
1160            for newName in renamedStructuresDict:
1161                for member in renamedStructuresDict[newName].members:
1162                    if member.type in renamedStructuresDict:
1163                        member.type = renamedStructuresDict[member.type].name
1164
1165        # remove enums that are not part of any vulkan version nor extension
1166        # (SC specific enums are in vk.xml without any attribute identifying that they are SC specific; same for enums for disabled extensions)
1167        def isEnumUsed(enumName, enumAlias):
1168            for feature in self.features:
1169                if self.apiName not in feature.api.split(','):
1170                    continue
1171                for requirement in feature.requirementsList:
1172                    for typeName in requirement.typeList:
1173                        if (typeName == enumName) or (typeName == enumAlias):
1174                            return True
1175            for ext in self.extensions:
1176                for requirement in ext.requirementsList:
1177                    for newType in requirement.newTypes:
1178                        if (newType.name == enumName) or (newType.name == enumAlias):
1179                            return True
1180                    for extendedEnum in requirement.extendedEnums:
1181                        if extendedEnum.extends == enumName:
1182                            return True
1183            return False
1184        # do removal using above function
1185        enumsToRemove = []
1186        for enum in self.enums:
1187            if isEnumUsed(enum.name, enum.alias):
1188                continue
1189            enumsToRemove.append(enum)
1190        for er in enumsToRemove:
1191            logging.debug("Removing enum %s because not used in API %s" % (er.name, self.apiName))
1192            self.enums.remove(er)
1193
1194        # remove structures that are not part of any vulkan version nor extension; SC specific
1195        # structures are in vk.xml without any attribute identifying that they are SC specific
1196        def isStructUsed(structNameList):
1197            for feature in self.features:
1198                if self.apiName not in feature.api.split(','):
1199                    continue
1200                for requirement in feature.requirementsList:
1201                    for typeName in requirement.typeList:
1202                        if typeName in structNameList:
1203                            return True
1204            for ext in self.extensions:
1205                for requirement in ext.requirementsList:
1206                    for newType in requirement.newTypes:
1207                        if newType.name in structNameList:
1208                            return isDependencyMet(requirement.depends, self.extensions)
1209            return False
1210
1211        structsToRemove = []
1212        for struct in self.compositeTypes:
1213            structNameList = [struct.name] + struct.aliasList
1214            if isStructUsed(structNameList):
1215                continue
1216            structsToRemove.append(struct)
1217        for st in structsToRemove:
1218            self.compositeTypes.remove(st)
1219
1220        # remove commands that are not part of any vulkan version nor extension
1221        # (SC specific commands are in vk.xml without any attribute identifying that they are SC specific)
1222        def isFunctionUsed(functionNameList):
1223            for feature in self.features:
1224                if self.apiName not in feature.api.split(','):
1225                    continue
1226                for requirement in feature.requirementsList:
1227                    for commandName in requirement.commandList:
1228                        if commandName in functionNameList:
1229                            return True
1230            for ext in self.extensions:
1231                for requirement in ext.requirementsList:
1232                    for newCommand in requirement.newCommands:
1233                        if newCommand.name in functionNameList:
1234                            return isDependencyMet(requirement.depends, self.extensions)
1235            return False
1236
1237        functionsToRemove = []
1238        for fun in self.functions:
1239            functionNameList = [fun.name] + fun.aliasList
1240            if isFunctionUsed(functionNameList):
1241                continue
1242            functionsToRemove.append(fun)
1243        for fun in functionsToRemove:
1244            logging.debug("Removing function %s because not used in API %s" % (fun.name, self.apiName))
1245            self.functions.remove(fun)
1246
1247        # remove handles that are not part of any vulkan command or structure
1248        def isHandleUsed(structList, functionList, handleName):
1249            for struct in structList:
1250                for member in struct.members:
1251                    if handleName in member.type:
1252                        return True
1253            for fun in functionList:
1254                for arg in fun.arguments:
1255                    if handleName in arg.type:
1256                        return True
1257            return False
1258
1259        handlesToRemove = []
1260        for h in self.handles:
1261            if isHandleUsed(self.compositeTypes, self.functions, h.name):
1262                continue
1263            handlesToRemove.append(h)
1264        for h in handlesToRemove:
1265            logging.debug("Removing unused handle %s from API %s" % (h.name, self.apiName))
1266            self.handles.remove(h)
1267
1268        # sort enumerators in enums
1269        sortLambda = lambda enumerator: int(enumerator.bitpos) if enumerator.value is None else int(enumerator.value, 16 if 'x' in enumerator.value else 10)
1270        for enum in self.enums:
1271            # skip enums that have no items or  just one in enumeratorList (e.g. VkQueryPoolCreateFlagBits)
1272            if len(enum.enumeratorList) < 2:
1273                continue
1274            # construct list of enumerators in which value and bitpos are not None
1275            enumeratorsToSort = [e for e in enum.enumeratorList if e.value != e.bitpos]
1276            # construct list of enumerators in which value and bitpos are equal to None
1277            remainingEnumerators = [e for e in enum.enumeratorList if e.value == e.bitpos]
1278            # construct sorted enumerator list with aliases at the end
1279            enum.enumeratorList = sorted(enumeratorsToSort, key=sortLambda)
1280            enum.enumeratorList.extend(remainingEnumerators)
1281
1282        # Fill in extension data that comes from the XML
1283        additionalExtensionNames = [item[0] for item in self.additionalExtensionData]
1284        for ext in self.extensions:
1285            if ext.name not in EXTENSIONS_TO_READ_FROM_XML_NOT_JSON:
1286                continue
1287            if ext.name in additionalExtensionNames:
1288                logging.error("Extension %s already defined as JSON!" % (ext.name))
1289            mandatoryFeatures = {}
1290            core = ""
1291            mandatory_variants = ext.supported
1292            if ext.promotedto is not None and 'VK_VERSION' in ext.promotedto:
1293                p = ext.promotedto
1294                major = int(p[-3])
1295                minor = int(p[-1])
1296                core = f'0.{major}.{minor}.0'
1297                if "vulkan" in mandatory_variants and major == 1 and minor <= 2:
1298                    mandatory_variants = []
1299            for requirement in ext.requirementsList:
1300                featureStructName = None
1301                featureStruct = None
1302                for feature in requirement.features:
1303                    newFeatureStructName = feature.struct
1304                    for ct in self.compositeTypes:
1305                        if newFeatureStructName in ct.aliasList:
1306                            newFeatureStructName = ct.name
1307                    if newFeatureStructName not in mandatoryFeatures.keys():
1308                        mandatoryFeatures[newFeatureStructName] = []
1309                    if newFeatureStructName != featureStructName:
1310                        featureStructName = newFeatureStructName
1311                        featureStruct = {'features': [], 'requirements': [], 'mandatory_variant': []}
1312                        mandatoryFeatures[featureStructName].append(featureStruct)
1313                    featureStruct['features'].append(feature.name)
1314                    featureStruct['requirements'].append(ext.name)
1315                    if requirement.depends is not None:
1316                        featureStruct['requirements'].append(requirement.depends)
1317                    if len(mandatory_variants) > 0:
1318                        featureStruct["mandatory_variant"] = mandatory_variants
1319
1320                #
1321                # for reqtype in requirement.newTypes:
1322                #     for ct in api.compositeTypes:
1323                #         if reqtype.name == ct.name or reqtype in ct.aliasList:
1324                #             featureStructName = ct.name
1325                #             featureStruct = {'features': [], 'requirements': [], 'mandatory_variant': []}
1326                #             for m in ct.members:
1327                #                 if m.name not in ["sType", "pNext"]:
1328                #                     featureStruct['features'].append(m.name)
1329                #                     featureStruct['requirements'].append(ext.name)
1330                #             if requirement.depends is not None:
1331                #                 featureStruct['requirements'].append(requirement.depends)
1332                #             if len(mandatory_variants) > 0:
1333                #                 featureStruct["mandatory_variant"] = mandatory_variants
1334                #             featureStruct['features'] = list(dict.fromkeys(featureStruct['features']))
1335                #             featureStruct['requirements'] = list(dict.fromkeys(featureStruct['requirements']))
1336                #             featureStruct['mandatory_variant'] = list(dict.fromkeys(featureStruct['mandatory_variant']))
1337                #             if len(featureStruct['mandatory_variant']) == 0:
1338                #                 featureStruct.pop('mandatory_variant')
1339                #             if featureStructName not in mandatoryFeatures.keys():
1340                #                 mandatoryFeatures[featureStructName] = []
1341                #             mandatoryFeatures[featureStructName].append(featureStruct)
1342            for featureStructName in mandatoryFeatures.keys():
1343                for featureStruct in mandatoryFeatures[featureStructName]:
1344                    featureStruct['features'] = list(dict.fromkeys(featureStruct['features']))
1345                    featureStruct['requirements'] = list(dict.fromkeys(featureStruct['requirements']))
1346                    featureStruct['mandatory_variant'] = list(dict.fromkeys(featureStruct['mandatory_variant']))
1347                    if len(featureStruct['mandatory_variant']) == 0:
1348                        featureStruct.pop('mandatory_variant')
1349            data = {}
1350            if ext.name.startswith("VK_KHR") or ext.name.startswith("VK_EXT"):
1351                data['register_extension'] = {'type': ext.type, 'core': core}
1352            if len(mandatoryFeatures) > 0:
1353                data['mandatory_features'] = mandatoryFeatures
1354
1355            jsonFilePath = os.path.join(SCRIPTS_SRC_DIR, "extensions", ext.name + ".json")
1356            with open(jsonFilePath, 'w') as file:
1357                printAttributesToFile(data, file, indent=4)
1358                logging.debug("File written to " + jsonFilePath)
1359            api.additionalExtensionData.append((ext.name, data))
1360
1361        # Here we do the API version requirements
1362        for apiFeature in self.features:
1363            if apiFeature.name not in EXTENSIONS_TO_READ_FROM_XML_NOT_JSON:
1364                continue
1365            if apiFeature.name in additionalExtensionNames:
1366                logging.error("API feature %s already defined as JSON!" % (ext.name))
1367            mandatoryFeatures = {}
1368            for requirement in apiFeature.requirementsList:
1369                featureStructName = None
1370                featureStruct = None
1371                for feature in requirement.features:
1372                    newFeatureStructName = feature.struct
1373                    for ct in self.compositeTypes:
1374                        if newFeatureStructName in ct.aliasList:
1375                            newFeatureStructName = ct.name
1376                    if newFeatureStructName not in mandatoryFeatures.keys():
1377                        mandatoryFeatures[newFeatureStructName] = []
1378                    if newFeatureStructName != featureStructName:
1379                        featureStructName = newFeatureStructName
1380                        featureStruct = {'features': [], 'requirements': []}
1381                        mandatoryFeatures[featureStructName].append(featureStruct)
1382                    featureStruct['features'].append(feature.name)
1383                    # if feature.name == "vulkanMemoryModel":
1384                    #     logging.debug("feature %s %s in %s" % (feature.name, featureStructName, apiFeature.name))
1385                    #     exit(-1)
1386                    dep = apiFeature.name
1387                    if 'VK_VERSION' in dep:
1388                        major = int(dep[-3])
1389                        minor = int(dep[-1])
1390                        featureStruct['requirements'].append(f"ApiVersion(0, {major}, {minor}, 0)")
1391                    else:
1392                        logging.error("requirement not valid in %s" % (apiFeature.name))
1393                        exit(-1)
1394                if featureStructName is not None:
1395                    featureStruct['features'] = list(dict.fromkeys(featureStruct['features']))
1396                    featureStruct['requirements'] = list(dict.fromkeys(featureStruct['requirements']))
1397            data = {'mandatory_features': mandatoryFeatures}
1398            jsonFilePath = os.path.join(SCRIPTS_SRC_DIR, "extensions", apiFeature.name + ".json")
1399            with open(jsonFilePath, 'w') as file:
1400                printAttributesToFile(data, file, indent=4)
1401                logging.debug("File written to " + jsonFilePath)
1402            api.additionalExtensionData.append((apiFeature.name, data))
1403
1404        self.additionalExtensionData = sorted(self.additionalExtensionData, key=lambda e: e[0])
1405
1406def prefixName (prefix, name):
1407    name = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', name[2:])
1408    name = re.sub(r'([a-zA-Z])([0-9])', r'\1_\2', name)
1409    name = name.upper()
1410    return prefix + name
1411
1412def parseInt (value):
1413    return int(value, 16 if ("0x" in value) else 10)
1414
1415def readFile (filename):
1416    with open(filename, 'rt') as f:
1417        return f.read()
1418
1419def getInterfaceName (functionName):
1420    assert functionName[:2] == "vk"
1421    return functionName[2].lower() + functionName[3:]
1422
1423def getFunctionTypeName (functionName):
1424    assert functionName[:2] == "vk"
1425    return functionName[2:] + "Func"
1426
1427def endsWith (str, postfix):
1428    return str[-len(postfix):] == postfix
1429
1430def writeHandleType (api, filename):
1431
1432    def getHandleName (name):
1433        return prefixName("HANDLE_TYPE_", name)
1434
1435    def genHandles ():
1436        yield "\t%s\t= 0," % getHandleName(api.handles[0].name)
1437        for h in api.handles[1:]:
1438            yield "\t%s," % getHandleName(h.name)
1439        for h in api.handles:
1440            if h.alias is not None:
1441                yield "\t%s\t= %s," % (getHandleName(h.alias), getHandleName(h.name))
1442        yield "\tHANDLE_TYPE_LAST\t= %s + 1" % (getHandleName(api.handles[-1].name))
1443
1444    def genHandlesBlock ():
1445        yield "enum HandleType"
1446        yield "{"
1447
1448        for line in indentLines(genHandles()):
1449            yield line
1450
1451        yield "};"
1452        yield ""
1453
1454    writeInlFile(filename, INL_HEADER, genHandlesBlock())
1455
1456def getEnumValuePrefixAndPostfix (enum):
1457    prefix = enum.name[0]
1458    for i in range(1, len(enum.name)):
1459        if enum.name[i].isupper() and not enum.name[i-1].isupper():
1460            prefix += "_"
1461        prefix += enum.name[i].upper()
1462    for p in EXTENSION_POSTFIXES:
1463        if prefix.endswith(p):
1464            return prefix[:-len(p)-1], '_'+p
1465    return prefix, ''
1466
1467def genEnumSrc (enum):
1468    yield "enum %s" % enum.name
1469    yield "{"
1470    lines = []
1471    for ed in enum.enumeratorList:
1472        if ed.value is not None:
1473            lines.append(f"\t{ed.name}\t= {ed.value},")
1474    for ed in enum.enumeratorList:
1475        for alias in ed.aliasList:
1476            lines.append(f"\t{alias}\t= {ed.name},")
1477
1478    # add *_LAST item when enum is linear
1479    prefix, postfix = getEnumValuePrefixAndPostfix(enum)
1480    if enum.areValuesLinear():
1481        lines.append(f"\t{prefix}{postfix}_LAST,")
1482
1483    # add _MAX_ENUM item with the ext postifix at the end
1484    lines.append(f"\t{prefix}_MAX_ENUM{postfix}\t= 0x7FFFFFFF")
1485
1486    for line in indentLines(lines):
1487        yield line
1488
1489    yield "};"
1490
1491def genBitfieldSrc (bitfield):
1492    lines = []
1493    for ev in bitfield.enumeratorList:
1494        # bitfields may use mix of bitpos and values
1495        if ev.bitpos is not None:
1496            value = pow(2, int(ev.bitpos))
1497            lines.append(f"\t{ev.name}\t= {value:#010x},")
1498        if ev.value is not None:
1499            lines.append(f"\t{ev.name}\t= {ev.value},")
1500    for ev in bitfield.enumeratorList:
1501        for alias in ev.aliasList:
1502            lines.append(f"\t{alias}\t= {ev.name},")
1503    # add _MAX_ENUM item
1504    prefix, postfix = getEnumValuePrefixAndPostfix(bitfield)
1505    lines.append(f"\t{prefix}_MAX_ENUM{postfix}\t= 0x7FFFFFFF")
1506    yield f"enum {bitfield.name}"
1507    yield "{"
1508    for line in indentLines(lines):
1509        yield line
1510    yield "};"
1511
1512def genBitfield64Src (bitfield64):
1513    def generateEntry(lines, bitfieldName, entryName, bitpos, value):
1514        if entryName is None:
1515            return
1516        # bitfields may use mix of bitpos and values
1517        if ev.bitpos is not None:
1518            v = pow(2, int(bitpos))
1519            lines.append(f"static const {bitfieldName} {entryName}\t= {v:#010x}ULL;")
1520        if value is not None:
1521            lines.append(f"static const {bitfieldName} {entryName}\t= {value}ULL;")
1522
1523    yield f"typedef uint64_t {bitfield64.name};"
1524    lines = []
1525    for ev in bitfield64.enumeratorList:
1526        generateEntry(lines, bitfield64.name, ev.name,  ev.bitpos, ev.value)
1527        for alias in ev.aliasList:
1528            generateEntry(lines, bitfield64.name, alias, ev.bitpos, ev.value)
1529    # write indented lines
1530    for line in indentLines(lines):
1531        yield line
1532    yield ""
1533
1534def genDefinesSrc (apiName, defines):
1535    def genLines (defines):
1536        for d in defines:
1537            if d.alias is not None:
1538                continue
1539            defineType = DEFINITIONS.get(d.name, d.type)
1540            yield f"#define {d.name}\t(static_cast<{defineType}>\t({d.value}))"
1541    for line in indentLines(genLines(defines)):
1542        yield line
1543    # add VK_API_MAX_FRAMEWORK_VERSION
1544    major, minor = 1, 0
1545    # In vk.xml, vulkan features (1.1, 1.2, 1.3) are marked as vulkan,vulkansc
1546    api_feature_name = "vulkan,vulkansc" if api.apiName == "vulkan" else api.apiName
1547    sorted_features = reversed(sorted(api.features, key=lambda feature: feature.number))
1548    for feature in sorted_features:
1549        if feature.api == api_feature_name:
1550            major, minor = feature.number.split('.')
1551            break
1552    logging.debug("Found max framework version for API '%s': %s.%s" % (api.apiName, major, minor))
1553    yield f"#define VK{apiName}_API_MAX_FRAMEWORK_VERSION\tVK{apiName}_API_VERSION_{major}_{minor}"
1554
1555def genHandlesSrc (handles):
1556    def genLines (handles):
1557        for h in handles:
1558            handleType = h.type
1559            handleObjtype = h.objtypeenum
1560            if h.alias is not None:
1561                # search for aliased handle
1562                for searchedHandle in handles:
1563                    if h.alias == searchedHandle.name:
1564                        handleType = searchedHandle.type
1565                        handleObjtype = searchedHandle.objtypeenum
1566                        break
1567            yield f"{handleType}\t({h.name},\tHANDLE{handleObjtype[9:]});"
1568    for line in indentLines(genLines(handles)):
1569        yield line
1570
1571def genHandlesSrc (handles):
1572    def genLines (handles):
1573        for h in handles:
1574            handleType    = h.type
1575            handleObjtype = h.objtypeenum
1576            line = f"{handleType}\t({{}},\tHANDLE{handleObjtype[9:]});"
1577            yield line.format(h.name)
1578            if h.alias is not None:
1579                yield line.format(h.alias)
1580
1581    for line in indentLines(genLines(handles)):
1582        yield line
1583
1584def writeBasicTypes (api, filename):
1585
1586    def gen ():
1587        yield "// Defines"
1588        for line in genDefinesSrc("SC" if api.apiName == "vulkansc" else "", api.defines):
1589            yield line
1590        yield ""
1591
1592        yield "// Handles"
1593        for line in genHandlesSrc(api.handles):
1594            yield line
1595        yield ""
1596
1597        yield "// Enums"
1598        for enum in api.enums:
1599            # skip empty enums only for vulkan
1600            # vulkan_json_data.hpp and vulkan_json_parser.hpp in SC need many empty enums
1601            if len(enum.enumeratorList) == 0 and api.apiName == "vulkan":
1602                continue
1603            if enum.type == "bitmask":
1604                if enum.bitwidth == "32":
1605                    for line in genBitfieldSrc(enum):
1606                        yield line
1607                else:
1608                    for line in genBitfield64Src(enum):
1609                        yield line
1610            else:
1611                for line in genEnumSrc(enum):
1612                    yield line
1613            if enum.alias is not None:
1614                yield f"typedef {enum.name} {enum.alias};"
1615            yield ""
1616
1617        yield "// Bitmasks"
1618        for bitmask in api.bitmasks:
1619            plainType = api.basetypes[bitmask.type]
1620            yield f"typedef {plainType} {bitmask.name};\n"
1621            if bitmask.alias:
1622                yield f"typedef {bitmask.name} {bitmask.alias};\n"
1623
1624        yield ""
1625        for line in indentLines(["VK_DEFINE_PLATFORM_TYPE(%s,\t%s)" % (s[0], c) for n, s, c in PLATFORM_TYPES]):
1626            yield line
1627        yield ""
1628
1629        yield "// Extensions"
1630        for ext in api.extensions:
1631            firstRequirementEnums = ext.requirementsList[0].extendedEnums
1632            for e in firstRequirementEnums:
1633                if e.extends is None and e.value is not None:
1634                    yield "#define " + e.name + " " + e.value
1635
1636    writeInlFile(filename, INL_HEADER, gen())
1637
1638def writeCompositeTypes (api, filename):
1639    # function that returns definition of structure member
1640    def memberAsString (member):
1641        result = ''
1642        if member.qualifiers:
1643            result += member.qualifiers
1644        result += member.type
1645        if member.pointer:
1646            result += member.pointer
1647        result += '\t' + member.name
1648        for size in member.arraySizeList:
1649            result += f"[{size}]"
1650        if member.fieldWidth:
1651            result += f":{member.fieldWidth}"
1652        return result
1653
1654    # function that prints single structure definition
1655    def genCompositeTypeSrc (type):
1656        structLines = "%s %s\n{\n" % (type.category, type.name)
1657        for line in indentLines(['\t'+memberAsString(m)+';' for m in type.members]):
1658            structLines += line + '\n'
1659        return structLines + "};\n"
1660
1661    # function that prints all structure definitions and alias typedefs
1662    def gen ():
1663        # structures in xml are not ordered in a correct way for C++
1664        # we need to save structures that are used in other structures first
1665        allStructureNamesList = [s.name for s in api.compositeTypes]
1666        commonTypesList = api.basicCTypes + ['VkStructureType']
1667        savedStructureNamesList = []
1668        delayedStructureObjectsList = []
1669
1670        # helper function that checks if all structure members were already saved
1671        def canStructBeSaved(compositeObject):
1672            for m in compositeObject.members:
1673                # check first commonTypesList to speed up the algorithm
1674                if m.type in commonTypesList:
1675                    continue
1676                # make sure that member is not of same type as compositeObject
1677                # (this hadles cases like VkBaseOutStructure)
1678                if m.type == compositeObject.name:
1679                    continue
1680                # if member is of compositeType that was not saved we cant save it now
1681                if m.type in allStructureNamesList and m.type not in savedStructureNamesList:
1682                    return False
1683            return True
1684
1685        # iterate over all composite types
1686        lastDelayedComposite = None
1687        for ct in api.compositeTypes:
1688            # check if one of delayed structures can be saved
1689            delayedButSaved = []
1690            for dct in delayedStructureObjectsList:
1691                if lastDelayedComposite != dct and canStructBeSaved(dct):
1692                    yield genCompositeTypeSrc(dct)
1693                    delayedButSaved.append(dct)
1694            lastDelayedComposite = None
1695            for dsct in delayedButSaved:
1696                savedStructureNamesList.append(dsct.name)
1697                delayedStructureObjectsList.remove(dsct)
1698            # check if current structure can be saved
1699            if canStructBeSaved(ct):
1700                yield genCompositeTypeSrc(ct)
1701                savedStructureNamesList.append(ct.name)
1702            else:
1703                delayedStructureObjectsList.append(ct)
1704                # memorize structure that was delayed in last iteration to
1705                # avoid calling for it canStructBeSaved in next iteration
1706                lastDelayedComposite = ct
1707        # save remaining delayed composite types (~4 video related structures)
1708        while len(delayedStructureObjectsList) > 0:
1709            for dct in delayedStructureObjectsList:
1710                if canStructBeSaved(dct):
1711                    yield genCompositeTypeSrc(dct)
1712                    savedStructureNamesList.append(dct.name)
1713                    delayedStructureObjectsList.remove(dct)
1714                    break
1715        # write all alias typedefs
1716        for ct in api.compositeTypes:
1717            for alias in ct.aliasList:
1718                yield "typedef %s %s;" % (ct.name, alias)
1719                yield ""
1720
1721    writeInlFile(filename, INL_HEADER, gen())
1722
1723def argListToStr (args):
1724    def argumentToString(arg):
1725        # args can be instance of FunctionArgument or CompositeMember
1726        # but CompositeMember has no arraySize atrribute nor secondPointerIsConst
1727        workingOnFunctionArgument = True if hasattr(arg, 'arraySize') else False
1728        result = ''
1729        if arg.qualifiers:
1730            result += arg.qualifiers
1731        result += arg.type
1732        if arg.pointer:
1733            if workingOnFunctionArgument and arg.secondPointerIsConst:
1734                result += '* const*'
1735            else:
1736                result += arg.pointer
1737        result += ' ' + arg.name
1738        if workingOnFunctionArgument:
1739            if arg.arraySize:
1740                result += arg.arraySize
1741        return result
1742    return ", ".join(argumentToString(arg) for arg in args)
1743
1744def writeInterfaceDecl (api, filename, functionTypes, concrete):
1745    def genProtos ():
1746        postfix = "" if concrete else " = 0"
1747        for function in api.functions:
1748            if not function.getType() in functionTypes:
1749                continue
1750            yield "virtual %s\t%s\t(%s) const%s;" % (function.returnType, getInterfaceName(function.name), argListToStr(function.arguments), postfix)
1751
1752    writeInlFile(filename, INL_HEADER, indentLines(genProtos()))
1753
1754def writeFunctionPtrTypes (api, filename):
1755    def genTypes ():
1756        pattern = "typedef VKAPI_ATTR {}\t(VKAPI_CALL* {})\t({});"
1757        for function in api.functions:
1758            argList = argListToStr(function.arguments)
1759            yield pattern.format(function.returnType, getFunctionTypeName(function.name), argList)
1760            for alias in function.aliasList:
1761                yield pattern.format(function.returnType, getFunctionTypeName(alias), argList)
1762
1763    writeInlFile(filename, INL_HEADER, indentLines(genTypes()))
1764
1765def writeFunctionPointers (api, filename, functionTypes):
1766    def FunctionsYielder ():
1767        for function in api.functions:
1768            if function.getType() in functionTypes:
1769                interfaceName = getInterfaceName(function.name)
1770                functionTypeName = getFunctionTypeName(function.name)
1771                yield f"{functionTypeName}\t{interfaceName};"
1772                if function.getType() == Function.TYPE_INSTANCE:
1773                    for alias in function.aliasList:
1774                        interfaceName = getInterfaceName(alias)
1775                        functionTypeName = getFunctionTypeName(alias)
1776                        yield f"{functionTypeName}\t{interfaceName};"
1777
1778    writeInlFile(filename, INL_HEADER, indentLines(FunctionsYielder()))
1779
1780def getPromotedFunctions (api):
1781    apiNum = 0 if api.apiName == "vulkan" else 1
1782    promotedFunctions = collections.defaultdict(lambda: list())
1783    for feature in api.features:
1784        versionSplit = feature.name.split('_')
1785        apiMajor = int(versionSplit[-2])
1786        apiMinor = int(versionSplit[-1])
1787        apiPrefix = '_'.join(versionSplit[:-2])
1788        if apiNum == 0 and apiPrefix != 'VK_VERSION':
1789            continue
1790        if apiNum == 1 and apiPrefix == 'VK_VERSION':
1791            # Map of "standard" Vulkan versions to VulkanSC version.
1792            stdToSCMap = {
1793                (1, 0): (1, 0),
1794                (1, 1): (1, 0),
1795                (1, 2): (1, 0),
1796            }
1797            mapKey = (apiMajor, apiMinor)
1798            if mapKey not in stdToSCMap:
1799                continue
1800            (apiMajor, apiMinor) = stdToSCMap[mapKey]
1801        apituple = (apiNum, apiMajor, apiMinor)
1802        for featureRequirement in feature.requirementsList:
1803            for promotedFun in featureRequirement.commandList:
1804                promotedFunctions[promotedFun].append(apituple)
1805    return promotedFunctions
1806
1807def writeInitFunctionPointers (api, filename, functionTypes, cond = None):
1808    promotedFunctions = getPromotedFunctions(api) if Function.TYPE_DEVICE in functionTypes else None
1809    def makeInitFunctionPointers ():
1810        for function in api.functions:
1811            if function.getType() in functionTypes and (cond == None or cond(function)):
1812                condition = ''
1813                if function.getType() == Function.TYPE_DEVICE:
1814                    versionCheck = ''
1815                    if function.name in promotedFunctions:
1816                        for versionTuple in promotedFunctions[function.name]:
1817                            if len(versionCheck) > 0:
1818                                versionCheck += ' || '
1819                            versionCheck = 'usedApiVersion >= VK_MAKE_API_VERSION(%s, %s, %s, 0)' % versionTuple
1820                    if len(versionCheck) > 0:
1821                        condition = f"if ({versionCheck})\n    "
1822                interfaceName = getInterfaceName(function.name)
1823                functionTypeName = getFunctionTypeName(function.name)
1824                yield f"{condition}m_vk.{interfaceName} = ({functionTypeName}) GET_PROC_ADDR(\"{function.name}\");"
1825                for alias in function.aliasList:
1826                    yield f"if (!m_vk.{interfaceName})"
1827                    yield f"    m_vk.{interfaceName} = ({functionTypeName}) GET_PROC_ADDR(\"{alias}\");"
1828                    if function.getType() == Function.TYPE_INSTANCE and function.arguments[0].type == "VkPhysicalDevice":
1829                        interfaceName = getInterfaceName(alias)
1830                        functionTypeName = getFunctionTypeName(alias)
1831                        yield f"m_vk.{interfaceName} = ({functionTypeName}) GET_PROC_ADDR(\"{alias}\");"
1832
1833    lines = makeInitFunctionPointers()
1834    writeInlFile(filename, INL_HEADER, lines)
1835
1836# List pre filled manually with commands forbidden for computation only implementations
1837computeOnlyForbiddenCommands = [
1838    "destroyRenderPass",
1839    "createRenderPass2",
1840    "createRenderPass",
1841    "createGraphicsPipelines"
1842]
1843
1844computeOnlyRestrictedCommands = {
1845    "createComputePipelines"    : "\t\tfor (uint32_t i=0; i<createInfoCount; ++i)\n\t\t\tif ((pCreateInfos[i].stage.stage & VK_SHADER_STAGE_ALL_GRAPHICS) != 0) THROW_NOT_SUPPORTED_COMPUTE_ONLY();",
1846    "createBuffer"                : "\t\tif ((pCreateInfo->usage & ( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT )) !=0) THROW_NOT_SUPPORTED_COMPUTE_ONLY();",
1847}
1848
1849def writeFuncPtrInterfaceImpl (api, filename, functionTypes, className):
1850
1851    # populate compute only forbidden commands
1852    for fun in api.functions:
1853        if "graphics" in fun.queuesList and "compute" not in fun.queuesList:
1854            # remove the 'vk' prefix and change the first character of the remaining string to lowercase
1855            commandName = fun.name[2:3].lower() + fun.name[3:]
1856            computeOnlyForbiddenCommands.append(commandName)
1857
1858            # if the command has an alias, also add it
1859            for alias_name in fun.aliasList:
1860                alias_name_without_vk = alias_name[2:3].lower() + alias_name[3:]
1861                computeOnlyForbiddenCommands.append(alias_name_without_vk)
1862
1863    def makeFuncPtrInterfaceImpl ():
1864        for function in api.functions:
1865            functionInterfaceName = getInterfaceName(function.name)
1866            if function.getType() in functionTypes:
1867                yield ""
1868                yield "%s %s::%s (%s) const" % (function.returnType, className, functionInterfaceName, argListToStr(function.arguments))
1869                yield "{"
1870                # Check for compute only forbidden commands
1871                if functionInterfaceName in computeOnlyForbiddenCommands:
1872                    yield "    if( m_computeOnlyMode ) THROW_NOT_SUPPORTED_COMPUTE_ONLY();"
1873                # Check for compute only restricted commands
1874                if functionInterfaceName in computeOnlyRestrictedCommands:
1875                    yield "\tif( m_computeOnlyMode )"
1876                    yield "\t{"
1877                    yield computeOnlyRestrictedCommands[functionInterfaceName]
1878                    yield "\t}"
1879                # Special case for vkEnumerateInstanceVersion
1880                if function.name == "vkEnumerateInstanceVersion":
1881                    yield "    if (m_vk.enumerateInstanceVersion)"
1882                    yield "        return m_vk.enumerateInstanceVersion(pApiVersion);"
1883                    yield ""
1884                    yield "    *pApiVersion = VK_API_VERSION_1_0;"
1885                    yield "    return VK_SUCCESS;"
1886                    yield "}"
1887                    continue
1888                # Simplify code by preparing string template needed in few code branches
1889                tab = ' ' * 4
1890                funReturn = "" if function.returnType == "void" else "return "
1891                funParams = ", ".join(a.name for a in function.arguments)
1892                callTemplate = f"{tab}{funReturn}m_vk.{{}}({funParams});"
1893                # Special case for all instance functions that operate on VkPhysicalDevice
1894                if function.getType() == Function.TYPE_INSTANCE and function.arguments[0].type == "VkPhysicalDevice":
1895                    # Helper function that checks if entry point was promoted to core
1896                    def isInCore(allFunAliases):
1897                        for feature in api.features:
1898                            if api.apiName not in feature.api.split(','):
1899                                continue
1900                            for r in feature.requirementsList:
1901                                for n in allFunAliases:
1902                                    if n in r.commandList:
1903                                        return (True, feature.number)
1904                        return (False, "1.0")
1905                    (inCore, coreNumber) = isInCore([function.name] + function.aliasList)
1906                    if inCore and "1.0" not in coreNumber:
1907                        callTemplate = f"{tab}{callTemplate}"
1908                        yield "    vk::VkPhysicalDeviceProperties props;"
1909                        yield "    m_vk.getPhysicalDeviceProperties(physicalDevice, &props);"
1910                        yield f"    if (props.apiVersion >= VK_API_VERSION_{coreNumber.replace('.', '_')})"
1911                        yield callTemplate.format(functionInterfaceName)
1912                        yield "    else"
1913                        yield callTemplate.format(getInterfaceName(function.aliasList[0]))
1914                        yield "}"
1915                        continue
1916                yield callTemplate.format(functionInterfaceName)
1917                yield "}"
1918    writeInlFile(filename, INL_HEADER, makeFuncPtrInterfaceImpl())
1919
1920def writeFuncPtrInterfaceSCImpl (api, filename, functionTypes, className):
1921    normFuncs = {
1922        "createGraphicsPipelines"        : "\t\treturn createGraphicsPipelinesHandlerNorm(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);",
1923        "createComputePipelines"        : "\t\treturn createComputePipelinesHandlerNorm(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);",
1924        "createSampler"                    : "\t\treturn createSamplerHandlerNorm(device, pCreateInfo, pAllocator, pSampler);",
1925        "createSamplerYcbcrConversion"    : "\t\treturn createSamplerYcbcrConversionHandlerNorm(device, pCreateInfo, pAllocator, pYcbcrConversion);",
1926        "createDescriptorSetLayout"        : "\t\treturn createDescriptorSetLayoutHandlerNorm(device, pCreateInfo, pAllocator, pSetLayout);",
1927        "createPipelineLayout"            : "\t\treturn createPipelineLayoutHandlerNorm(device, pCreateInfo, pAllocator, pPipelineLayout);",
1928        "createRenderPass"                : "\t\treturn createRenderPassHandlerNorm(device, pCreateInfo, pAllocator, pRenderPass);",
1929        "createRenderPass2"                : "\t\treturn createRenderPass2HandlerNorm(device, pCreateInfo, pAllocator, pRenderPass);",
1930        "createCommandPool"                : "\t\treturn createCommandPoolHandlerNorm(device, pCreateInfo, pAllocator, pCommandPool);",
1931        "resetCommandPool"                : "\t\treturn resetCommandPoolHandlerNorm(device, commandPool, flags);",
1932        "createFramebuffer"                : "\t\treturn createFramebufferHandlerNorm(device, pCreateInfo, pAllocator, pFramebuffer);",
1933    }
1934    statFuncs = {
1935        "destroyDevice"                    : "\t\tdestroyDeviceHandler(device, pAllocator);",
1936        "createDescriptorSetLayout"        : "\t\tcreateDescriptorSetLayoutHandlerStat(device, pCreateInfo, pAllocator, pSetLayout);",
1937        "destroyDescriptorSetLayout"    : "\t\tdestroyDescriptorSetLayoutHandler(device, descriptorSetLayout, pAllocator);",
1938        "createImageView"                : "\t\tcreateImageViewHandler(device, pCreateInfo, pAllocator, pView);",
1939        "destroyImageView"                : "\t\tdestroyImageViewHandler(device, imageView, pAllocator);",
1940        "createSemaphore"                : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(semaphoreRequestCount,1);\n\t\t*pSemaphore = m_resourceInterface->incResourceCounter<VkSemaphore>();\n\t}",
1941        "destroySemaphore"                : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(semaphore,semaphoreRequestCount,1);\n\t}",
1942        "createFence"                    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(fenceRequestCount,1);\n\t\t*pFence = m_resourceInterface->incResourceCounter<VkFence>();\n\t}",
1943        "destroyFence"                    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(fence,fenceRequestCount,1);\n\t}",
1944        "allocateMemory"                : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(deviceMemoryRequestCount,1);\n\t\t*pMemory = m_resourceInterface->incResourceCounter<VkDeviceMemory>();\n\t}",
1945        "createBuffer"                    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(bufferRequestCount,1);\n\t\t*pBuffer = m_resourceInterface->incResourceCounter<VkBuffer>();\n\t}",
1946        "destroyBuffer"                    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(buffer,bufferRequestCount,1);\n\t}",
1947        "createImage"                    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(imageRequestCount,1);\n\t\t*pImage = m_resourceInterface->incResourceCounter<VkImage>();\n\t}",
1948        "destroyImage"                    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(image,imageRequestCount,1);\n\t}",
1949        "createEvent"                    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(eventRequestCount,1);\n\t\t*pEvent = m_resourceInterface->incResourceCounter<VkEvent>();\n\t}",
1950        "destroyEvent"                    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(event,eventRequestCount,1);\n\t}",
1951        "createQueryPool"                : "\t\tcreateQueryPoolHandler(device, pCreateInfo, pAllocator, pQueryPool);",
1952        "createBufferView"                : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(bufferViewRequestCount,1);\n\t\t*pView = m_resourceInterface->incResourceCounter<VkBufferView>();\n\t}",
1953        "destroyBufferView"                : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(bufferView,bufferViewRequestCount,1);\n\t}",
1954        "createPipelineLayout"            : "\t\tcreatePipelineLayoutHandlerStat(device, pCreateInfo, pAllocator, pPipelineLayout);",
1955        "destroyPipelineLayout"            : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(pipelineLayout,pipelineLayoutRequestCount,1);\n\t}",
1956        "createRenderPass"                : "\t\tcreateRenderPassHandlerStat(device, pCreateInfo, pAllocator, pRenderPass);",
1957        "createRenderPass2"                : "\t\tcreateRenderPass2HandlerStat(device, pCreateInfo, pAllocator, pRenderPass);",
1958        "destroyRenderPass"                : "\t\tdestroyRenderPassHandler(device, renderPass, pAllocator);",
1959        "createGraphicsPipelines"        : "\t\tcreateGraphicsPipelinesHandlerStat(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);",
1960        "createComputePipelines"        : "\t\tcreateComputePipelinesHandlerStat(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines);",
1961        "destroyPipeline"                : "\t\tdestroyPipelineHandler(device, pipeline, pAllocator);",
1962        "createSampler"                    : "\t\tcreateSamplerHandlerStat(device, pCreateInfo, pAllocator, pSampler);",
1963        "destroySampler"                : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(sampler,samplerRequestCount,1);\n\t}",
1964        "createDescriptorPool"            : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(descriptorPoolRequestCount,1);\n\t\t*pDescriptorPool = m_resourceInterface->incResourceCounter<VkDescriptorPool>();\n\t}",
1965        "resetDescriptorPool"            : "\t\tresetDescriptorPoolHandlerStat(device, descriptorPool, flags);",
1966        "allocateDescriptorSets"        : "\t\tallocateDescriptorSetsHandlerStat(device, pAllocateInfo, pDescriptorSets);",
1967        "freeDescriptorSets"            : "\t\tfreeDescriptorSetsHandlerStat(device, descriptorPool, descriptorSetCount, pDescriptorSets);",
1968        "createFramebuffer"                : "\t\tcreateFramebufferHandlerStat(device, pCreateInfo, pAllocator, pFramebuffer);",
1969        "destroyFramebuffer"            : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(framebuffer,framebufferRequestCount,1);\n\t}",
1970        "createCommandPool"                : "\t\tcreateCommandPoolHandlerStat(device, pCreateInfo, pAllocator, pCommandPool);",
1971        "resetCommandPool"                : "\t\tresetCommandPoolHandlerStat(device, commandPool, flags);",
1972        "allocateCommandBuffers"        : "\t\tallocateCommandBuffersHandler(device, pAllocateInfo, pCommandBuffers);",
1973        "freeCommandBuffers"            : "\t\tfreeCommandBuffersHandler(device, commandPool, commandBufferCount, pCommandBuffers);",
1974        "createSamplerYcbcrConversion"    : "\t\tcreateSamplerYcbcrConversionHandlerStat(device, pCreateInfo, pAllocator, pYcbcrConversion);",
1975        "destroySamplerYcbcrConversion"    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(ycbcrConversion,samplerYcbcrConversionRequestCount,1);\n\t}",
1976        "getDescriptorSetLayoutSupport"    : "\t\tgetDescriptorSetLayoutSupportHandler(device, pCreateInfo, pSupport);",
1977#        "" : "surfaceRequestCount",
1978#        "" : "swapchainRequestCount",
1979#        "" : "displayModeRequestCount"
1980        "mapMemory"                        : "\t{\n\t\tDDSTAT_LOCK();\n\t\tif(m_falseMemory.size() < (static_cast<std::size_t>(offset+size)))\n\t\t\tm_falseMemory.resize(static_cast<std::size_t>(offset+size));\n\t\t*ppData = (void*)m_falseMemory.data();\n\t}",
1981        "getBufferMemoryRequirements"    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tpMemoryRequirements->size = 1048576U;\n\t\tpMemoryRequirements->alignment = 1U;\n\t\tpMemoryRequirements->memoryTypeBits = ~0U;\n\t}",
1982        "getImageMemoryRequirements"    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tpMemoryRequirements->size = 1048576U;\n\t\tpMemoryRequirements->alignment = 1U;\n\t\tpMemoryRequirements->memoryTypeBits = ~0U;\n\t}",
1983        "getBufferMemoryRequirements2"    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tpMemoryRequirements->memoryRequirements.size = 1048576U;\n\t\tpMemoryRequirements->memoryRequirements.alignment = 1U;\n\t\tpMemoryRequirements->memoryRequirements.memoryTypeBits = ~0U;\n\t}",
1984        "getImageMemoryRequirements2"    : "\t{\n\t\tDDSTAT_LOCK();\n\t\tpMemoryRequirements->memoryRequirements.size = 1048576U;\n\t\tpMemoryRequirements->memoryRequirements.alignment = 1U;\n\t\tpMemoryRequirements->memoryRequirements.memoryTypeBits = ~0U;\n\t}",
1985        "getImageSubresourceLayout"        : "\t{\n\t\tDDSTAT_LOCK();\n\t\tpLayout->offset = 0U;\n\t\tpLayout->size = 1048576U;\n\t\tpLayout->rowPitch = 0U;\n\t\tpLayout->arrayPitch = 0U;\n\t\tpLayout->depthPitch = 0U;\n\t}",
1986        "createPipelineCache"            : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_CREATE(pipelineCacheRequestCount,1);\n\t\t*pPipelineCache = m_resourceInterface->incResourceCounter<VkPipelineCache>();\n\t}",
1987        "destroyPipelineCache"            : "\t{\n\t\tDDSTAT_LOCK();\n\t\tDDSTAT_HANDLE_DESTROY_IF(pipelineCache,pipelineCacheRequestCount,1);\n\t}",
1988        "cmdUpdateBuffer"                : "\t\tincreaseCommandBufferSize(commandBuffer, dataSize);",
1989        "getDeviceQueue"                : "\t\tm_vk.getDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);",
1990    }
1991
1992    statReturns = {
1993        "VkResult"            : "return VK_SUCCESS;",
1994        "VkDeviceAddress"    : "return 0u;",
1995        "uint64_t"            : "return 0u;",
1996    }
1997
1998    def makeFuncPtrInterfaceStatisticsImpl ():
1999        for function in api.functions:
2000            if function.getType() in functionTypes:
2001                ifaceName = getInterfaceName(function.name)
2002                yield ""
2003                yield "%s %s::%s (%s) const" % (function.returnType, className, ifaceName, argListToStr(function.arguments))
2004                yield "{"
2005                # Check for compute only forbidden commands
2006                if ifaceName in computeOnlyForbiddenCommands:
2007                    yield "\tif( m_computeOnlyMode ) THROW_NOT_SUPPORTED_COMPUTE_ONLY();"
2008                # Check for compute only restricted commands
2009                if ifaceName in computeOnlyRestrictedCommands:
2010                    yield "\tif( m_computeOnlyMode )"
2011                    yield "\t{"
2012                    yield computeOnlyRestrictedCommands[ifaceName]
2013                    yield "\t}"
2014                if ( ifaceName in normFuncs ) or ( ifaceName in statFuncs ):
2015                    yield "\tstd::lock_guard<std::mutex> lock(functionMutex);"
2016                if ifaceName != "getDeviceProcAddr" :
2017                    yield "\tif (m_normalMode)"
2018                if ifaceName in normFuncs :
2019                    yield "%s" % ( normFuncs[ifaceName] )
2020                else:
2021                    yield "\t\t%sm_vk.%s(%s);" % ("return " if function.returnType != "void" else "", ifaceName, ", ".join(a.name for a in function.arguments))
2022                if ifaceName in statFuncs :
2023                    yield "\telse"
2024                    yield "%s" % ( statFuncs[ifaceName] )
2025                elif ifaceName[:3] == "cmd" :
2026                    yield "\telse"
2027                    yield "\t\tincreaseCommandBufferSize(commandBuffer, 0u);"
2028                if function.returnType in statReturns:
2029                    yield "\t%s" % ( statReturns[function.returnType] )
2030                yield "}"
2031
2032    writeInlFile(filename, INL_HEADER, makeFuncPtrInterfaceStatisticsImpl())
2033
2034def writeStrUtilProto (api, filename):
2035    def makeStrUtilProto ():
2036        for line in indentLines(["const char*\tget%sName\t(%s value);" % (enum.name[2:], enum.name) for enum in api.enums if enum.type == "enum"]):
2037            yield line
2038        yield ""
2039        for line in indentLines(["inline tcu::Format::Enum<%s>\tget%sStr\t(%s value)\t{ return tcu::Format::Enum<%s>(get%sName, value);\t}" % (e.name, e.name[2:], e.name, e.name, e.name[2:]) for e in api.enums if e.type == "enum"]):
2040            yield line
2041        yield ""
2042        for line in indentLines(["inline std::ostream&\toperator<<\t(std::ostream& s, %s value)\t{ return s << get%sStr(value);\t}" % (e.name, e.name[2:]) for e in api.enums if e.type == "enum"]):
2043            yield line
2044        yield ""
2045        for line in indentLines(["tcu::Format::Bitfield<%s>\tget%sStr\t(%s value);" % (("64" if b.type == "VkFlags64" else "32"), b.name[2:], b.name) for b in api.bitmasks]):
2046            yield line
2047        yield ""
2048        for line in indentLines(["std::ostream&\toperator<<\t(std::ostream& s, const %s& value);" % (s.name) for s in api.compositeTypes]):
2049            yield line
2050
2051    writeInlFile(filename, INL_HEADER, makeStrUtilProto())
2052
2053def writeStrUtilImpl (api, filename):
2054    def makeStrUtilImpl ():
2055        for line in indentLines(["template<> const char*\tgetTypeName<%s>\t(void) { return \"%s\";\t}" % (handle.name, handle.name) for handle in api.handles]):
2056            yield line
2057
2058        yield ""
2059        yield "namespace %s" % PLATFORM_TYPE_NAMESPACE
2060        yield "{"
2061
2062        for line in indentLines("std::ostream& operator<< (std::ostream& s, %s\tv) { return s << tcu::toHex(v.internal); }" % ''.join(s) for n, s, c in PLATFORM_TYPES):
2063            yield line
2064
2065        yield "}"
2066
2067        savedBitmasks = []
2068        for enum in api.enums:
2069            if enum.type == "enum":
2070                yield ""
2071                yield "const char* get%sName (%s value)" % (enum.name[2:], enum.name)
2072                yield "{"
2073                yield "\tswitch (value)"
2074                yield "\t{"
2075                enumValues = []
2076                lastValue = 0x7FFFFFFF
2077                for e in enum.enumeratorList:
2078                    enumValues.append(f"\t\tcase {e.name}:\treturn \"{e.name}\";")
2079                enumValues.append("\t\tdefault:\treturn nullptr;")
2080                for line in indentLines(enumValues):
2081                    yield line
2082                yield "\t}"
2083                yield "}"
2084            elif enum.type == "bitmask":
2085                # find bitfield that uses those bitmasks
2086                foundBitmask = None
2087                for bitmask in api.bitmasks:
2088                    if bitmask.requires == enum.name or bitmask.bitvalues == enum.name:
2089                        foundBitmask = bitmask
2090                        break
2091                if foundBitmask == None:
2092                    continue
2093                savedBitmasks.append(foundBitmask.name)
2094                bitSize = "64" if foundBitmask.type == "VkFlags64" else "32"
2095                yield ""
2096                yield f"tcu::Format::Bitfield<{bitSize}> get{bitmask.name[2:]}Str ({bitmask.name} value)"
2097                yield "{"
2098                yield "\tstatic const tcu::Format::BitDesc s_desc[] ="
2099                yield "\t{"
2100                if len(enum.enumeratorList) == 0:
2101                    # some bitfields in SC have no items
2102                    yield f"\t\ttcu::Format::BitDesc(0, \"0\")"
2103                else:
2104                    for line in indentLines([f"\t\ttcu::Format::BitDesc({e.name},\t\"{e.name}\")," for e in enum.enumeratorList]):
2105                        yield line
2106                yield "\t};"
2107                yield f"\treturn tcu::Format::Bitfield<{bitSize}>(value, DE_ARRAY_BEGIN(s_desc), DE_ARRAY_END(s_desc));"
2108                yield "}"
2109
2110        for bitmask in api.bitmasks:
2111            if bitmask.name not in savedBitmasks:
2112                bitSize = "64" if bitmask.type == "VkFlags64" else "32"
2113                yield ""
2114                yield f"tcu::Format::Bitfield<{bitSize}> get{bitmask.name[2:]}Str ({bitmask.name} value)"
2115                yield "{"
2116                yield f"\treturn tcu::Format::Bitfield<{bitSize}>(value, nullptr, nullptr);"
2117                yield "}"
2118
2119        bitfieldTypeNames = set([bitmask.name for bitmask in api.bitmasks])
2120
2121        for type in api.compositeTypes:
2122            yield ""
2123            yield "std::ostream& operator<< (std::ostream& s, const %s& value)" % type.name
2124            yield "{"
2125            yield "\ts << \"%s = {\\n\";" % type.name
2126            for member in type.members:
2127                memberName = member.name
2128                valFmt = None
2129                newLine = ""
2130                if member.type in bitfieldTypeNames:
2131                    operator = '*' if member.pointer == '*' else ''
2132                    valFmt = "get%sStr(%svalue.%s)" % (member.type[2:], operator, member.name)
2133                elif member.type == "char" and member.pointer == '*':
2134                    valFmt = "getCharPtrStr(value.%s)" % member.name
2135                elif member.type == PLATFORM_TYPE_NAMESPACE + "::Win32LPCWSTR":
2136                    valFmt = "getWStr(value.%s)" % member.name
2137                elif len(member.arraySizeList) == 1:
2138                    if member.name in ["extensionName", "deviceName", "layerName", "description"]:
2139                        valFmt = "(const char*)value.%s" % member.name
2140                    elif member.type == 'char' or member.type == 'uint8_t':
2141                        newLine = "'\\n' << "
2142                        valFmt = "tcu::formatArray(tcu::Format::HexIterator<%s>(DE_ARRAY_BEGIN(value.%s)), tcu::Format::HexIterator<%s>(DE_ARRAY_END(value.%s)))" % (member.type, member.name, member.type, member.name)
2143                    else:
2144                        if member.name == "memoryTypes" or member.name == "memoryHeaps":
2145                            endIter = "DE_ARRAY_BEGIN(value.%s) + value.%sCount" % (member.name, member.name[:-1])
2146                        else:
2147                            endIter = "DE_ARRAY_END(value.%s)" % member.name
2148                        newLine = "'\\n' << "
2149                        valFmt = "tcu::formatArray(DE_ARRAY_BEGIN(value.%s), %s)" % (member.name, endIter)
2150                    memberName = member.name
2151                elif len(member.arraySizeList) > 1:
2152                    yield f"\ts << \"\\t{member.name} = \" << '\\n';"
2153                    dim = 0
2154                    index = ''
2155                    dimensionCount = len(member.arraySizeList)
2156                    while dim < dimensionCount-1:
2157                        yield f"\tfor(uint32_t i{dim} = 0 ; i{dim} < {member.arraySizeList[dim]} ; ++i{dim})"
2158                        index += f"[i{dim}]"
2159                        dim +=1
2160                    yield f"\t\ts << tcu::formatArray(DE_ARRAY_BEGIN(value.{member.name}{index}), DE_ARRAY_END(value.{member.name}{index})) << '\\n';"
2161                    # move to next member
2162                    continue
2163                else:
2164                    valFmt = "value.%s" % member.name
2165                yield ("\ts << \"\\t%s = \" << " % memberName) + newLine + valFmt + " << '\\n';"
2166            yield "\ts << '}';"
2167            yield "\treturn s;"
2168            yield "}"
2169    writeInlFile(filename, INL_HEADER, makeStrUtilImpl())
2170
2171def writeObjTypeImpl (api, filename):
2172    def makeObjTypeImpl ():
2173
2174        yield "namespace vk"
2175        yield "{"
2176
2177        yield "template<typename T> VkObjectType getObjectType    (void);"
2178
2179        for line in indentLines(["template<> inline VkObjectType\tgetObjectType<%s>\t(void) { return %s;\t}" % (handle.name, prefixName("VK_OBJECT_TYPE_", handle.name)) for handle in api.handles]):
2180            yield line
2181
2182        yield "}"
2183
2184    writeInlFile(filename, INL_HEADER, makeObjTypeImpl())
2185
2186class ConstructorFunction:
2187    def __init__ (self, type, name, objectType, ifaceArgs, arguments):
2188        self.type = type
2189        self.name = name
2190        self.objectType = objectType
2191        self.ifaceArgs = ifaceArgs
2192        self.arguments = arguments
2193
2194def getConstructorFunctions (api):
2195    funcs = []
2196
2197    ifacesDict = {
2198        Function.TYPE_PLATFORM: [FunctionArgument("vk", "const ", "PlatformInterface&")],
2199        Function.TYPE_INSTANCE: [FunctionArgument("vk", "const ", "InstanceInterface&")],
2200        Function.TYPE_DEVICE: [FunctionArgument("vk", "const ", "DeviceInterface&")]}
2201
2202    for function in api.functions:
2203        if (function.name[:8] == "vkCreate" or function.name == "vkAllocateMemory") and not "createInfoCount" in [a.name for a in function.arguments]:
2204            if function.name in ["vkCreatePipelineBinariesKHR", "vkCreateDisplayModeKHR"]:
2205                continue # No way to delete display modes (bug?)
2206
2207            ifaceArgs = []
2208            if function.name == "vkCreateDevice":
2209                ifaceArgs = [FunctionArgument("vkp", "const ", "PlatformInterface&"),
2210                             FunctionArgument("instance", "", "VkInstance")]
2211            ifaceArgs.extend(ifacesDict[function.getType()])
2212
2213            allocatorArg = function.arguments[-2]
2214            assert (allocatorArg.type == "VkAllocationCallbacks" and \
2215                    "const" in allocatorArg.qualifiers and \
2216                    allocatorArg.pointer == "*")
2217
2218            objectType = function.arguments[-1].type
2219            arguments = function.arguments[:-1]
2220            funcs.append(ConstructorFunction(function.getType(), getInterfaceName(function.name), objectType, ifaceArgs, arguments))
2221    return funcs
2222
2223def addVersionDefines(versionSpectrum):
2224    output = ["#define " + ver.getDefineName() + " " + ver.getInHex() for ver in versionSpectrum if not ver.isStandardVersion()]
2225    return output
2226
2227def writeRefUtilProto (api, filename):
2228    functions = getConstructorFunctions(api)
2229
2230    def makeRefUtilProto ():
2231        unindented = []
2232        for line in indentLines(["Move<%s>\t%s\t(%s = nullptr);" % (function.objectType, function.name, argListToStr(function.ifaceArgs + function.arguments)) for function in functions]):
2233            yield line
2234
2235    writeInlFile(filename, INL_HEADER, makeRefUtilProto())
2236
2237def writeRefUtilImpl (api, filename):
2238    functions = getConstructorFunctions(api)
2239
2240    def makeRefUtilImpl ():
2241        yield "namespace refdetails"
2242        yield "{"
2243        yield ""
2244
2245        for function in api.functions:
2246            if function.getType() == Function.TYPE_DEVICE \
2247            and (function.name[:9] == "vkDestroy" or function.name == "vkFreeMemory") \
2248            and not function.name == "vkDestroyDevice":
2249                objectType = function.arguments[-2].type
2250                yield "template<>"
2251                yield "void Deleter<%s>::operator() (%s obj) const" % (objectType, objectType)
2252                yield "{"
2253                yield "\tm_deviceIface->%s(m_device, obj, m_allocator);" % (getInterfaceName(function.name))
2254                yield "}"
2255                yield ""
2256
2257        yield "} // refdetails"
2258        yield ""
2259
2260        dtorDict = {
2261            Function.TYPE_PLATFORM: "object",
2262            Function.TYPE_INSTANCE: "instance",
2263            Function.TYPE_DEVICE: "device"
2264        }
2265
2266        for function in functions:
2267            deleterArgsString = ''
2268            if function.name == "createDevice":
2269                # createDevice requires two additional parameters to setup VkDevice deleter
2270                deleterArgsString = "vkp, instance, object, " +  function.arguments[-1].name
2271            else:
2272                deleterArgsString = "vk, %s, %s" % (dtorDict[function.type], function.arguments[-1].name)
2273
2274            yield "Move<%s> %s (%s)" % (function.objectType, function.name, argListToStr(function.ifaceArgs + function.arguments))
2275            yield "{"
2276            yield "\t%s object = VK_NULL_HANDLE;" % function.objectType
2277            yield "\tVK_CHECK(vk.%s(%s));" % (function.name, ", ".join([a.name for a in function.arguments] + ["&object"]))
2278            yield "\treturn Move<%s>(check<%s>(object), Deleter<%s>(%s));" % (function.objectType, function.objectType, function.objectType, deleterArgsString)
2279            yield "}"
2280            yield ""
2281
2282    writeInlFile(filename, INL_HEADER, makeRefUtilImpl())
2283
2284def writeStructTraitsImpl (api, filename):
2285    def gen ():
2286        for cType in api.compositeTypes:
2287            if cType.category == "struct" and cType.members[0].name == "sType" and cType.name != "VkBaseOutStructure" and cType.name != "VkBaseInStructure":
2288                yield "template<> VkStructureType getStructureType<%s> (void)" % cType.name
2289                yield "{"
2290                yield "\treturn %s;" % cType.members[0].values
2291                yield "}"
2292                yield ""
2293
2294    writeInlFile(filename, INL_HEADER, gen())
2295
2296def writeNullDriverImpl (api, filename):
2297    def genNullDriverImpl ():
2298        specialFuncNames = [
2299                "vkCreateGraphicsPipelines",
2300                "vkCreateComputePipelines",
2301                "vkCreateRayTracingPipelinesNV",
2302                "vkCreateRayTracingPipelinesKHR",
2303                "vkGetInstanceProcAddr",
2304                "vkGetDeviceProcAddr",
2305                "vkEnumeratePhysicalDevices",
2306                "vkEnumerateInstanceExtensionProperties",
2307                "vkEnumerateDeviceExtensionProperties",
2308                "vkGetPhysicalDeviceFeatures",
2309                "vkGetPhysicalDeviceFeatures2KHR",
2310                "vkGetPhysicalDeviceProperties",
2311                "vkGetPhysicalDeviceProperties2KHR",
2312                "vkGetPhysicalDeviceQueueFamilyProperties",
2313                "vkGetPhysicalDeviceMemoryProperties",
2314                "vkGetPhysicalDeviceFormatProperties",
2315                "vkGetPhysicalDeviceImageFormatProperties",
2316                "vkGetDeviceQueue",
2317                "vkGetBufferMemoryRequirements",
2318                "vkGetBufferMemoryRequirements2KHR",
2319                "vkGetImageMemoryRequirements",
2320                "vkGetImageMemoryRequirements2KHR",
2321                "vkAllocateMemory",
2322                "vkMapMemory",
2323                "vkUnmapMemory",
2324                "vkAllocateDescriptorSets",
2325                "vkFreeDescriptorSets",
2326                "vkResetDescriptorPool",
2327                "vkAllocateCommandBuffers",
2328                "vkFreeCommandBuffers",
2329                "vkCreateDisplayModeKHR",
2330                "vkCreateSharedSwapchainsKHR",
2331                "vkGetPhysicalDeviceExternalBufferPropertiesKHR",
2332                "vkGetPhysicalDeviceImageFormatProperties2KHR",
2333                "vkGetMemoryAndroidHardwareBufferANDROID",
2334                "vkCreateShadersEXT",
2335            ]
2336
2337        specialFuncs = [f for f in api.functions if f.name in specialFuncNames]
2338        createFuncs = [f for f in api.functions if (f.name[:8] == "vkCreate" or f.name == "vkAllocateMemory") and not f in specialFuncs]
2339        destroyFuncs = [f for f in api.functions if (f.name[:9] == "vkDestroy" or f.name == "vkFreeMemory") and not f in specialFuncs]
2340        dummyFuncs = [f for f in api.functions if f not in specialFuncs + createFuncs + destroyFuncs]
2341
2342        def getHandle (name):
2343            for handle in api.handles:
2344                if handle.name == name:
2345                    return handle
2346            raise Exception("No such handle: %s" % name)
2347
2348        for function in createFuncs:
2349            objectType = function.arguments[-1].type
2350            argsStr = ", ".join([a.name for a in function.arguments[:-1]])
2351
2352            yield "VKAPI_ATTR %s VKAPI_CALL %s (%s)" % (function.returnType, getInterfaceName(function.name), argListToStr(function.arguments))
2353            yield "{"
2354            yield "\tDE_UNREF(%s);" % function.arguments[-2].name
2355
2356            if function.arguments[-1].len != None:
2357                yield "\tVK_NULL_RETURN((allocateNonDispHandleArray<%s, %s>(%s, %s)));" % (objectType[2:], objectType, argsStr, function.arguments[-1].name)
2358            else:
2359                if function.name == "vkCreatePipelineBinariesKHR":
2360                    yield "\tDE_UNREF(device);"
2361                    yield "\tDE_UNREF(pCreateInfo);"
2362                    yield "\tDE_UNREF(pAllocator);"
2363                    yield "\tDE_UNREF(pBinaries);"
2364                    yield "\treturn VK_SUCCESS;"
2365                elif getHandle(objectType).type == "VK_DEFINE_NON_DISPATCHABLE_HANDLE":
2366                    yield "\tVK_NULL_RETURN((*%s = allocateNonDispHandle<%s, %s>(%s)));" % (function.arguments[-1].name, objectType[2:], objectType, argsStr)
2367                else:
2368                    yield "\tVK_NULL_RETURN((*%s = allocateHandle<%s, %s>(%s)));" % (function.arguments[-1].name, objectType[2:], objectType, argsStr)
2369            yield "}"
2370            yield ""
2371
2372        for function in destroyFuncs:
2373            objectArg = function.arguments[-2]
2374
2375            yield "VKAPI_ATTR %s VKAPI_CALL %s (%s)" % (function.returnType, getInterfaceName(function.name), argListToStr(function.arguments))
2376            yield "{"
2377            for arg in function.arguments[:-2]:
2378                yield "\tDE_UNREF(%s);" % arg.name
2379
2380            if getHandle(objectArg.type).type == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE':
2381                yield "\tfreeNonDispHandle<%s, %s>(%s, %s);" % (objectArg.type[2:], objectArg.type, objectArg.name, function.arguments[-1].name)
2382            else:
2383                yield "\tfreeHandle<%s, %s>(%s, %s);" % (objectArg.type[2:], objectArg.type, objectArg.name, function.arguments[-1].name)
2384
2385            yield "}"
2386            yield ""
2387
2388        for function in dummyFuncs:
2389            yield "VKAPI_ATTR %s VKAPI_CALL %s (%s)" % (function.returnType, getInterfaceName(function.name), argListToStr(function.arguments))
2390            yield "{"
2391            for arg in function.arguments:
2392                yield "\tDE_UNREF(%s);" % arg.name
2393            if function.returnType != "void":
2394                yield "\treturn VK_SUCCESS;"
2395            yield "}"
2396            yield ""
2397
2398        def genFuncEntryTable (type, name):
2399
2400            entries = []
2401            pattern = "\tVK_NULL_FUNC_ENTRY(%s,\t%s),"
2402            for f in api.functions:
2403                if f.getType() != type:
2404                    continue
2405                entries.append(pattern % (f.name, getInterfaceName(f.name)))
2406
2407            yield "static const tcu::StaticFunctionLibrary::Entry %s[] =" % name
2408            yield "{"
2409
2410            for line in indentLines(entries):
2411                yield line
2412            yield "};"
2413            yield ""
2414
2415        # Func tables
2416        for line in genFuncEntryTable(Function.TYPE_PLATFORM, "s_platformFunctions"):
2417            yield line
2418
2419        for line in genFuncEntryTable(Function.TYPE_INSTANCE, "s_instanceFunctions"):
2420            yield line
2421
2422        for line in genFuncEntryTable(Function.TYPE_DEVICE, "s_deviceFunctions"):
2423            yield line
2424
2425    writeInlFile(filename, INL_HEADER, genNullDriverImpl())
2426
2427def writeTypeUtil (api, filename):
2428    # Structs filled by API queries are not often used in test code
2429    QUERY_RESULT_TYPES = set([
2430            "VkPhysicalDeviceFeatures",
2431            "VkPhysicalDeviceLimits",
2432            "VkFormatProperties",
2433            "VkImageFormatProperties",
2434            "VkPhysicalDeviceSparseProperties",
2435            "VkQueueFamilyProperties",
2436            "VkMemoryType",
2437            "VkMemoryHeap",
2438            "StdVideoH264SpsVuiFlags",
2439            "StdVideoH264SpsFlags",
2440            "StdVideoH264PpsFlags",
2441            "StdVideoDecodeH264PictureInfoFlags",
2442            "StdVideoDecodeH264ReferenceInfoFlags",
2443            "StdVideoEncodeH264SliceHeaderFlags",
2444            "StdVideoEncodeH264PictureInfoFlags",
2445            "StdVideoEncodeH264ReferenceInfoFlags",
2446            "StdVideoEncodeH264ReferenceInfoFlags",
2447            "StdVideoH265HrdFlags",
2448            "StdVideoH265VpsFlags",
2449            "StdVideoH265SpsVuiFlags",
2450            "StdVideoH265SpsFlags",
2451            "StdVideoH265PpsFlags",
2452            "StdVideoDecodeH265PictureInfoFlags",
2453            "StdVideoDecodeH265ReferenceInfoFlags",
2454            "StdVideoEncodeH265PictureInfoFlags",
2455            "StdVideoEncodeH265ReferenceInfoFlags",
2456            "StdVideoEncodeH265SliceSegmentHeaderFlags",
2457            "StdVideoH265ProfileTierLevelFlags",
2458            "StdVideoH265ShortTermRefPicSetFlags",
2459            "StdVideoEncodeH264ReferenceListsInfoFlags",
2460            "StdVideoEncodeH265ReferenceListsInfoFlags",
2461            "StdVideoEncodeAV1OperatingPointInfoFlags",
2462            "StdVideoEncodeAV1PictureInfoFlags",
2463            "StdVideoEncodeAV1ReferenceInfoFlags",
2464        ])
2465
2466    def isSimpleStruct (type):
2467        def hasArrayMember (type):
2468            for member in type.members:
2469                if len(member.arraySizeList) > 0:
2470                    return True
2471            return False
2472
2473        def hasCompositeMember (type):
2474            for member in type.members:
2475                if member.pointer is not None and '*' not in member.pointer:
2476                    match = [c for c in api.compositeTypes if member.type == c.name]
2477                    if len(match) > 0:
2478                        return True
2479            return False
2480
2481        return type.category == "struct" and \
2482        type.members[0].type != "VkStructureType" and \
2483        not type.name in QUERY_RESULT_TYPES and \
2484        not hasArrayMember(type) and \
2485        not hasCompositeMember(type)
2486
2487    def gen ():
2488        for type in api.compositeTypes:
2489            if not isSimpleStruct(type):
2490                continue
2491
2492            if "StdVideoAV1" in type.name or "StdVideoDecodeAV1" in type.name:
2493                continue
2494
2495            name = type.name[2:] if type.name[:2].lower() == "vk" else type.name
2496
2497            yield ""
2498            yield "inline %s make%s (%s)" % (type.name, name, argListToStr(type.members))
2499            yield "{"
2500            yield "\t%s res;" % type.name
2501            for line in indentLines(["\tres.%s\t= %s;" % (m.name, m.name) for m in type.members]):
2502                yield line
2503            yield "\treturn res;"
2504            yield "}"
2505
2506    writeInlFile(filename, INL_HEADER, gen())
2507
2508def writeDriverIds(api, filename):
2509    driverIdsString = []
2510    driverIdsString.append("static const struct\n"
2511                     "{\n"
2512                     "\tstd::string driver;\n"
2513                     "\tuint32_t id;\n"
2514                     "} driverIds [] =\n"
2515                     "{")
2516    driverItems = dict()
2517    driverIdEnum = [enum for enum in api.enums if enum.name == 'VkDriverId'][0]
2518    for enumerator in driverIdEnum.enumeratorList:
2519        driverIdsString.append(f"\t{{\"{enumerator.name}\", {enumerator.value}}},")
2520        driverItems[enumerator.name] = enumerator.value
2521    for enumerator in driverIdEnum.enumeratorList:
2522        if len(enumerator.aliasList) > 0:
2523            driverIdsString.append(f"\t{{\"{enumerator.aliasList[0]}\", {enumerator.value}}},\t// {enumerator.name}")
2524    driverIdsString.append("\t{\"VK_DRIVER_ID_MAX_ENUM\", 0x7FFFFFFF}")
2525    driverIdsString.append("};")
2526
2527    writeInlFile(filename, INL_HEADER, driverIdsString)
2528
2529def writeSupportedExtensions(api, filename):
2530
2531    def writeExtensionsForVersions(map):
2532        result = []
2533        for version in map:
2534            result.append("    if (coreVersion >= " + str(version) + ")")
2535            result.append("    {")
2536            for extension in map[version]:
2537                result.append('        dst.push_back("' + extension.name + '");')
2538            result.append("    }")
2539
2540        if not map:
2541            result.append("    DE_UNREF(coreVersion);")
2542
2543        return result
2544
2545    isSC = api.apiName == 'vulkansc'
2546    instanceMap = {}
2547    deviceMap = {}
2548
2549    allExtensionNames = {e.name for e in api.extensions}
2550    for ext in api.extensions:
2551        if ext.promotedto is None or "VK_VERSION" not in ext.promotedto:
2552            continue
2553        # skip partialy promoted extensions
2554        if ext.partiallyPromoted is True:
2555            continue
2556        major = int(ext.promotedto[-3])
2557        minor = int(ext.promotedto[-1])
2558        currVersion = "VK_API_VERSION_" + ext.promotedto[-3:]
2559        # VulkanSC is based on Vulkan 1.2. Any Vulkan version greater than 1.2 should be excluded
2560        if isSC and major==1 and minor>2:
2561            continue
2562        if ext.type == 'instance':
2563            list = instanceMap.get(currVersion)
2564            instanceMap[currVersion] = list + [ext] if list else [ext]
2565        else:
2566            list = deviceMap.get(currVersion)
2567            deviceMap[currVersion] = list + [ext] if list else [ext]
2568
2569    # add list of extensions missing in Vulkan SC specification
2570    if isSC:
2571        for extensionName, data in api.additionalExtensionData:
2572            if extensionName in allExtensionNames:
2573                logging.debug("Skipping additional extension " + extensionName + " because already added")
2574                continue
2575            # make sure that this extension was registered
2576            if 'register_extension' not in data.keys():
2577                logging.debug("Skipping unregistered extension " + extensionName)
2578                continue
2579            # save array containing 'device' or 'instance' string followed by the optional vulkan version in which this extension is core;
2580            # note that register_extension section is also required for partialy promoted extensions like VK_EXT_extended_dynamic_state2
2581            # but those extensions should not fill 'core' tag
2582            match = re.match(r"(\d).(\d).(\d).(\d)", data['register_extension']['core'])
2583            if match is None:
2584                logging.debug("Skipping extension that is not matching core " + extensionName)
2585                continue
2586            major = int(match.group(2))
2587            minor = int(match.group(3))
2588            if major==1 and minor>2:
2589                continue
2590            currVersion = f"VK_API_VERSION_{major}_{minor}"
2591            ext = Extension(extensionName, 0, 0, 0, 0, 0, 0, 0, 0)
2592            if data['register_extension']['type'] == 'instance':
2593                list = instanceMap.get(currVersion)
2594                instanceMap[currVersion] = list + [ext] if list else [ext]
2595            else:
2596                list = deviceMap.get(currVersion)
2597                deviceMap[currVersion] = list + [ext] if list else [ext]
2598
2599    lines = [
2600    "",
2601    "void getCoreDeviceExtensionsImpl (uint32_t coreVersion, ::std::vector<const char*>&%s)" % (" dst" if len(deviceMap) != 0 or isSC else ""),
2602    "{"] + writeExtensionsForVersions(deviceMap) + [
2603    "}",
2604    "",
2605    "void getCoreInstanceExtensionsImpl (uint32_t coreVersion, ::std::vector<const char*>&%s)" % (" dst" if len(instanceMap) != 0 or isSC else ""),
2606    "{"] + writeExtensionsForVersions(instanceMap) + [
2607    "}",
2608    ""]
2609    writeInlFile(filename, INL_HEADER, lines)
2610
2611
2612def writeExtensionFunctions (api, filename):
2613
2614    def writeExtensionNameArrays ():
2615        instanceExtensionNames = [f"\t\"{ext.name}\"," for ext in api.extensions if ext.type == "instance"]
2616        deviceExtensionNames = [f"\t\"{ext.name}\"," for ext in api.extensions if ext.type == "device"]
2617        yield '::std::string instanceExtensionNames[] =\n{'
2618        for instanceExtName in instanceExtensionNames:
2619            yield instanceExtName
2620        yield '};\n'
2621        yield '::std::string deviceExtensionNames[] =\n{'
2622        for deviceExtName in deviceExtensionNames:
2623            yield deviceExtName
2624        yield '};'
2625
2626    def writeExtensionFunctions (functionType):
2627        isFirstWrite = True
2628        dg_list = []    # Device groups functions need special casing, as Vulkan 1.0 keeps them in VK_KHR_device_groups whereas 1.1 moved them into VK_KHR_swapchain
2629        if functionType == Function.TYPE_INSTANCE:
2630            yield 'void getInstanceExtensionFunctions (uint32_t apiVersion, const std::vector<std::string> vIEP, const std::vector<std::string> vDEP, const std::string extName, ::std::vector<const char*>& functions)\n{'
2631            yield '\t(void)vIEP;\n(void)vDEP;'
2632            dg_list = ["vkGetPhysicalDevicePresentRectanglesKHR"]
2633        elif functionType == Function.TYPE_DEVICE:
2634            yield 'void getDeviceExtensionFunctions (uint32_t apiVersion, const std::vector<std::string> vIEP, const std::vector<std::string> vDEP, const std::string extName, ::std::vector<const char*>& functions)\n{'
2635            yield '\t(void)vIEP;\n(void)vDEP;'
2636            dg_list = ["vkGetDeviceGroupPresentCapabilitiesKHR", "vkGetDeviceGroupSurfacePresentModesKHR", "vkAcquireNextImage2KHR"]
2637        for ext in api.extensions:
2638            parsedRequirements = []
2639            for requirement in ext.requirementsList:
2640                funcNames = []
2641                for requiredCommand in requirement.newCommands:
2642                    commandName = requiredCommand.name
2643                    # find function that has specified name
2644                    func = None
2645                    funcList = [f for f in api.functions if f.name == commandName]
2646                    # if name was not found check if this is alias
2647                    if len(funcList) == 0:
2648                        for f in api.functions:
2649                            for aliasName in f.aliasList:
2650                                if aliasName == commandName:
2651                                    func = f
2652                                    break
2653                            if func:
2654                                break
2655                    else:
2656                        func = funcList[0]
2657                    if func == None:
2658                        if api.apiName == "vulkansc":
2659                            continue
2660                        # something went wrong, for "vulkan" func should always be found
2661                        logging.error("%s in %s not valid" % (commandName, ext.name))
2662                        assert(False)
2663                    if func.getType() == functionType:
2664                        funcNames.append(commandName)
2665                condition = None
2666                if requirement.depends is not None:
2667                    try:
2668                        condition = transformDependsToCondition(requirement.depends, api, 'checkVersion(%s, %s, apiVersion)', 'extensionIsSupported(%s, "%s")')
2669                    except Exception as e:
2670                        if api.apiName != "vulkansc":
2671                            raise e
2672                parsedRequirements.append((requirement.depends, condition, funcNames))
2673            if ext.name:
2674                yield '\tif (extName == "%s")' % ext.name
2675                yield '\t{'
2676                for depends, condition, funcNames in parsedRequirements:
2677                    if len(funcNames) == 0:
2678                        continue
2679                    indent = '\t\t'
2680                    if depends is not None:
2681                        yield '\t\t// Dependencies: %s' % depends
2682                        yield '\t\tif (%s) {' % condition
2683                        indent = '\t\t\t'
2684                    for funcName in funcNames:
2685                        if funcName in dg_list:
2686                            yield '%sif(apiVersion >= VK_API_VERSION_1_1) functions.push_back("%s");' % (indent, funcName)
2687                        else:
2688                            yield '%sfunctions.push_back("%s");' % (indent, funcName)
2689                    if depends is not None:
2690                        yield '\t\t}'
2691                if ext.name == "VK_KHR_device_group":
2692                    for dg_func in dg_list:
2693                        yield '\t\tif(apiVersion < VK_API_VERSION_1_1) functions.push_back("%s");' % dg_func
2694                yield '\t\treturn;'
2695                yield '\t}'
2696                isFirstWrite = False
2697        if not isFirstWrite:
2698            yield '\tDE_FATAL("Extension name not found");'
2699            yield '}'
2700
2701    def genHelperFunctions():
2702        yield 'bool checkVersion(uint32_t major, uint32_t minor, const uint32_t testedApiVersion)'
2703        yield '{'
2704        yield '\tuint32_t testedMajor = VK_API_VERSION_MAJOR(testedApiVersion);'
2705        yield '\tuint32_t testedMinor = VK_API_VERSION_MINOR(testedApiVersion);'
2706        yield '\t// return true when tested api version is greater'
2707        yield '\t// or equal to version represented by two uints'
2708        yield '\tif (major == testedMajor)'
2709        yield '\t\treturn minor <= testedMinor;'
2710        yield '\treturn major < testedMajor;'
2711        yield '}\n'
2712        yield 'bool extensionIsSupported(const std::vector<std::string> extNames, const std::string& ext)'
2713        yield '{'
2714        yield '\tfor (const std::string& supportedExtension : extNames)'
2715        yield '\t{'
2716        yield '\t\tif (supportedExtension == ext) return true;'
2717        yield '\t}'
2718        yield '\treturn false;'
2719        yield '}\n'
2720
2721    lines = ['']
2722    lines.extend(genHelperFunctions())
2723    for line in writeExtensionFunctions(Function.TYPE_INSTANCE):
2724        lines += [line]
2725    lines += ['']
2726    for line in writeExtensionFunctions(Function.TYPE_DEVICE):
2727        lines += [line]
2728    lines += ['']
2729    for line in writeExtensionNameArrays():
2730        lines += [line]
2731
2732    writeInlFile(filename, INL_HEADER, lines)
2733
2734def writeCoreFunctionalities(api, filename):
2735    functionOriginValues = ["FUNCTIONORIGIN_PLATFORM", "FUNCTIONORIGIN_INSTANCE", "FUNCTIONORIGIN_DEVICE"]
2736
2737    functionNamesPerApiVersionDict = {}
2738    for feature in api.features:
2739        if api.apiName not in feature.api.split(','):
2740            continue
2741        apiVersion = "VK_API_VERSION_" + feature.number.replace('.', '_')
2742        functionNamesPerApiVersionDict[apiVersion] = []
2743        for r in feature.requirementsList:
2744            # skip optional promotions like for VK_EXT_host_image_copy
2745            if float(feature.number) > 1.35 and r.comment is not None and 'Promoted from ' not in r.comment:
2746                continue
2747            functionNamesPerApiVersionDict[apiVersion].extend(r.commandList)
2748
2749    lines = [
2750    "",
2751    'enum FunctionOrigin', '{'] + [line for line in indentLines([
2752    '\t' + functionOriginValues[0] + '\t= 0,',
2753    '\t' + functionOriginValues[1] + ',',
2754    '\t' + functionOriginValues[2]])] + [
2755    "};",
2756    "",
2757    "typedef ::std::pair<const char*, FunctionOrigin> FunctionInfo;",
2758    "typedef ::std::vector<FunctionInfo> FunctionInfosList;",
2759    "typedef ::std::map<uint32_t, FunctionInfosList> ApisMap;",
2760    "",
2761    "void initApisMap (ApisMap& apis)",
2762    "{",
2763    "    apis.clear();"] + [
2764    "    apis.insert(::std::pair<uint32_t, FunctionInfosList>(" + v + ", FunctionInfosList()));" for v in functionNamesPerApiVersionDict] + [
2765    ""]
2766
2767    apiVersions = []
2768    functionLines = []
2769    for apiVersion in functionNamesPerApiVersionDict:
2770        lines += [f'\tapis[{apiVersion}] = {{']
2771        # iterate over names of functions added with api
2772        for functionName in functionNamesPerApiVersionDict[apiVersion]:
2773            # search for data of this function in all functions list
2774            functionData = None
2775            for f in api.functions:
2776                if functionName == f.name or functionName in f.aliasList:
2777                    functionData = f
2778                    break
2779            if functionData == None:
2780                if api.apiName == "vulkansc":
2781                    continue
2782                # something went wrong, for "vulkan" functionData should always be found
2783                assert(False)
2784            # add line corresponding to this function
2785            functionLines.append('\t\t{"' + functionName + '",\t' + functionOriginValues[functionData.getType()] + '},')
2786        # indent all functions of specified api and add them to main list
2787        lines = lines + [line for line in indentLines(functionLines)] + ["\t};"]
2788
2789    lines = lines + ["}"]
2790    writeInlFile(filename, INL_HEADER, lines)
2791
2792def camelToSnake(name):
2793    name = re.sub('([a-z])([23])D([A-Z])', r'\1_\2d\3', name)
2794    name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
2795    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
2796
2797def writeDeviceFeatures2(api, filename):
2798
2799    def structInAPI(compositeObject):
2800        for c in api.compositeTypes:
2801            if c.name == compositeObject.name:
2802                return True
2803        return False
2804
2805    # helper class used to encapsulate all data needed during generation
2806    class StructureDetail:
2807        def __init__ (self, compositeObject):
2808            self.nameList = [compositeObject.name] + compositeObject.aliasList
2809            self.sType = compositeObject.members[0].values
2810            self.instanceName = 'd' + compositeObject.name[11:]
2811            self.flagName = 'is' + compositeObject.name[16:]
2812            self.extension = None
2813            self.api = None
2814            self.major = None
2815            self.minor = None
2816            structureMembers = compositeObject.members[2:]
2817            self.members = [m.name for m in structureMembers]
2818
2819    # helper extension class used in algorith below
2820    class StructureFoundContinueToNextOne(Exception):
2821        pass
2822
2823    # find structures that extend VkPhysicalDeviceFeatures2
2824    structures = [c for c in api.compositeTypes if c.structextends is not None and 'VkPhysicalDeviceFeatures2' in c.structextends]
2825    # remove structures that were added by extensions other than KHR and EXT
2826    testedStructures = []
2827    for s in structures:
2828        if all([postfix not in s.name for postfix in EXTENSION_POSTFIXES_VENDOR]):
2829            testedStructures.append(s)
2830
2831    existingStructures = list(filter(structInAPI, testedStructures)) # remove features not found in API ( important for Vulkan SC )
2832    testedStructureDetail = [StructureDetail(struct) for struct in existingStructures]
2833    # list of partially promoted extensions that are not marked in vk.xml as partially promoted in extension definition
2834    # note: for VK_EXT_host_image_copy there is a comment in require section for vk1.4
2835    partiallyPromotedExtensions = ['VK_EXT_pipeline_protected_access', 'VK_EXT_host_image_copy']
2836    # iterate over all searched structures and find extensions that enabled them
2837    for structureDetail in testedStructureDetail:
2838        try:
2839            # iterate over all extensions
2840            for extension in api.extensions:
2841                for requirement in extension.requirementsList:
2842                    for extensionStructure in requirement.newTypes:
2843                        if extensionStructure.name in structureDetail.nameList:
2844                            structureDetail.extension = extension.name
2845                            if extension.promotedto is not None and extension.partiallyPromoted is False and extension.name not in partiallyPromotedExtensions:
2846                                # check if extension was promoted to vulkan version or other extension;
2847                                if 'VK_VERSION' in extension.promotedto:
2848                                    versionSplit = extension.promotedto.split('_')
2849                                    structureDetail.api = 0 if api.apiName == "vulkan" else 1
2850                                    structureDetail.major = versionSplit[-2]
2851                                    structureDetail.minor = versionSplit[-1]
2852                                else:
2853                                    structureDetail.extension = extension.promotedto
2854                            raise StructureFoundContinueToNextOne
2855        except StructureFoundContinueToNextOne:
2856            continue
2857    structureDetailToRemove = []
2858    for structureDetail in testedStructureDetail:
2859        if structureDetail.major is not None or structureDetail.extension in partiallyPromotedExtensions:
2860            continue
2861        # if structure was not added with extension then check if
2862        # it was added directly with one of vulkan versions
2863        structureName = structureDetail.nameList[0]
2864        for feature in api.features:
2865            for requirement in feature.requirementsList:
2866                if structureName in requirement.typeList:
2867                    if api.apiName == "vulkansc" and int(feature.number[-1]) > 2:
2868                        structureDetailToRemove.append(structureDetail)
2869                    else:
2870                        versionSplit = feature.name.split('_')
2871                        structureDetail.api = 0 if api.apiName == "vulkan" else 1
2872                        structureDetail.major = versionSplit[-2]
2873                        structureDetail.minor = versionSplit[-1]
2874                    break
2875            if structureDetail.major is not None:
2876                break
2877    # remove structures that should not be tested for given api version
2878    for sd in structureDetailToRemove:
2879        testedStructureDetail.remove(sd)
2880    # generate file content
2881    structureDefinitions = []
2882    featureEnabledFlags = []
2883    clearStructures = []
2884    structureChain = []
2885    logStructures = []
2886    verifyStructures = []
2887    for index, structureDetail in enumerate(testedStructureDetail):
2888        structureName = structureDetail.nameList[0]
2889        # create two instances of each structure
2890        nameSpacing = '\t'
2891        structureDefinitions.append(structureName + nameSpacing + structureDetail.instanceName + '[count];')
2892        # create flags that check if proper extension or vulkan version is available
2893        condition = ''
2894        extension = structureDetail.extension
2895        major = structureDetail.major
2896        if extension is not None:
2897            condition = ' checkExtension(properties, "' + extension + '")'
2898        if major is not None:
2899            condition = ' ' if condition == '' else condition + ' || '
2900            condition += 'context.contextSupports(vk::ApiVersion(' + str(structureDetail.api) + ', ' + str(major) + ', ' + str(structureDetail.minor) + ', 0))'
2901        if condition == '':
2902            condition = ' true'
2903        condition += ';'
2904        nameSpacing = '\t' * int((len(structureName) - 4) / 4)
2905        featureEnabledFlags.append('const bool' + nameSpacing + structureDetail.flagName + ' =' + condition)
2906        # clear memory of each structure
2907        clearStructures.append('\tdeMemset(&' + structureDetail.instanceName + '[ndx], 0xFF * ndx, sizeof(' + structureName + '));')
2908        # construct structure chain
2909        nextInstanceName = 'nullptr';
2910        if index < len(testedStructureDetail)-1:
2911            nextInstanceName = '&' + testedStructureDetail[index+1].instanceName + '[ndx]'
2912        structureChain.append([
2913            '\t\t' + structureDetail.instanceName + '[ndx].sType = ' + structureDetail.flagName + ' ? ' + structureDetail.sType + ' : VK_STRUCTURE_TYPE_MAX_ENUM;',
2914            '\t\t' + structureDetail.instanceName + '[ndx].pNext = nullptr;'])
2915        # construct log section
2916        logStructures.append([
2917            '\tif (' + structureDetail.flagName + ')',
2918            '\t\tlog << TestLog::Message << ' + structureDetail.instanceName + '[0] << TestLog::EndMessage;'
2919            ])
2920        #construct verification section
2921        verifyStructure = []
2922        verifyStructure.append('\tif (' + structureDetail.flagName + ' &&')
2923        for index, m in enumerate(structureDetail.members):
2924            prefix = '\t\t(' if index == 0 else '\t\t '
2925            postfix = '))' if index == len(structureDetail.members)-1 else ' ||'
2926            verifyStructure.append(prefix + structureDetail.instanceName + '[0].' + m + ' != ' + structureDetail.instanceName + '[1].' + m + postfix)
2927        if len(structureDetail.members) == 0:
2928            verifyStructure.append('\t\tfalse)')
2929        verifyStructure.append('\t{\n\t\tTCU_FAIL("Mismatch between ' + structureName + '");\n\t}')
2930        verifyStructures.append(verifyStructure)
2931
2932    # construct file content
2933    stream = []
2934
2935    # individual test functions
2936    for n, x in enumerate(testedStructureDetail):
2937        stream.append("tcu::TestStatus testPhysicalDeviceFeature" + x.instanceName[len('device'):]+" (Context& context)")
2938        stream.append("""{
2939    const VkPhysicalDevice        physicalDevice = context.getPhysicalDevice();
2940    const CustomInstance        instance        (createCustomInstanceWithExtension(context, "VK_KHR_get_physical_device_properties2"));
2941    const InstanceDriver&        vki                (instance.getDriver());
2942    const int                    count = 2u;
2943    TestLog&                    log = context.getTestContext().getLog();
2944    VkPhysicalDeviceFeatures2    extFeatures;
2945    vector<VkExtensionProperties> properties = enumerateDeviceExtensionProperties(vki, physicalDevice, nullptr);
2946""")
2947        stream.append("\t"+structureDefinitions[n])
2948        stream.append("\t"+featureEnabledFlags[n])
2949        stream.append('')
2950        stream.append('\tfor (int ndx = 0; ndx < count; ++ndx)\n\t{')
2951        stream.append("\t" + clearStructures[n])
2952        stream.extend(structureChain[n])
2953        stream.append('')
2954        stream.append(
2955                '\t\tdeMemset(&extFeatures.features, 0xcd, sizeof(extFeatures.features));\n'
2956                '\t\textFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;\n'
2957                '\t\textFeatures.pNext = &' + testedStructureDetail[n].instanceName + '[ndx];\n\n'
2958                '\t\tvki.getPhysicalDeviceFeatures2(physicalDevice, &extFeatures);')
2959        stream.append('\t}\n')
2960        stream.extend(logStructures[n])
2961        stream.append('')
2962        stream.extend(verifyStructures[n])
2963        stream.append('\treturn tcu::TestStatus::pass("Querying succeeded");')
2964        stream.append("}\n")
2965
2966    allApiVersions = [f.number for f in api.features]
2967    promotedTests = []
2968    if api.apiName == "vulkan":
2969        for feature in api.features:
2970            if api.apiName not in feature.api.split(','):
2971                continue
2972            major = feature.number[0]
2973            minor = feature.number[-1]
2974            promotedFeatures = []
2975            if feature.name == 'VK_VERSION_1_0':
2976                continue
2977            for requirement in feature.requirementsList:
2978                for type in requirement.typeList:
2979                    matchedStructType = re.search(r'VkPhysicalDevice(\w+)Features', type, re.IGNORECASE)
2980                    matchedCoreStructType = re.search(r'VkPhysicalDeviceVulkan(\d+)Features', type, re.IGNORECASE)
2981                    if matchedStructType and not matchedCoreStructType:
2982                        promotedFeatures.append(type)
2983            if promotedFeatures:
2984                testName = "createDeviceWithPromoted" + feature.number.replace('.', '') + "Structures"
2985                promotedTests.append(testName)
2986                stream.append("tcu::TestStatus " + testName + " (Context& context)")
2987                stream.append("{")
2988                stream.append(
2989                '    if (!context.contextSupports(vk::ApiVersion(0, ' + major + ', ' + minor + ', 0)))\n'
2990                '        TCU_THROW(NotSupportedError, "Vulkan ' + major + '.' + minor + ' is not supported");')
2991                stream.append("""
2992    const PlatformInterface&        platformInterface = context.getPlatformInterface();
2993    const CustomInstance            instance            (createCustomInstanceFromContext(context));
2994    const InstanceDriver&            instanceDriver        (instance.getDriver());
2995    const VkPhysicalDevice            physicalDevice = chooseDevice(instanceDriver, instance, context.getTestContext().getCommandLine());
2996    const uint32_t                    queueFamilyIndex = 0;
2997    const uint32_t                    queueCount = 1;
2998    const uint32_t                    queueIndex = 0;
2999    const float                        queuePriority = 1.0f;
3000
3001    const vector<VkQueueFamilyProperties> queueFamilyProperties = getPhysicalDeviceQueueFamilyProperties(instanceDriver, physicalDevice);
3002
3003    const VkDeviceQueueCreateInfo    deviceQueueCreateInfo =
3004    {
3005        VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
3006        nullptr,
3007        (VkDeviceQueueCreateFlags)0u,
3008        queueFamilyIndex, //queueFamilyIndex;
3009        queueCount, //queueCount;
3010        &queuePriority, //pQueuePriorities;
3011    };
3012""")
3013                lastFeature = ''
3014                usedFeatures = []
3015                for feature in promotedFeatures:
3016                    for struct in testedStructureDetail:
3017                        if (struct.instanceName in usedFeatures):
3018                            continue
3019                        if feature in struct.nameList:
3020                            if lastFeature:
3021                                stream.append("\t" + feature + " " + struct.instanceName + " = initVulkanStructure(&" + lastFeature + ");")
3022                            else:
3023                                stream.append("\t" + feature + " " + struct.instanceName + " = initVulkanStructure();")
3024                            lastFeature = struct.instanceName
3025                            usedFeatures.append(struct.instanceName)
3026                            break
3027                stream.append("\tVkPhysicalDeviceFeatures2 extFeatures = initVulkanStructure(&" + lastFeature + ");")
3028                stream.append("""
3029    instanceDriver.getPhysicalDeviceFeatures2 (physicalDevice, &extFeatures);
3030
3031    const VkDeviceCreateInfo        deviceCreateInfo =
3032    {
3033        VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, //sType;
3034        &extFeatures, //pNext;
3035        (VkDeviceCreateFlags)0u,
3036        1, //queueRecordCount;
3037        &deviceQueueCreateInfo, //pRequestedQueues;
3038        0, //layerCount;
3039        nullptr, //ppEnabledLayerNames;
3040        0, //extensionCount;
3041        nullptr, //ppEnabledExtensionNames;
3042        nullptr, //pEnabledFeatures;
3043    };
3044
3045    const Unique<VkDevice>            device            (createCustomDevice(context.getTestContext().getCommandLine().isValidationEnabled(), platformInterface, instance, instanceDriver, physicalDevice, &deviceCreateInfo));
3046    const DeviceDriver                deviceDriver    (platformInterface, instance, device.get(), context.getUsedApiVersion(), context.getTestContext().getCommandLine());
3047    const VkQueue                    queue = getDeviceQueue(deviceDriver, *device, queueFamilyIndex, queueIndex);
3048
3049    VK_CHECK(deviceDriver.queueWaitIdle(queue));
3050
3051    return tcu::TestStatus::pass("Pass");
3052}
3053""")
3054
3055    # function to create tests
3056    stream.append("void addSeparateFeatureTests (tcu::TestCaseGroup* testGroup)\n{")
3057    for x in testedStructureDetail:
3058        stream.append('\taddFunctionCase(testGroup, "' + camelToSnake(x.instanceName[len('device'):]) + '", testPhysicalDeviceFeature' + x.instanceName[len('device'):] + ');')
3059    for x in promotedTests:
3060        stream.append('\taddFunctionCase(testGroup, "' + camelToSnake(x) + '", ' + x + ');')
3061    stream.append('}\n')
3062
3063    # write out
3064    writeInlFile(filename, INL_HEADER, stream)
3065
3066class FeaturesOrPropertiesDefs:
3067    def __init__ (self, structureType, structureTypeName):
3068        self.structureType = structureType           # string with most important part from structure type e.g. 'LINE_RASTERIZATION'
3069                                                     #   (for VkPhysicalDeviceLineRasterizationFeaturesEXT) without prefix nor postfix
3070        self.verSuffix = ''                          # string containing version, this is needed to handle corner case like e.g.
3071                                                     #   VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD where 2 is after PROPERTIES
3072        self.extSuffix = ''                          # string with extension type e.g. '_EXT' for VkPhysicalDeviceLineRasterizationFeaturesEXT
3073        self.structureTypeName = structureTypeName   # full structure name e.g. 'VkPhysicalDeviceCustomBorderColorFeaturesEXT'
3074        self.extensionName = None                    # name of extension that added this structure eg. 'VK_EXT_depth_clip_enable'
3075        self.nameString = None                       # e.g. 'VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME'
3076        self.versionString = '0'                     # e.g. 'VK_EXT_SHADER_ATOMIC_FLOAT_SPEC_VERSION'
3077        self.compositeType = None                    # None or pointer to composite type
3078    def __iter__(self):
3079        return iter((self.structureType, self.verSuffix, self.extSuffix, self.structureTypeName, self.extensionName, self.nameString, self.versionString))
3080
3081def generateDeviceFeaturesOrPropertiesDefs(api, FeaturesOrProperties):
3082    assert(FeaturesOrProperties in ['Features', 'Properties'])
3083    defs = []
3084    foundStructureEnums = []
3085    structureEnumPattern = fr'VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_(\w+)_{FeaturesOrProperties.upper()}(\w+)'
3086    structureEnumPatternNotExtension = structureEnumPattern[:-5] + '$'
3087    structureTypePattern = fr'VkPhysicalDevice(\w+){FeaturesOrProperties}(\w+)'
3088    structureTypePatternNotExtension = structureTypePattern[:-5] + '$'
3089    structureTypeToSkipPattern = fr'VkPhysicalDeviceVulkan\d\d{FeaturesOrProperties}'
3090    structureExtendsPattern = f'VkPhysicalDevice{FeaturesOrProperties}2'
3091    # iterate over all extensions to find extension that adds enum value matching pattern;
3092    # this will always be in first requirement section
3093    for ext in api.extensions:
3094        # skip extensions that were promoted to other extensions (not vk version)
3095        if ext.promotedto is not None and "VK_VERSION" not in ext.promotedto:
3096            continue
3097        allExtendedEnums = ext.requirementsList[0].extendedEnums
3098        for extendedEnum in allExtendedEnums:
3099            matchedStructEnum = re.search(structureEnumPattern, extendedEnum.name, re.IGNORECASE)
3100            if matchedStructEnum:
3101                # find feature/property structure type name
3102                structureTypeName = ""
3103                for stRequirement in ext.requirementsList[0].newTypes:
3104                    stName = stRequirement.name
3105                    matchedStructType = re.search(structureTypePattern, stName, re.IGNORECASE)
3106                    if matchedStructType:
3107                        structureTypeName = stName
3108                        break
3109                # iterate over all composite types to check if structureTypeName is not alias
3110                # this handles case where extension was promoted and with it feature/property structure
3111                structureType = None
3112                for ct in api.compositeTypes:
3113                    if structureTypeName == ct.name:
3114                        structureType = ct
3115                        break
3116                    elif structureTypeName in ct.aliasList:
3117                        structureType = ct
3118                        structureTypeName = structureType.name
3119                        break
3120                # use data in structextends to skip structures that should not be passed to vkGetPhysicalDeviceProperties(/Features)2 function
3121                if structureType is None or structureType.structextends is None or structureExtendsPattern not in structureType.structextends:
3122                    continue
3123                # meke sure that structure was not added earlier - this handles special
3124                # cases like VkPhysicalDeviceIDPropertiesKHR added by 3 extensions
3125                if len([d for d in defs if d.structureTypeName == structureTypeName]) > 0:
3126                    continue
3127                foundStructureEnums.append(matchedStructEnum.group(1))
3128                fop = FeaturesOrPropertiesDefs(matchedStructEnum.group(1), structureTypeName)
3129                fop.extensionName = ext.name
3130                fop.nameString    = allExtendedEnums[1].name
3131                fop.versionString = allExtendedEnums[0].name
3132                fop.compositeType = structureType
3133                defs.append(fop)
3134                # there are cases like VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD
3135                # where 2 is after PROPERTIES - to handle this we need to split suffix to two parts
3136                fop.extSuffix = matchedStructEnum.group(2)
3137                suffixStart = fop.extSuffix.rfind('_')
3138                if suffixStart > 0:
3139                    fop.verSuffix = fop.extSuffix[:suffixStart]
3140                    fop.extSuffix = fop.extSuffix[suffixStart:]
3141                # accept single feature/property structure per extension - this also handles cases
3142                # like VK_KHR_variable_pointers which specify feature structure and its alias
3143                break
3144
3145    # iterate over all structures to find Feature/Property structures that were not added with extension
3146    # but with vulkan version; to do that we need to skip extension part from pattern
3147    for ct in api.compositeTypes:
3148        matchedStructType = re.search(structureTypePatternNotExtension, ct.name, re.IGNORECASE)
3149        if matchedStructType:
3150            if ct.members[0].name != "sType":
3151                continue
3152            if ct.structextends is None or structureExtendsPattern not in ct.structextends:
3153                continue
3154            matchedStructEnum = re.search(structureEnumPatternNotExtension, ct.members[0].values, re.IGNORECASE)
3155            if (matchedStructEnum.group(1) not in foundStructureEnums) and (re.match(structureTypeToSkipPattern, ct.name) == None):
3156                defs.append(FeaturesOrPropertiesDefs(matchedStructEnum.group(1), ct.name))
3157    return defs
3158
3159def writeDeviceFeatures(api, dfDefs, filename):
3160    # find VkPhysicalDeviceVulkan[1-9][0-9]Features blob structurs
3161    # and construct dictionary with all of their attributes
3162    blobMembers = {}
3163    blobStructs = {}
3164    blobPattern = re.compile("^VkPhysicalDeviceVulkan([1-9][0-9])Features[0-9]*$")
3165    for structureType in api.compositeTypes:
3166        match = blobPattern.match(structureType.name)
3167        if match:
3168            allMembers = [member.name for member in structureType.members]
3169            vkVersion = match.group(1)
3170            blobMembers[vkVersion] = allMembers[2:]
3171            blobStructs[vkVersion] = set()
3172    initFromBlobDefinitions = []
3173    emptyInitDefinitions = []
3174    # iterate over all feature structures
3175    allFeaturesPattern = re.compile(r"^VkPhysicalDevice\w+Features[1-9]*")
3176    nonExtFeaturesPattern = re.compile(r"^VkPhysicalDevice\w+Features[1-9]*$")
3177    for structureType in api.compositeTypes:
3178        # skip structures that are not feature structures
3179        if not allFeaturesPattern.match(structureType.name):
3180            continue
3181        # skip structures that were previously identified as blobs
3182        if blobPattern.match(structureType.name):
3183            continue
3184        # skip sType and pNext and just grab third and next attributes
3185        structureMembers = structureType.members[2:]
3186        notPartOfBlob = True
3187        if nonExtFeaturesPattern.match(structureType.name):
3188            # check if this member is part of any of the blobs
3189            for blobName, blobMemberList in blobMembers.items():
3190                # if just one member is not part of this blob go to the next blob
3191                # (we asume that all members are part of blob - no need to check all)
3192                if structureMembers[0].name not in blobMemberList:
3193                    continue
3194                # add another feature structure name to this blob
3195                blobStructs[blobName].add(structureType)
3196                # add specialization for this feature structure
3197                memberCopying = ""
3198                for member in structureMembers:
3199                    memberCopying += "\tfeatureType.{0} = allFeaturesBlobs.vk{1}.{0};\n".format(member.name, blobName)
3200                wholeFunction = \
3201                    "template<> void initFeatureFromBlob<{0}>({0}& featureType, const AllFeaturesBlobs& allFeaturesBlobs)\n" \
3202                    "{{\n" \
3203                    "{1}" \
3204                    "}}".format(structureType.name, memberCopying)
3205                initFromBlobDefinitions.append(wholeFunction)
3206                notPartOfBlob = False
3207                # assuming that all members are part of blob, goto next
3208                break
3209        # add empty template definition as on Fedora there are issue with
3210        # linking using just generic template - all specializations are needed
3211        if notPartOfBlob:
3212            emptyFunction = "template<> void initFeatureFromBlob<{0}>({0}&, const AllFeaturesBlobs&) {{}}"
3213            emptyInitDefinitions.append(emptyFunction.format(structureType.name))
3214    extensionDefines = []
3215    makeFeatureDescDefinitions = []
3216    featureStructWrappers = []
3217    for idx, (sType, sVerSuffix, sExtSuffix, extStruct, _, extNameDef, specVersionDef) in enumerate(dfDefs):
3218        extensionNameDefinition = extNameDef
3219        if not extensionNameDefinition:
3220            extensionNameDefinition = 'DECL{0}_{1}_EXTENSION_NAME'.format((sExtSuffix if sExtSuffix else ''), sType)
3221            extensionDefines.append(f'#define {extensionNameDefinition} "core_feature"')
3222        # construct makeFeatureDesc template function definitions
3223        sTypeName = "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_{0}_FEATURES{1}".format(sType, sVerSuffix + sExtSuffix)
3224        makeFeatureDescDefinitions.append("template<> FeatureDesc makeFeatureDesc<{0}>(void) " \
3225                                          "{{ return FeatureDesc{{{1}, {2}, {3}}}; }}".format(extStruct, sTypeName, extensionNameDefinition, specVersionDef))
3226        # construct CreateFeatureStruct wrapper block
3227        featureStructWrappers.append("\t{{ createFeatureStructWrapper<{0}>, {1}, {2} }},".format(extStruct, extensionNameDefinition, specVersionDef))
3228    # construct function that will check for which vk version structure sType is part of blob
3229    blobChecker = "uint32_t getBlobFeaturesVersion (VkStructureType sType)\n{\n" \
3230                  "\tconst std::map<VkStructureType, uint32_t> sTypeBlobMap\n" \
3231                  "\t{\n"
3232    # iterate over blobs with list of structures
3233    for blobName in sorted(blobStructs.keys()):
3234        blobChecker += "\t\t// Vulkan{0}\n".format(blobName)
3235        # iterate over all feature structures in current blob
3236        structuresList = list(blobStructs[blobName])
3237        structuresList = sorted(structuresList, key=lambda s: s.name)
3238        for structType in structuresList:
3239            # find definition of this structure in dfDefs
3240            structDef = None
3241            allNamesToCheck = [structType.name]
3242            if len(structType.aliasList) > 0:
3243                allNamesToCheck.extend(structType.aliasList)
3244            for structName in allNamesToCheck:
3245                structDefList = [s for s in dfDefs if s.structureTypeName == structName]
3246                if len(structDefList) > 0:
3247                    structDef = structDefList[0]
3248                    break
3249            sType = structDef.structureType
3250            sSuffix = structDef.verSuffix + structDef.extSuffix
3251            sTypeName = "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_{0}_FEATURES{1}".format(sType, sSuffix)
3252            tabs = "\t" * int((88 - len(sTypeName)) / 4)
3253            blobChecker += "\t\t{{ {0},{1}VK_API_VERSION_{2}_{3} }},\n".format(sTypeName, tabs, blobName[0], blobName[1])
3254    blobChecker += "\t};\n\n" \
3255                   "\tauto it = sTypeBlobMap.find(sType);\n" \
3256                   "\tif(it == sTypeBlobMap.end())\n" \
3257                   "\t\treturn 0;\n" \
3258                   "\treturn it->second;\n" \
3259                   "}\n"
3260    # combine all definition lists
3261    stream = [
3262    '#include "vkDeviceFeatures.hpp"\n',
3263    'namespace vk\n{']
3264    stream.extend(extensionDefines)
3265    stream.append('\n')
3266    stream.extend(initFromBlobDefinitions)
3267    stream.append('\n// generic template is not enough for some compilers')
3268    stream.extend(emptyInitDefinitions)
3269    stream.append('\n')
3270    stream.extend(makeFeatureDescDefinitions)
3271    stream.append('\n')
3272    stream.append('static const FeatureStructCreationData featureStructCreationArray[]\n{')
3273    stream.extend(featureStructWrappers)
3274    stream.append('};\n')
3275    stream.append(blobChecker)
3276    stream.append('} // vk\n')
3277    writeInlFile(filename, INL_HEADER, stream)
3278
3279def writeDeviceFeatureTest(api, filename):
3280
3281    coreFeaturesPattern = re.compile("^VkPhysicalDeviceVulkan([1-9][0-9])Features[0-9]*$")
3282    featureItems = []
3283    testFunctions = []
3284    # iterate over all feature structures
3285    allFeaturesPattern = re.compile(r"^VkPhysicalDevice\w+Features[1-9]*")
3286    for structureType in api.compositeTypes:
3287        # skip structures that are not feature structures
3288        if not allFeaturesPattern.match(structureType.name):
3289            continue
3290        # skip sType and pNext and just grab third and next attributes
3291        structureMembers = structureType.members[2:]
3292
3293        items = []
3294        for member in structureMembers:
3295            items.append("        FEATURE_ITEM ({0}, {1}),".format(structureType.name, member.name))
3296
3297        testBlock = """
3298tcu::TestStatus createDeviceWithUnsupportedFeaturesTest{4} (Context& context)
3299{{
3300    const PlatformInterface&                vkp = context.getPlatformInterface();
3301    tcu::TestLog&                            log = context.getTestContext().getLog();
3302    tcu::ResultCollector                    resultCollector            (log);
3303    const CustomInstance                    instance                (createCustomInstanceWithExtensions(context, context.getInstanceExtensions(), nullptr, true));
3304    const InstanceDriver&                    instanceDriver            (instance.getDriver());
3305    const VkPhysicalDevice                    physicalDevice = chooseDevice(instanceDriver, instance, context.getTestContext().getCommandLine());
3306    const uint32_t                            queueFamilyIndex = 0;
3307    const uint32_t                            queueCount = 1;
3308    const float                                queuePriority = 1.0f;
3309    const DeviceFeatures                    deviceFeaturesAll        (context.getInstanceInterface(), context.getUsedApiVersion(), physicalDevice, context.getInstanceExtensions(), context.getDeviceExtensions(), true);
3310    const VkPhysicalDeviceFeatures2            deviceFeatures2 = deviceFeaturesAll.getCoreFeatures2();
3311    int                                        numErrors = 0;
3312    const tcu::CommandLine&                    commandLine = context.getTestContext().getCommandLine();
3313    bool                                    isSubProcess = context.getTestContext().getCommandLine().isSubProcess();
3314{6}
3315
3316    VkPhysicalDeviceFeatures emptyDeviceFeatures;
3317    deMemset(&emptyDeviceFeatures, 0, sizeof(emptyDeviceFeatures));
3318
3319    // Only non-core extensions will be used when creating the device.
3320    const auto& extensionNames = context.getDeviceCreationExtensions();
3321    DE_UNREF(extensionNames); // In some cases this is not used.
3322
3323    if (const void* featuresStruct = findStructureInChain(const_cast<const void*>(deviceFeatures2.pNext), getStructureType<{0}>()))
3324    {{
3325        static const Feature features[] =
3326        {{
3327{1}
3328        }};
3329        auto* supportedFeatures = reinterpret_cast<const {0}*>(featuresStruct);
3330        checkFeatures(vkp, instance, instanceDriver, physicalDevice, {2}, features, supportedFeatures, queueFamilyIndex, queueCount, queuePriority, numErrors, resultCollector, {3}, emptyDeviceFeatures, {5}, context.getUsedApiVersion(), commandLine);
3331    }}
3332
3333    if (numErrors > 0)
3334        return tcu::TestStatus(resultCollector.getResult(), "Enabling unsupported features didn't return VK_ERROR_FEATURE_NOT_PRESENT.");
3335
3336    return tcu::TestStatus(resultCollector.getResult(), resultCollector.getMessage());
3337}}
3338"""
3339        additionalParams = ( 'memReservationStatMax, isSubProcess' if api.apiName == 'vulkansc' else 'isSubProcess' )
3340        additionalDefs = ( '    VkDeviceObjectReservationCreateInfo memReservationStatMax = context.getResourceInterface()->getStatMax();' if apiName == 'vulkansc' else '')
3341        featureItems.append(testBlock.format(structureType.name, "\n".join(items), len(items), ("nullptr" if coreFeaturesPattern.match(structureType.name) else "&extensionNames"), structureType.name[len('VkPhysicalDevice'):], additionalParams, additionalDefs))
3342
3343        testFunctions.append("createDeviceWithUnsupportedFeaturesTest" + structureType.name[len('VkPhysicalDevice'):])
3344
3345    stream = ['']
3346    stream.extend(featureItems)
3347    stream.append("""
3348void addSeparateUnsupportedFeatureTests (tcu::TestCaseGroup* testGroup)
3349{
3350""")
3351    for x in testFunctions:
3352        stream.append('\taddFunctionCase(testGroup, "' + camelToSnake(x[len('createDeviceWithUnsupportedFeaturesTest'):]) + '", ' + x + ');')
3353    stream.append('}\n')
3354
3355    writeInlFile(filename, INL_HEADER, stream)
3356
3357def writeDeviceProperties(api, dpDefs, filename):
3358    # find VkPhysicalDeviceVulkan[1-9][0-9]Features blob structurs
3359    # and construct dictionary with all of their attributes
3360    blobMembers = {}
3361    blobStructs = {}
3362    blobPattern = re.compile("^VkPhysicalDeviceVulkan([1-9][0-9])Properties[0-9]*$")
3363    for structureType in api.compositeTypes:
3364        match = blobPattern.match(structureType.name)
3365        if match:
3366            allMembers = [member.name for member in structureType.members]
3367            vkVersion = match.group(1)
3368            blobMembers[vkVersion] = allMembers[2:]
3369            blobStructs[vkVersion] = set()
3370    initFromBlobDefinitions = []
3371    emptyInitDefinitions = []
3372    # iterate over all property structures
3373    allPropertiesPattern = re.compile(r"^VkPhysicalDevice\w+Properties[1-9]*")
3374    nonExtPropertiesPattern = re.compile(r"^VkPhysicalDevice\w+Properties[1-9]*$")
3375    for structureType in api.compositeTypes:
3376        # skip structures that are not property structures
3377        if not allPropertiesPattern.match(structureType.name):
3378            continue
3379        # skip structures that were previously identified as blobs
3380        if blobPattern.match(structureType.name):
3381            continue
3382        # skip sType and pNext and just grab third and next attributes
3383        structureMembers = structureType.members[2:]
3384        notPartOfBlob = True
3385        if nonExtPropertiesPattern.match(structureType.name):
3386            # check if this member is part of any of the blobs
3387            for blobName, blobMemberList in blobMembers.items():
3388                # if just one member is not part of this blob go to the next blob
3389                # (we asume that all members are part of blob - no need to check all)
3390                if structureMembers[0].name not in blobMemberList:
3391                    continue
3392                # add another property structure name to this blob
3393                blobStructs[blobName].add(structureType)
3394                # add specialization for this property structure
3395                memberCopying = ""
3396                for member in structureMembers:
3397                    if len(member.arraySizeList) == 0:
3398                        # handle special case
3399                        if structureType.name == "VkPhysicalDeviceSubgroupProperties" and "subgroup" not in member.name :
3400                            blobMemberName = "subgroup" + member.name[0].capitalize() + member.name[1:]
3401                            memberCopying += "\tpropertyType.{0} = allPropertiesBlobs.vk{1}.{2};\n".format(member.name, blobName, blobMemberName)
3402                        # end handling special case
3403                        else:
3404                            memberCopying += "\tpropertyType.{0} = allPropertiesBlobs.vk{1}.{0};\n".format(member.name, blobName)
3405                    else:
3406                        memberCopying += "\tmemcpy(propertyType.{0}, allPropertiesBlobs.vk{1}.{0}, sizeof({2}) * {3});\n".format(member.name, blobName, member.type, member.arraySizeList[0])
3407                wholeFunction = \
3408                    "template<> void initPropertyFromBlob<{0}>({0}& propertyType, const AllPropertiesBlobs& allPropertiesBlobs)\n" \
3409                    "{{\n" \
3410                    "{1}" \
3411                    "}}".format(structureType.name, memberCopying)
3412                initFromBlobDefinitions.append(wholeFunction)
3413                notPartOfBlob = False
3414                # assuming that all members are part of blob, goto next
3415                break
3416        # add empty template definition as on Fedora there are issue with
3417        # linking using just generic template - all specializations are needed
3418        if notPartOfBlob:
3419            emptyFunction = "template<> void initPropertyFromBlob<{0}>({0}&, const AllPropertiesBlobs&) {{}}"
3420            emptyInitDefinitions.append(emptyFunction.format(structureType.name))
3421    extensionDefines = []
3422    makePropertyDescDefinitions = []
3423    propertyStructWrappers = []
3424    for idx, (sType, sVerSuffix, sExtSuffix, extStruct, _, extNameDef, specVersionDef) in enumerate(dpDefs):
3425        extensionNameDefinition = extNameDef
3426        if not extensionNameDefinition:
3427            extensionNameDefinition = 'DECL{0}_{1}_EXTENSION_NAME'.format((sExtSuffix if sExtSuffix else ''), sType)
3428            extensionDefines.append(f'#define {extensionNameDefinition} "core_property"')
3429        # construct makePropertyDesc template function definitions
3430        sTypeName = "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_{0}_PROPERTIES{1}".format(sType, sVerSuffix + sExtSuffix)
3431        makePropertyDescDefinitions.append("template<> PropertyDesc makePropertyDesc<{0}>(void) " \
3432                                           "{{ return PropertyDesc{{{1}, {2}, {3}}}; }}".format(extStruct, sTypeName, extensionNameDefinition, specVersionDef))
3433        # construct CreateProperty struct wrapper block
3434        propertyStructWrappers.append("\t{{ createPropertyStructWrapper<{0}>, {1}, {2} }},".format(extStruct, extensionNameDefinition, specVersionDef))
3435    # construct method that will check if structure sType is part of blob
3436    blobChecker = "uint32_t getBlobPropertiesVersion (VkStructureType sType)\n{\n" \
3437                  "\tconst std::map<VkStructureType, uint32_t> sTypeBlobMap\n" \
3438                  "\t{\n"
3439    # iterate over blobs with list of structures
3440    for blobName in sorted(blobStructs.keys()):
3441        blobChecker += "\t\t// Vulkan{0}\n".format(blobName)
3442        # iterate over all feature structures in current blob
3443        structuresList = list(blobStructs[blobName])
3444        structuresList = sorted(structuresList, key=lambda s: s.name)
3445        for structType in structuresList:
3446            # find definition of this structure in dpDefs
3447            structName = structType.name
3448            structDef = None
3449            foundDefs = [s for s in dpDefs if s.structureTypeName == structName]
3450            if len(foundDefs) > 0:
3451                structDef = foundDefs[0]
3452            else:
3453                for alias in structType.aliasList:
3454                    foundDefs = [s for s in dpDefs if s.structureTypeName == alias]
3455                    if len(foundDefs) > 0:
3456                        structDef = foundDefs[0]
3457                        break
3458            sType = structDef.structureType
3459            sSuffix = structDef.verSuffix + structDef.extSuffix
3460            sTypeName = "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_{0}_PROPERTIES{1}".format(sType, sSuffix)
3461            tabs = "\t" * int((80 - len(sTypeName)) / 4)
3462            blobChecker += "\t\t{{ {0},{1}VK_API_VERSION_{2}_{3} }},\n".format(sTypeName, tabs, blobName[0], blobName[1])
3463    blobChecker += "\t};\n\n" \
3464                   "\tauto it = sTypeBlobMap.find(sType);\n" \
3465                   "\tif(it == sTypeBlobMap.end())\n" \
3466                   "\t\treturn 0;\n" \
3467                   "\treturn it->second;\n" \
3468                   "}\n"
3469    # combine all definition lists
3470    stream = [
3471    '#include "vkDeviceProperties.hpp"\n',
3472    'namespace vk\n{']
3473    stream.extend(extensionDefines)
3474    stream.append('\n')
3475    stream.extend(initFromBlobDefinitions)
3476    stream.append('\n// generic template is not enough for some compilers')
3477    stream.extend(emptyInitDefinitions)
3478    stream.append('\n')
3479    stream.extend(makePropertyDescDefinitions)
3480    stream.append('\n')
3481    stream.append('static const PropertyStructCreationData propertyStructCreationArray[] =\n{')
3482    stream.extend(propertyStructWrappers)
3483    stream.append('};\n')
3484    stream.append(blobChecker)
3485    stream.append('} // vk\n')
3486    writeInlFile(filename, INL_HEADER, stream)
3487
3488def genericDeviceFeaturesOrPropertiesWriter(dfDefs, pattern, filename):
3489    UNSUFFIXED_STRUCTURES = [
3490        "CornerSampledImage",
3491        "ShaderSMBuiltins",
3492        "ShadingRateImage",
3493        "RayTracing",
3494        "RepresentativeFragmentTest",
3495        "ComputeShaderDerivatives",
3496        "MeshShader",
3497        "ShaderImageFootprint",
3498        "ExclusiveScissor",
3499        "DedicatedAllocationImageAliasing",
3500        "CoverageReductionMode",
3501        "DeviceGeneratedCommands",
3502        "InheritedViewportScissor",
3503        "PresentBarrier",
3504        "DiagnosticsConfig",
3505        "FragmentShadingRateEnums",
3506        "RayTracingMotionBlur",
3507        "ExternalMemoryRDMA",
3508        "CopyMemoryIndirect",
3509        "MemoryDecompression",
3510        "LinearColorAttachment",
3511        "OpticalFlow",
3512        "RayTracingInvocationReorder",
3513        "DisplacementMicromap"]
3514    stream = []
3515    for fop in dfDefs:
3516        # remove VkPhysicalDevice prefix from structure name
3517        nameSubStr = fop.structureTypeName[16:]
3518        # remove extension type in some cases
3519        if nameSubStr[-3:] == "KHR":
3520            nameSubStr = nameSubStr[:-3]
3521        elif fop.compositeType and fop.compositeType.notSupportedAlias:
3522            # remove KHR also for extensions that were promoted in Vulkan but
3523            # not in VulkanSC this reduces number of ifdefs for SC in CTS code
3524            nameSubStr = nameSubStr[:-3]
3525        elif nameSubStr[-2:] == "NV":
3526            suffix = nameSubStr[-2:]
3527            nameSubStr = nameSubStr[:-2]
3528            if nameSubStr[-8:] == "Features":
3529                infix = nameSubStr[-8:]
3530                nameSubStr = nameSubStr[:-8]
3531            elif nameSubStr[-10:] == "Properties":
3532                infix = nameSubStr[-10:]
3533                nameSubStr = nameSubStr[:-10]
3534            if (nameSubStr in UNSUFFIXED_STRUCTURES):
3535                suffix = ""
3536            nameSubStr = nameSubStr + infix + suffix
3537        stream.append(pattern.format(fop.structureTypeName, nameSubStr))
3538    writeInlFile(filename, INL_HEADER, indentLines(stream))
3539
3540def writeDeviceFeaturesDefaultDeviceDefs(dfDefs, filename):
3541    pattern = "const {0}&\tget{1}\t(void) const {{ return m_deviceFeatures.getFeatureType<{0}>();\t}}"
3542    genericDeviceFeaturesOrPropertiesWriter(dfDefs, pattern, filename)
3543
3544def writeDeviceFeaturesContextDecl(dfDefs, filename):
3545    pattern = "const vk::{0}&\tget{1}\t(void) const;"
3546    genericDeviceFeaturesOrPropertiesWriter(dfDefs, pattern, filename)
3547
3548def writeDeviceFeaturesContextDefs(dfDefs, filename):
3549    pattern = "const vk::{0}&\tContext::get{1}\t(void) const {{ return m_device->get{1}();\t}}"
3550    genericDeviceFeaturesOrPropertiesWriter(dfDefs, pattern, filename)
3551
3552def writeDevicePropertiesDefaultDeviceDefs(dfDefs, filename):
3553    pattern = "const {0}&\tget{1}\t(void) const {{ return m_deviceProperties.getPropertyType<{0}>();\t}}"
3554    genericDeviceFeaturesOrPropertiesWriter(dfDefs, pattern, filename)
3555
3556def writeDevicePropertiesContextDecl(dfDefs, filename):
3557    pattern = "const vk::{0}&\tget{1}\t(void) const;"
3558    genericDeviceFeaturesOrPropertiesWriter(dfDefs, pattern, filename)
3559
3560def writeDevicePropertiesContextDefs(dfDefs, filename):
3561    pattern = "const vk::{0}&\tContext::get{1}\t(void) const {{ return m_device->get{1}();\t}}"
3562    genericDeviceFeaturesOrPropertiesWriter(dfDefs, pattern, filename)
3563
3564def writeMandatoryFeatures(api, filename):
3565
3566    def structInAPI(name):
3567        for c in api.compositeTypes:
3568            if c.name == name:
3569                return True
3570            for alias in c.aliasList:
3571                if alias == name:
3572                    return True
3573        return False
3574    stream = []
3575
3576    dictStructs = {}
3577    dictData = []
3578    extData = []
3579    usedFeatureStructs = {}
3580    for _, data in api.additionalExtensionData:
3581        if 'mandatory_features' in data.keys():
3582            # sort to have same results for py2 and py3
3583            listStructFeatures = sorted(data['mandatory_features'].items(), key=lambda tup: tup[0])
3584            for structure, featuresList in listStructFeatures:
3585                for featureData in featuresList:
3586                    # allow for featureless VKSC only extensions
3587                    if not 'features' in featureData.keys() or 'requirements' not in featureData.keys():
3588                        continue
3589                    requirements = featureData['requirements']
3590
3591                    mandatory_variant = ''
3592                    try:
3593                        mandatory_variant = featureData['mandatory_variant']
3594                    except KeyError:
3595                        mandatory_variant = ''
3596
3597                    dictData.append( [ structure, featureData['features'], requirements, mandatory_variant] )
3598
3599                    if structure == 'VkPhysicalDeviceFeatures':
3600                        continue
3601
3602                    # if structure is not in dict construct name of variable and add is as a first item
3603                    if (structure not in dictStructs):
3604                        dictStructs[structure] = ([structure[2:3].lower() + structure[3:]], mandatory_variant)
3605                    # add first requirement if it is unique
3606                    if requirements and (requirements[0] not in dictStructs[structure][0]):
3607                        dictStructs[structure][0].append(requirements[0])
3608
3609                    usedFeatureStructs[structure] = []
3610
3611                    if requirements:
3612                        for req in requirements:
3613                            if '.' in req:
3614                                req = req.split('.')[0]
3615                                reqStruct = 'Vk' + req[0].upper() + req[1:]
3616                                usedFeatureStructs[reqStruct] = []
3617
3618        if 'mandatory_extensions' in data:
3619            mandatoryExtensions = []
3620            for mandatoryExt in data['mandatory_extensions']:
3621                if 'extension' in mandatoryExt:
3622                    extName = mandatoryExt.pop('extension')
3623                    mandatoryExtensions.append((extName, mandatoryExt))
3624
3625            for extension, extensionData in mandatoryExtensions:
3626                # requirements are actually mandatory.
3627                if 'requirements' not in extensionData:
3628                    continue
3629
3630                requirements = extensionData['requirements']
3631                mandatory_variant = '' if 'mandatory_variant' not in extensionData else extensionData['mandatory_variant']
3632                extData.append((extension, requirements, mandatory_variant))
3633
3634                for req in requirements:
3635                    if '.' in req:
3636                        req = req.split('.')[0]
3637                        reqStruct = 'Vk' + req[0].upper() + req[1:]
3638                        usedFeatureStructs[reqStruct] = []
3639
3640    stream.extend(['bool canUseFeaturesStruct (const vector<VkExtensionProperties>& deviceExtensions, uint32_t usedApiVersion, const char* extension)',
3641                   '{',
3642                   '\treturn (isExtensionStructSupported(deviceExtensions, RequiredExtension(extension))',
3643                   '\t\t\t|| isCoreDeviceExtension(usedApiVersion, extension));',
3644                   '}',
3645                   '',
3646                   'bool checkBasicMandatoryFeatures(const vkt::Context& context)\n{',
3647                   '\tif (!context.isInstanceFunctionalitySupported("VK_KHR_get_physical_device_properties2"))',
3648                   '\t\tTCU_THROW(NotSupportedError, "Extension VK_KHR_get_physical_device_properties2 is not present");',
3649                   '',
3650                   '\tVkPhysicalDevice\t\t\t\t\tphysicalDevice\t\t= context.getPhysicalDevice();',
3651                   '\tconst InstanceInterface&\t\t\tvki\t\t\t\t\t= context.getInstanceInterface();',
3652                   '\tconst vector<VkExtensionProperties>\tdeviceExtensions\t= enumerateDeviceExtensionProperties(vki, physicalDevice, nullptr);',
3653                   '\tconst uint32_t\t\t\t\t\t\tusedApiVersion\t\t= context.getUsedApiVersion();',
3654                   '',
3655                   '\ttcu::TestLog& log = context.getTestContext().getLog();',
3656                   '\tvk::VkPhysicalDeviceFeatures2 coreFeatures;',
3657                   '\tdeMemset(&coreFeatures, 0, sizeof(coreFeatures));',
3658                   '\tcoreFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;',
3659                   '\tvoid** nextPtr = &coreFeatures.pNext;',
3660                   ''])
3661
3662    # Find the extensions that added the required feature structs.
3663    class StructFoundContinue(Exception):
3664        pass
3665
3666    for usedStruct in usedFeatureStructs:
3667        for compType in api.compositeTypes:
3668            nameList = [compType.name] + compType.aliasList
3669            if usedStruct in nameList:
3670                # Found the official name list for the struct.
3671                for extension in api.extensions:
3672                    try:
3673                        for requirement in extension.requirementsList:
3674                            for extensionStructure in requirement.newTypes:
3675                                if extensionStructure.name in nameList:
3676                                    # Found extension for the struct.
3677                                    usedFeatureStructs[usedStruct].append(extension.name)
3678                                    raise StructFoundContinue
3679                    except StructFoundContinue:
3680                        pass
3681
3682    structList = sorted(usedFeatureStructs.items(), key=lambda tup: tup[0]) # sort to have same results for py2 and py3
3683    apiStructs = list( filter(lambda x : structInAPI(x[0]), structList)) # remove items not defined in current API
3684    varVariants = {} # Some variables are going to be declared only for specific variants.
3685
3686    for structName, extensions in apiStructs:
3687        # The variable name will be the structure name without the Vk prefix and starting in lowercase.
3688        newVar = structName[2].lower() + structName[3:]
3689
3690        metaCondition = ''
3691        if structName in dictStructs:
3692            mandatoryVariantList = dictStructs[structName][1]
3693            if len(mandatoryVariantList) > 0:
3694                mandatoryVariant = mandatoryVariantList[0]
3695                metaCondition = 'defined(CTS_USES_' + mandatoryVariant.upper() + ')'
3696                stream.append('#if ' + metaCondition)
3697                varVariants[newVar] = mandatoryVariant
3698
3699        stream.extend(['\tvk::' + structName + ' ' + newVar + ';',
3700                    '\tdeMemset(&' + newVar + ', 0, sizeof(' + newVar + '));',
3701                    ''])
3702        if len(extensions) > 0:
3703            canUseCond = '\tif ('
3704            for (i, extName) in enumerate(extensions):
3705                canUseCond += ' ' if i == 0 else ' || '
3706                canUseCond += 'canUseFeaturesStruct(deviceExtensions, usedApiVersion, "' + extName + '")'
3707            canUseCond += ' )'
3708            stream.append(canUseCond)
3709        elif api.apiName == "vulkan" and structName in dictStructs:
3710            #reqs = v[0][1:]
3711            reqs = dictStructs[structName][0][1:]
3712            cond = 'if ( '
3713            i = 0
3714            for req in reqs:
3715                if i > 0:
3716                    cond = cond + ' || '
3717                if (req.startswith("ApiVersion")):
3718                    cond = cond + 'context.contextSupports(vk::' + req + ')'
3719                    i += 1
3720            cond = cond + ' )'
3721            stream.append('\t' + cond)
3722
3723        stream.extend(['\t{',
3724                       '\t\t' + newVar + '.sType = getStructureType<' + structName + '>();',
3725                       '\t\t*nextPtr = &' + newVar + ';',
3726                       '\t\tnextPtr  = &' + newVar + '.pNext;',
3727                       '\t}'])
3728
3729        if len(metaCondition) > 0:
3730            stream.append('#endif // ' + metaCondition)
3731
3732        stream.append('')
3733
3734    stream.extend(['\tcontext.getInstanceInterface().getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &coreFeatures);',
3735                   '\tbool result = true;',
3736                   ''])
3737
3738    for v in dictData:
3739        if not structInAPI(v[0]): # remove items not defined in current API ( important for Vulkan SC )
3740            continue
3741        structType = v[0]
3742        structName = 'coreFeatures.features'
3743        metaCondition = ''
3744        if len(v) == 4 and v[3] != '':
3745            # for x in v[3].split('_'):
3746            metaCondition = metaCondition + ' || defined(CTS_USES_' + v[3][0].upper() + ')'
3747            stream.extend(['#if ' + metaCondition[4:]])
3748        if v[0] != 'VkPhysicalDeviceFeatures' :
3749            structName = dictStructs[v[0]][0][0]
3750        if len(v[2]) > 0 :
3751            condition = 'if ( '
3752            for i, req in enumerate(v[2]) :
3753                if (req.startswith("ApiVersion")):
3754                    condition = condition + 'context.contextSupports(vk::' + req + ')'
3755                elif '.' in req:
3756                    condition = condition + req
3757                else:
3758                    condition = condition + 'isExtensionStructSupported(deviceExtensions, RequiredExtension("' + req + '"))'
3759                if i+1 < len(v[2]) :
3760                    condition = condition + ' && '
3761            condition = condition + ' )'
3762            stream.append('\t' + condition)
3763        stream.append('\t{')
3764        # Don't need to support an AND case since that would just be another line in the .txt
3765        reqMetaCondition = ''
3766        if len(v[1]) == 1:
3767            # If the req struct type has a mandatory variant we need to add an #ifdef block, unless we're already inside one.
3768            if len(metaCondition) == 0 and structName in varVariants:
3769                reqMetaCondition = 'defined(CTS_USES_' + varVariants[structName].upper() + ')'
3770                stream.append('#if ' + reqMetaCondition)
3771            stream.append('\t\tif ( ' + structName + '.' + v[1][0] + ' == VK_FALSE )')
3772        else:
3773            condition = 'if ( '
3774            for i, feature in enumerate(v[1]):
3775                if i != 0:
3776                    condition = condition + ' && '
3777                condition = condition + '( ' + structName + '.' + feature + ' == VK_FALSE )'
3778            condition = condition + ' )'
3779            stream.append('\t\t' + condition)
3780        featureSet = " or ".join(v[1])
3781        stream.extend(['\t\t{',
3782                       '\t\t\tlog << tcu::TestLog::Message << "Mandatory feature ' + featureSet + ' not supported" << tcu::TestLog::EndMessage;',
3783                       '\t\t\tresult = false;',
3784                       '\t\t}'])
3785        if reqMetaCondition != '':
3786            stream.append('#endif // ' + reqMetaCondition)
3787        stream.append('\t}')
3788        if metaCondition != '':
3789            stream.extend(['#endif // ' + metaCondition[4:],
3790                          ''])
3791        else:
3792            stream.extend([''])
3793
3794    for extension, requirements, mandatory_variant in extData:
3795        metaCondition = ''
3796        if mandatory_variant != '':
3797            metaCondition = metaCondition + ' || defined(CTS_USES_' + mandatory_variant[0].upper() + ')'
3798            stream.extend(['#if ' + metaCondition[4:]])
3799        if len(requirements) > 0 :
3800            condition = 'if ( '
3801            for i, req in enumerate(requirements) :
3802                if (req.startswith("ApiVersion")):
3803                    condition = condition + 'context.contextSupports(vk::' + req + ')'
3804                elif '.' in req:
3805                    condition = condition + req
3806                else:
3807                    condition = condition + 'isExtensionStructSupported(deviceExtensions, RequiredExtension("' + req + '"))'
3808                if i+1 < len(requirements) :
3809                    condition = condition + ' && '
3810            condition = condition + ' )'
3811            stream.append('\t' + condition)
3812        stream.append('\t{')
3813        stream.extend(['\t\tif (!(isExtensionStructSupported(deviceExtensions, RequiredExtension("' + extension + '")) || isCoreDeviceExtension(usedApiVersion, "' + extension + '")))',
3814                       '\t\t{',
3815                       '\t\t\tlog << tcu::TestLog::Message << "Mandatory extension ' + extension + ' not supported" << tcu::TestLog::EndMessage;',
3816                       '\t\t\tresult = false;',
3817                       '\t\t}',
3818                       '\t}'])
3819        if metaCondition != '':
3820            stream.extend(['#endif // ' + metaCondition[4:],
3821                          ''])
3822        else:
3823            stream.append('')
3824
3825    stream.append('\treturn result;')
3826    stream.append('}\n')
3827
3828    writeInlFile(filename, INL_HEADER, stream)
3829
3830def writeExtensionList(api, filename, extensionType):
3831    extensionList = []
3832    for extensionName, data in api.additionalExtensionData:
3833        # make sure extension name starts with VK_KHR
3834        if not extensionName.startswith('VK_KHR'):
3835            continue
3836        # make sure that this extension was registered
3837        if 'register_extension' not in data.keys():
3838            continue
3839        # skip extensions that are not supported in Vulkan SC
3840        if api.apiName == 'vulkansc':
3841            if any(ext.name == extensionName for ext in api.notSupportedExtensions):
3842                continue
3843        # make sure extension is intended for the vulkan variant
3844        is_sc_only = False
3845
3846        if api.apiName != 'vulkansc':
3847            if 'mandatory_features' in data.keys():
3848                for structure, listStruct in data['mandatory_features'].items():
3849                    for featureData in listStruct:
3850                        mandatory_variant = ''
3851                        try:
3852                            mandatory_variant = featureData['mandatory_variant']
3853                        except KeyError:
3854                            mandatory_variant = ''
3855                        # VKSC only
3856                        if 'vulkansc' in mandatory_variant:
3857                            is_sc_only = True
3858        if is_sc_only:
3859            continue
3860
3861        # make sure extension has proper type
3862        if extensionType == data['register_extension']['type']:
3863            extensionList.append(extensionName)
3864    extensionList.sort()
3865    # write list of all found extensions
3866    stream = []
3867    stream.append('static const char* s_allowed{0}KhrExtensions[] =\n{{'.format(extensionType.title()))
3868    for n in extensionList:
3869        stream.append('\t"' + n + '",')
3870    stream.append('};\n')
3871    writeInlFile(filename, INL_HEADER, stream)
3872
3873def transformDependsToCondition(depends, api, checkVersionString, checkExtensionString):
3874    depList = re.split(r'(\W+)', depends)
3875    for idx, depPart in enumerate(depList):
3876        if ',' in depPart:
3877            depList[idx] = depList[idx].replace(',', ' || ')
3878        elif '+' in depPart:
3879            depList[idx] = depList[idx].replace('+', ' && ')
3880        elif 'VK_' in depPart:
3881            if 'VK_VERSION' in depPart:
3882                if idx > 0 and ' || ' in depList[idx-1]:
3883                    # some vk.xml entries include "promoted to" version preceded by logical OR operator in the extension "depends" attribute
3884                    # script don't rely on this optional information and will find "promoted to" versions for all dependencies of all extensions in the below code
3885                    # accordingly the one from vk.xml is ignored to avoid redundant isCompatibile() checks
3886                    depList[idx-1] = depList[idx-1].replace(' || ', '')
3887                    depList[idx] = ''
3888                    continue
3889                # when dependency is vulkan version then replace it with proper condition
3890                depList[idx] = checkVersionString % (depPart[-3], depPart[-1])
3891            else:
3892                # when dependency is extension check if it was promoted
3893                extNotFound = True
3894                for dExt in api.extensions:
3895                    if depPart == dExt.name:
3896                        depExtVector = 'vDEP' if dExt.type == 'device' else 'vIEP'
3897                        isSupportedCheck = checkExtensionString % (depExtVector, depPart)
3898                        if dExt.promotedto is not None:
3899                            p = dExt.promotedto
3900                            # check if dependency was promoted to vulkan version or other extension
3901                            if 'VK_VERSION' in p:
3902                                depList[idx] = f'({checkVersionString % (p[-3], p[-1])} || {isSupportedCheck})'
3903                            else:
3904                                depList[idx] = f'({checkExtensionString % (depExtVector, depPart)} || {isSupportedCheck})'
3905                        else:
3906                            depList[idx] = isSupportedCheck
3907                        extNotFound = False
3908                        break
3909                # for SC when extension was not found try checking also not supported
3910                # extensions and see if this extension is part of core
3911                if extNotFound and api.apiName == "vulkansc":
3912                    for dExt in api.notSupportedExtensions:
3913                        if depPart == dExt.name:
3914                            p = dExt.promotedto
3915                            if p is None:
3916                                break
3917                            if int(p[-1]) > 2:
3918                                break
3919                            extNotFound = False
3920                            depList[idx] = "true"
3921                if extNotFound:
3922                    assert False, f"{depPart} from dependencies ({depends}) not found"
3923    return ''.join(depList)
3924
3925def writeApiExtensionDependencyInfo(api, filename):
3926
3927    def genHelperFunctions():
3928        yield 'using namespace tcu;'
3929        yield 'using ExtPropVect = std::vector<vk::VkExtensionProperties>;'
3930        yield 'using IsSupportedFun = bool (*)(const tcu::UVec2&, const ExtPropVect&, const ExtPropVect&);'
3931        yield 'using DependencyCheckVect = std::vector<std::pair<const char*, IsSupportedFun> >;\n'
3932        yield 'bool isCompatibile(uint32_t major, uint32_t minor, const tcu::UVec2& testedApiVersion)'
3933        yield '{'
3934        yield '\t// return true when tested api version is greater'
3935        yield '\t// or equal to version represented by two uints'
3936        yield '\tif (major == testedApiVersion.x())'
3937        yield '\t\treturn minor <= testedApiVersion.y();'
3938        yield '\treturn major < testedApiVersion.x();'
3939        yield '}\n'
3940        yield 'bool isSupported(const ExtPropVect& extensions, const char* ext)'
3941        yield '{'
3942        yield '\treturn isExtensionStructSupported(extensions, vk::RequiredExtension(ext));'
3943        yield '}\n'
3944
3945    def genExtDepArray(extType):
3946        extensionList = []
3947        maxExtLength = 0
3948        extVector = 'vIEP'
3949        othVector = 'vDEP'
3950        if extType == 'device':
3951            extVector, othVector = othVector, extVector        # swap
3952        # iterate over all extension that are of specified type and that have requirements
3953        for ext in api.extensions:
3954            if ext.type != extType:
3955                continue
3956            if ext.depends is None:
3957                continue
3958            # memorize extension name and dependencies for future vector generation
3959            extensionList.append(ext.name)
3960            # memorize max extension name and dependency length
3961            maxExtLength = max(maxExtLength, len(ext.name))
3962            # generate check function for this extension
3963            yield f'bool check_{ext.name}(const tcu::UVec2& v, const ExtPropVect& vIEP, const ExtPropVect& vDEP)'
3964            yield '{'
3965            # check if extension was promoted; for SC we need to check vulkan version as sc10 is based on vk12
3966            if ext.promotedto is not None and 'VK_VERSION' in ext.promotedto:
3967                p = ext.promotedto
3968                yield f'\tif (isCompatibile({p[-3]}, {p[-1]}, v))'
3969                yield '\t\treturn true;\n'
3970            else:
3971                yield '\tDE_UNREF(v);'
3972            # there is a high chance that other vector won't be used
3973            yield f'\tDE_UNREF({othVector});'
3974            # check if extension is supported
3975            yield f'\n\tif (!isSupported({extVector}, "{ext.name}"))'
3976            yield '\t\treturn true;\n'
3977            # replace dependent extensions/versions with proper conditions
3978            finalCondition = transformDependsToCondition(ext.depends, api, 'isCompatibile(%s, %s, v)', 'isSupported(%s, "%s")')
3979            yield f'\t// depends attribute in xml: {ext.depends}'
3980            yield f'\treturn {finalCondition};'
3981            yield '}\n'
3982        # save list of all device/instance extensions
3983        yield 'static const DependencyCheckVect {}ExtensionDependencies'.format(extType)
3984        yield '{'
3985        for ext in extensionList:
3986            extTabCount = (maxExtLength - len(ext)) / 4
3987            eTabs = '\t'*int(round(extTabCount+1.49))
3988            yield f'\tstd::make_pair("{ext}",{eTabs}&check_{ext}),'
3989        yield '};\n'
3990
3991    def genApiVersions():
3992        yield 'static const std::tuple<uint32_t, uint32_t, uint32_t, uint32_t>\treleasedApiVersions[]\t='
3993        yield '{'
3994        for f in reversed(api.features):
3995            apiVariant = '0' if f.api == 'vulkan' else '1'
3996            major, minor = f.number.split('.')
3997            version = (int(apiVariant) << 29) | (int(major) << 22) | (int(minor) << 12)
3998            yield '\tstd::make_tuple({}, {}, {}, {}),'.format(version, apiVariant, major, minor)
3999        yield '};'
4000
4001    def parseExtensionDependencies(extDeps, ext):
4002        major, minor = 1, 0
4003        requiredVerFound = False;
4004        # return in case nothing more left to be processed
4005        if extDeps is None or extDeps == "":
4006            return major, minor, requiredVerFound
4007        ungrpPartLen = 0
4008        versionPattern = "[A-Z]+_VERSION_([0-9]+)_([0-9]+)"
4009        ungroupedPattern = r"^.*?\(+|^.*?$"
4010        # look for non-grouped part, it may include the required vulkan version
4011        ungroupPart = re.search(ungroupedPattern, extDeps)
4012        if ungroupPart is not None and ungroupPart[0].replace(r"(", "") != "":
4013            ungrpPartLen = len(ungroupPart[0].replace(r"(", ""))
4014            # is specific version explicitly requested?
4015            match = re.search(versionPattern, ungroupPart[0])
4016            if match is not None:
4017                if len(match[0]) != len(extDeps):
4018                    # there is more than just a version; check if it's accompanied by AND operator(s)
4019                    ext_pattern = r".*\+*"+versionPattern+r"\++.*|.*\++"+versionPattern+r"\+*.*"
4020                    match = re.search(ext_pattern, ungroupPart[0])
4021                if match is not None:
4022                    # specific version is explicitly requested
4023                    major, minor = int(match[1]), int(match[2])
4024                    return major, minor, True
4025            # no explicit version is requested, continue parsing the remaining part
4026            extDeps = extDeps[ungrpPartLen:]
4027        groupedPattern = r"(.*)\+|(.*)$"
4028        match = re.search(groupedPattern, extDeps)
4029        if match is not None and match[0] != "":
4030            # groups may include the dependency "promoted to" versions accompanied by OR operator
4031            # but they don't include the extension explicit required version; continue parsing the remaining part
4032            groupLength = len(match[0])
4033            major, minor, requiredVerFound = parseExtensionDependencies(extDeps[groupLength:], ext)
4034        return major, minor, requiredVerFound
4035
4036    def genRequiredCoreVersions():
4037        yield 'static const std::tuple<uint32_t, uint32_t, const char*>\textensionRequiredCoreVersion[]\t ='
4038        yield '{'
4039        versionPattern = "[A-Z]+_VERSION_([0-9]+)_([0-9]+)"
4040        for ext in api.extensions:
4041            # skip video extensions
4042            if 'vulkan_video_' in ext.name:
4043                continue
4044            major, minor = 1, 0
4045            if ext.depends is not None:
4046                major, minor, requiredVerFound = parseExtensionDependencies(ext.depends, ext)
4047                if not requiredVerFound:
4048                    # find all extensions that are dependencies of this one
4049                    matches = re.findall(r"VK_\w+", ext.depends, re.M)
4050                    for m in matches:
4051                        for de in api.extensions:
4052                            if de.name == m:
4053                                if de.depends is not None:
4054                                    # check if the dependency states explicitly the required vulkan version and pick the higher one
4055                                    newMajor, newMinor, requiredVerFound = parseExtensionDependencies(de.depends, de)
4056                                    if requiredVerFound:
4057                                        if newMajor > major:
4058                                            major, minor = newMajor, newMinor
4059                                        elif newMajor == major and newMinor > minor:
4060                                            minor = newMinor
4061                                break
4062            yield '\tstd::make_tuple({}, {}, "{}"),'.format(major, minor, ext.name)
4063        yield '};'
4064
4065    stream = []
4066    stream.extend(genHelperFunctions())
4067    stream.extend(genExtDepArray('instance'))
4068    stream.extend(genExtDepArray('device'))
4069    stream.extend(genApiVersions())
4070    stream.extend(genRequiredCoreVersions())
4071
4072    writeInlFile(filename, INL_HEADER, stream)
4073
4074def writeEntryPointValidation(api, filename):
4075    # keys are instance extension names and value is list of device-level functions
4076    instExtDeviceFunDict = {}
4077    # iterate over all extensions and find instance extensions
4078    for ext in api.extensions:
4079        if ext.type == "instance":
4080            # iterate over all functions instance extension adds
4081            for requirement in ext.requirementsList:
4082                for extCommand in requirement.newCommands:
4083                    # to get a type of command we need to find this command definition in list of all functions
4084                    for command in api.functions:
4085                        if extCommand.name == command.name or extCommand.name in command.aliasList:
4086                            # check if this is device-level entry-point
4087                            if command.getType() == Function.TYPE_DEVICE:
4088                                if ext.name not in instExtDeviceFunDict:
4089                                    instExtDeviceFunDict[ext.name] = []
4090                                instExtDeviceFunDict[ext.name].append(extCommand.name)
4091    stream = ['std::map<std::string, std::vector<std::string> > instExtDeviceFun', '{']
4092    for extName in instExtDeviceFunDict:
4093        stream.append(f'\t{{ "{extName}",\n\t\t{{')
4094        for fun in instExtDeviceFunDict[extName]:
4095            stream.append(f'\t\t\t"{fun}",')
4096        stream.append('\t\t}\n\t},')
4097    stream.append('};')
4098    writeInlFile(filename, INL_HEADER, stream)
4099
4100def writeGetDeviceProcAddr(api, filename):
4101    testBlockStart = '''tcu::TestStatus        testGetDeviceProcAddr        (Context& context)
4102{
4103    tcu::TestLog&                                log                        (context.getTestContext().getLog());
4104    const PlatformInterface&                    platformInterface = context.getPlatformInterface();
4105    const auto                                    validationEnabled = context.getTestContext().getCommandLine().isValidationEnabled();
4106    const CustomInstance                        instance                (createCustomInstanceFromContext(context));
4107    const InstanceDriver&                        instanceDriver = instance.getDriver();
4108    const VkPhysicalDevice                        physicalDevice = chooseDevice(instanceDriver, instance, context.getTestContext().getCommandLine());
4109    const uint32_t                                queueFamilyIndex = 0;
4110    const uint32_t                                queueCount = 1;
4111    const float                                    queuePriority = 1.0f;
4112    const std::vector<VkQueueFamilyProperties>    queueFamilyProperties = getPhysicalDeviceQueueFamilyProperties(instanceDriver, physicalDevice);
4113
4114    const VkDeviceQueueCreateInfo            deviceQueueCreateInfo =
4115    {
4116        VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, //  VkStructureType sType;
4117        nullptr, //  const void* pNext;
4118        (VkDeviceQueueCreateFlags)0u, //  VkDeviceQueueCreateFlags flags;
4119        queueFamilyIndex, //  uint32_t queueFamilyIndex;
4120        queueCount, //  uint32_t queueCount;
4121        &queuePriority, //  const float* pQueuePriorities;
4122    };
4123
4124    const VkDeviceCreateInfo                deviceCreateInfo =
4125    {
4126        VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, //  VkStructureType sType;
4127        nullptr, //  const void* pNext;
4128        (VkDeviceCreateFlags)0u, //  VkDeviceCreateFlags flags;
4129        1u, //  uint32_t queueCreateInfoCount;
4130        &deviceQueueCreateInfo, //  const VkDeviceQueueCreateInfo* pQueueCreateInfos;
4131        0u, //  uint32_t enabledLayerCount;
4132        nullptr, //  const char* const* ppEnabledLayerNames;
4133        0u, //  uint32_t enabledExtensionCount;
4134        nullptr, //  const char* const* ppEnabledExtensionNames;
4135        nullptr, //  const VkPhysicalDeviceFeatures* pEnabledFeatures;
4136    };
4137    const Unique<VkDevice>                    device            (createCustomDevice(validationEnabled, platformInterface, instance, instanceDriver, physicalDevice, &deviceCreateInfo));
4138    const DeviceDriver                        deviceDriver    (platformInterface, instance, device.get(), context.getUsedApiVersion(), context.getTestContext().getCommandLine());
4139
4140    const std::vector<std::string> functions{'''
4141    testBlockEnd = '''    };
4142
4143    bool fail = false;
4144    for (const auto& function : functions)
4145    {
4146        if (deviceDriver.getDeviceProcAddr(device.get(), function.c_str()) != nullptr)
4147        {
4148            fail = true;
4149            log << tcu::TestLog::Message << "Function " << function << " is not NULL" << tcu::TestLog::EndMessage;
4150        }
4151    }
4152    if (fail)
4153        return tcu::TestStatus::fail("Fail");
4154    return tcu::TestStatus::pass("All functions are NULL");
4155}
4156'''
4157
4158    def functions(functionType):
4159        for ext in api.extensions:
4160            for requirement in ext.requirementsList:
4161                for requiredCommand in requirement.newCommands:
4162                    yield '\t\t"' + requiredCommand.name + '",'
4163    stream = []
4164    stream.append('#include "tcuCommandLine.hpp"')
4165    stream.append('#include "vktTestCase.hpp"')
4166    stream.append('#include "vkPlatform.hpp"')
4167    stream.append('#include "vkDeviceUtil.hpp"')
4168    stream.append('#include "vkQueryUtil.hpp"')
4169    stream.append('#include "vktCustomInstancesDevices.hpp"')
4170    stream.append('#include "vktTestCase.hpp"')
4171    stream.append('#include "vktTestCaseUtil.hpp"')
4172    stream.append('\nnamespace vkt\n{\n')
4173    stream.append('using namespace vk;\n')
4174    stream.append(testBlockStart)
4175    stream.extend(functions(api))
4176    stream.append(testBlockEnd)
4177
4178    # function to create tests
4179    stream.append("void addGetDeviceProcAddrTests (tcu::TestCaseGroup* testGroup)\n{")
4180    stream.append('\taddFunctionCase(testGroup, "non_enabled", testGetDeviceProcAddr);')
4181    stream.append('}\n')
4182    stream.append('}\n')
4183
4184    writeInlFile(filename, INL_HEADER, stream)
4185
4186def writeProfileTests(inlFileName, jsonFilesList):
4187
4188    # helper function; workaround for lack of information in json about limit type
4189    def getLimitMacro(propName, propComponent):
4190        maxUintPropNames = ["bufferImageGranularity", "storageTexelBufferOffsetAlignmentBytes",\
4191                            "robustUniformBufferAccessSizeAlignment", "shaderWarpsPerSM",\
4192                            "perViewPositionAllComponents", "minTexelBufferOffsetAlignment",\
4193                            "minUniformBufferOffsetAlignment"]
4194        minFloatPropNames = ["maxSamplerLodBias"]
4195        maxFloatPropNames = ["pointSizeGranularity", "lineWidthGranularity"]
4196        minDevSizePropNames = ["maxBufferSize"]
4197        if propName in maxUintPropNames:
4198            return "LIM_MAX_UINT32"
4199        elif propName in minFloatPropNames:
4200            return "LIM_MIN_FLOAT"
4201        elif propName in maxFloatPropNames:
4202            return "LIM_MAX_FLOAT"
4203        elif propName in minDevSizePropNames:
4204            return "LIM_MIN_DEVSIZE"
4205        elif propName.endswith("SampleCounts"):
4206            return "LIM_MIN_BITI32"
4207        elif not propName.startswith("max") and propName.endswith("Range"):
4208            return "LIM_MAX_FLOAT" if propComponent == 0 else "LIM_MIN_FLOAT"
4209        return "LIM_MIN_UINT32"
4210
4211    # helper function that adds property or feature structures to lists of struct initializers
4212    def constructStruct(structName, structInitNamesList, structInitList):
4213        # skip structures that already are in the chain
4214        if structName in structInitNamesList:
4215            return
4216        structInitNamesList.append(structName)
4217        # construct structure instance and connect it to chain
4218        parentStruct = "" if (len(structInitNamesList) == 3) else "&vk" + structInitNamesList[-2]
4219        structInitList.append(f"\tVkPhysicalDevice{structName} vk{structName} = initVulkanStructure({parentStruct});")
4220
4221    # helper function handling strings representing property limit checks
4222    def addPropertyEntries(structName, propName, propLimit, propertyTableItems):
4223        if propName == "driverName":
4224            return
4225        propSubItems = [(propName, propLimit)]
4226        combinedStructName = structName
4227        # checkk if propLimit is actualy a dictionary this will be the case when propName is "limits";
4228        # in that case we have to get all sub items and add all of them to propertyTableItems
4229        if isinstance(propLimit, dict):
4230            propSubItems = propLimit.items()
4231            combinedStructName += "." + propName
4232        # usualy we will add just one item but we need to handle cases where there is more
4233        for name, limit in propSubItems:
4234            limitComponentCount = 1
4235            if isinstance(limit, list):
4236                limitComponentCount = len(limit)
4237                # handle special case like storageImageSampleCounts
4238                if limitComponentCount == 1:
4239                   limit = limit[0]
4240            componentAccessFormat = ""
4241            if limitComponentCount > 1:
4242                # if limit is list of strings joint them together;
4243                # e.g. this is the case for subgroupSupportedStages
4244                if isinstance(limit[0], str):
4245                    limitComponentCount = 1
4246                    limit = "|".join(limit)
4247                else:
4248                    componentAccessFormat = "[{}]"
4249            # handle case where limit is represented by more than one value;
4250            # in that case we will add as many entries to propertyTableItems as there are limiting values
4251            for i in range(limitComponentCount):
4252                componentAccess = componentAccessFormat.format(i)
4253                limitMacro = getLimitMacro(name, i)
4254                limitValue = "true" if limit == True else limit
4255                if limitValue == False:
4256                     limitValue = "false"
4257                limitValue = limitValue[i] if limitComponentCount > 1 else limitValue
4258                propertyTableItems += [f"PN({combinedStructName}.{name}{componentAccess}), {limitMacro}({limitValue})"]
4259
4260    vkpdLen = len("VkPhysicalDevice")
4261    profilesList = []
4262    stream = []
4263
4264    for jsonFile in jsonFilesList:
4265        jsonContent = readFile(jsonFile)
4266        profilesDict = json.loads(jsonContent)
4267        capabilitiesDefinitionsDict = profilesDict["capabilities"]
4268
4269        for profileName, profileData in reversed(profilesDict["profiles"].items()):
4270            featureStructInitList = []
4271            featureStructInitNamesList = ["Features", "Features2"]
4272            featureTableItems = []
4273            propertyStructInitList = []
4274            propertyStructInitNamesList = ["Properties", "Properties2"]
4275            propertyTableItems = []
4276            extensionList = []
4277            formatsList = []
4278            highestMajor = 1
4279            highestMinor = 0
4280
4281            allCapabilities = profileData["capabilities"] + profileData.get("optionals", [])
4282            for capability in allCapabilities:
4283                capabilityList = capability if isinstance(capability, list) else [capability]
4284                for capabilityName in capabilityList:
4285                    capabilityDefinition = capabilitiesDefinitionsDict[capabilityName]
4286                    # identify highest required vulkan version
4287                    match = re.match(r"vulkan(\d)(\d)requirements", capabilityName)
4288                    if match is not None:
4289                        major, minor = int(match.group(1)), int (match.group(2))
4290                        if major*10 + minor > highestMajor * 10 + highestMinor:
4291                            highestMajor, highestMinor = major, minor
4292                    if "features" in capabilityDefinition:
4293                        featureStructList = capabilityDefinition["features"]
4294                        # skip adding comment for empty requirements
4295                        if len(featureStructList) == 1 and not list(featureStructList.values())[0]:
4296                            continue
4297                        featureTableItems.append(f"\t\t// {capabilityName}");
4298                        # iterate over required features
4299                        for featureStruct in featureStructList:
4300                            structName = featureStruct[vkpdLen:]
4301                            constructStruct(structName, featureStructInitNamesList, featureStructInitList)
4302                            for feature in featureStructList[featureStruct]:
4303                                featureTableItems.append(f"vk{structName}, {feature}")
4304                            featureTableItems.append("")
4305                    if "properties" in capabilityDefinition:
4306                        propertyStructList = capabilityDefinition["properties"]
4307                        propertyTableItems.append(f"\t\t// {capabilityName}");
4308                        for propertyStruct in propertyStructList:
4309                            structName = propertyStruct[vkpdLen:]
4310                            constructStruct(structName, propertyStructInitNamesList, propertyStructInitList)
4311                            for propName, propLimit in propertyStructList[propertyStruct].items():
4312                                addPropertyEntries("vk" + structName, propName, propLimit, propertyTableItems)
4313                            propertyTableItems.append("")
4314                    if "extensions" in capabilityDefinition:
4315                        extensionList = [n for n in capabilityDefinition["extensions"]]
4316                    if "formats" in capabilityDefinition:
4317                        formatsList = capabilityDefinition["formats"]
4318
4319            # remove empty lines at the end
4320            featureTableItems.pop()
4321            propertyTableItems.pop()
4322
4323            # remove "VP_KHR_" from roadmap profile name
4324            if "VP_KHR_" in profileName:
4325                profileName = profileName[7:]
4326            # lower letters for all profile names
4327            profileName = profileName.lower()
4328
4329            # template used to get both device features and device properties
4330            structGetterTemplate = "\n"\
4331            "\tVkPhysicalDevice{0}2 vk{0}2 = initVulkanStructure(&vk{2});\n"\
4332            "\tauto& vk{0} = vk{0}2.{1};\n"\
4333            "\tvki.getPhysicalDevice{0}2(pd, &vk{0}2);\n"
4334
4335            # construct function that will validate profile
4336            stream.append(f"tcu::TestStatus validate_{profileName}(Context& context)")
4337
4338            stream.append("{\n"
4339            "\tconst VkBool32 checkAlways = true;\n"
4340            "\tbool oneOrMoreChecksFailed = false;\n"
4341            "\tauto pd = context.getPhysicalDevice();\n"
4342            "\tconst auto &vki = context.getInstanceInterface();\n"
4343            "\tTestLog& log = context.getTestContext().getLog();\n")
4344
4345            stream.extend(featureStructInitList)
4346            stream.append(structGetterTemplate.format("Features", "features", featureStructInitNamesList[-1]))
4347            stream.extend(propertyStructInitList)
4348            stream.append(structGetterTemplate.format("Properties", "properties", propertyStructInitNamesList[-1]))
4349            if len(featureTableItems):
4350                stream.append("\tconst std::vector<FeatureEntry> featureTable {")
4351                stream.extend(["\t\tROADMAP_FEATURE_ITEM(" + f + ")," if ("," in f) else f for f in featureTableItems])
4352                stream.append("\t};\n"
4353                "\tfor (const auto &testedFeature : featureTable)\n"
4354                "\t{\n"
4355                "\t    if (!testedFeature.fieldPtr[0])\n"
4356                "\t    {\n"
4357                "\t        log << TestLog::Message\n"
4358                "\t            << \"Feature \" << testedFeature.fieldName << \" is not supported\"\n"
4359                "\t            << TestLog::EndMessage;\n"
4360                "\t        oneOrMoreChecksFailed = true;\n"
4361                "\t    }\n"
4362                "\t}\n")
4363            if len(propertyTableItems):
4364                stream.append("\tconst std::vector<FeatureLimitTableItem> propertyTable {")
4365                stream.extend(["\t\t{ PN(checkAlways), " + p + " }," if ("," in p) else p for p in propertyTableItems])
4366                stream.append("\t};\n"
4367                "\tfor (const auto& testedProperty : propertyTable)\n"
4368                "\t    oneOrMoreChecksFailed |= !validateLimit(testedProperty, log);\n")
4369            if len(extensionList):
4370                stream.append("\tstd::vector<std::string> extensionList {")
4371                stream.append('\t\t"' + '",\n\t\t"'.join(extensionList) + '"')
4372                stream.append("\t};\n"
4373                "\tconst auto deviceExtensions = enumerateDeviceExtensionProperties(vki, pd, nullptr);\n"
4374                "\tfor (const auto& testedExtension : extensionList)\n"
4375                "\t{\n"
4376                "\t    if (isExtensionStructSupported(deviceExtensions, RequiredExtension(testedExtension)) ||\n"
4377                "\t        context.isInstanceFunctionalitySupported(testedExtension))\n"
4378                "\t        continue;\n"
4379                "\t    log << TestLog::Message\n"
4380                "\t        << testedExtension << \" is not supported\"\n"
4381                "\t        << TestLog::EndMessage;\n"
4382                "\t    oneOrMoreChecksFailed = true;\n"
4383                "\t}")
4384            if len(formatsList):
4385                stream.append("\n\tstd::vector<FormatEntry> formatsList {")
4386                for formatName, formatProperties in formatsList.items():
4387                    formatProperties = formatProperties["VkFormatProperties"]
4388                    linearTilingFeatures = formatProperties["linearTilingFeatures"]
4389                    linearTilingFeatures = "0" if not linearTilingFeatures else linearTilingFeatures
4390                    optimalTilingFeatures = formatProperties["optimalTilingFeatures"]
4391                    optimalTilingFeatures = "0" if not optimalTilingFeatures else optimalTilingFeatures
4392                    bufferFeatures = formatProperties["bufferFeatures"]
4393                    bufferFeatures = "0" if not bufferFeatures else bufferFeatures
4394                    stream.append(f"""\t\t{{ {formatName}, "{formatName}",
4395            {{ {"|".join(linearTilingFeatures)},
4396              {"|".join(optimalTilingFeatures)},
4397              {"|".join(bufferFeatures)} }} }},""")
4398                stream.append("\t};\n"
4399                "\t\tVkFormatProperties supportedFormatPropertiess;\n"
4400                "\t\tfor (const auto& [f, fn, fp] : formatsList)\n"
4401                "\t\t{\n"
4402                "\t\t    vki.getPhysicalDeviceFormatProperties(pd, f, &supportedFormatPropertiess);\n"
4403                "\t\t    if (((fp.linearTilingFeatures & supportedFormatPropertiess.linearTilingFeatures) == fp.linearTilingFeatures) &&\n"
4404                "\t\t        ((fp.optimalTilingFeatures & supportedFormatPropertiess.optimalTilingFeatures) == fp.optimalTilingFeatures) &&\n"
4405                "\t\t        ((fp.bufferFeatures & supportedFormatPropertiess.bufferFeatures) == fp.bufferFeatures))\n"
4406                "\t\t        continue;\n"
4407                "\t\t    log << TestLog::Message\n"
4408                "\t\t        << \"Required format properties for \" << fn << \" are not supported\"\n"
4409                "\t\t        << TestLog::EndMessage;\n"
4410                "\t\t    oneOrMoreChecksFailed = true;\n"
4411                "\t\t}\n")
4412
4413            stream.append("\n"
4414            "\tif (oneOrMoreChecksFailed)\n"
4415            "\t    TCU_THROW(NotSupportedError, \"Profile not supported\");\n"
4416            "\treturn tcu::TestStatus::pass(\"Profile supported\");\n}\n")
4417
4418            profilesList.append(f"\t{{ \"{profileName}\", checkApiVersionSupport<{highestMajor}, {highestMinor}>, validate_{profileName} }},")
4419
4420    # save list of all callbacks
4421    stream.append("static const std::vector<ProfileEntry> profileEntries {")
4422    stream.extend(profilesList)
4423    stream.append("};")
4424
4425    writeInlFile(inlFileName, INL_HEADER, stream)
4426
4427def writeConformanceVersions(api, filename):
4428    logging.debug("Preparing to generate " + filename)
4429    # get list of all vulkan/vulkansc tags from git
4430    remote_urls = os.popen("git remote -v").read().split('\n')
4431    remote_url = None
4432    url_regexp = r'\bgerrit\.khronos\.org\b.*\bvk-gl-cts\b'
4433    for line in remote_urls:
4434        if re.search(url_regexp, line, re.IGNORECASE) is not None:
4435            remote_url = line.split()[1]
4436            break
4437    listOfTags = os.popen("git ls-remote -t %s" % (remote_url)).read()
4438    pattern = r"vulkan-cts-(\d).(\d).(\d).(\d)"
4439    if args.api == 'SC':
4440        pattern = r"vulkansc-cts-(\d).(\d).(\d).(\d)"
4441    matches = re.findall(pattern, listOfTags, re.M)
4442    if len(matches) == 0:
4443        return
4444    # read all text files in doc folder and find withdrawn cts versions (branches)
4445    withdrawnBranches = set()
4446    today = datetime.date.today()
4447    docFiles = glob.glob(os.path.join(os.path.dirname(__file__), "..", "doc", "*.txt"))
4448    for fileName in docFiles:
4449        if "withdrawal" not in fileName:
4450            continue
4451        fileContent = readFile(fileName)
4452        # get date when releases are withdrawn
4453        match = re.search(r"(20\d\d)-(\d\d)-(\d\d).+ withdrawn", fileContent, re.IGNORECASE)
4454        if match is not None:
4455            # check if announcement refers to date in the past
4456            if today > datetime.date(int(match[1]), int(match[2]), int(match[3])):
4457                # get names of withdrawn branches
4458                branchMatches = re.findall(pattern, fileContent, re.M)
4459                for v in branchMatches:
4460                    withdrawnBranches.add((v[0], v[1], v[2], v[3]))
4461    # define helper function that will be used to add entries for both vk and sc
4462    def appendToStream(stream, versionsToAdd, maxWithdrawnVersion):
4463        addedVersions = set()
4464        for v in reversed(versionsToAdd):
4465            # add only unique versions; ignore duplicates (e.g. with "-rc1", "-rc2" postfix);
4466            # also add versions that are greater than maximal withdrawn version
4467            if v in addedVersions or v <= maxWithdrawnVersion:
4468                continue
4469            addedVersions.add(v)
4470            stream.append(f'\tmakeConformanceVersion({v[0]}, {v[1]}, {v[2]}, {v[3]}),')
4471    # save array with versions
4472    stream = ['static const VkConformanceVersion knownConformanceVersions[]',
4473              '{']
4474    appendToStream(stream, matches, tuple('0'*4) if len(withdrawnBranches) == 0 else max(withdrawnBranches))
4475    stream.append('};')
4476    writeInlFile(filename, INL_HEADER, stream)
4477
4478def parseCmdLineArgs():
4479    parser = argparse.ArgumentParser(description = "Generate Vulkan INL files",
4480                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)
4481    parser.add_argument("-a",
4482                        "--api",
4483                        dest="api",
4484                        default="",
4485                        help="Choose between Vulkan and Vulkan SC")
4486    parser.add_argument("-o",
4487                        "--outdir",
4488                        dest="outdir",
4489                        default="",
4490                        help="Choose output directory")
4491    parser.add_argument("-v", "--verbose",
4492                        dest="verbose",
4493                        action="store_true",
4494                        help="Enable verbose logging")
4495    return parser.parse_args()
4496
4497if __name__ == "__main__":
4498    args = parseCmdLineArgs()
4499    initializeLogger(args.verbose)
4500
4501    # if argument was specified it is interpreted as a path to which .inl files will be written
4502    outputPath = DEFAULT_OUTPUT_DIR[args.api] if args.outdir == '' else args.outdir
4503
4504    vkTree = etree.parse(os.path.join(VULKAN_XML_DIR, "vk.xml"))
4505    apiName = "vulkansc" if args.api == 'SC' else "vulkan"
4506
4507    # Read vk.xml and generate vulkan headers from it
4508    api = API(apiName)
4509    api.build(vkTree)
4510    api.postProcess()
4511
4512    # Read video.xml
4513    if args.api != 'SC':
4514        api.build( etree.parse(os.path.join(VULKAN_XML_DIR, "video.xml")) )
4515
4516    platformFuncs = [Function.TYPE_PLATFORM]
4517    instanceFuncs = [Function.TYPE_INSTANCE]
4518    deviceFuncs = [Function.TYPE_DEVICE]
4519
4520    dfd = generateDeviceFeaturesOrPropertiesDefs(api, 'Features')
4521    writeDeviceFeatures                         (api, dfd, os.path.join(outputPath, "vkDeviceFeatures.inl"))
4522    writeDeviceFeaturesDefaultDeviceDefs        (dfd, os.path.join(outputPath, "vkDeviceFeaturesForDefaultDeviceDefs.inl"))
4523    writeDeviceFeaturesContextDecl              (dfd, os.path.join(outputPath, "vkDeviceFeaturesForContextDecl.inl"))
4524    writeDeviceFeaturesContextDefs              (dfd, os.path.join(outputPath, "vkDeviceFeaturesForContextDefs.inl"))
4525    writeDeviceFeatureTest                      (api, os.path.join(outputPath, "vkDeviceFeatureTest.inl"))
4526
4527    dpd = generateDeviceFeaturesOrPropertiesDefs(api, 'Properties')
4528    writeDeviceProperties                       (api, dpd, os.path.join(outputPath, "vkDeviceProperties.inl"))
4529    writeDevicePropertiesDefaultDeviceDefs      (dpd, os.path.join(outputPath, "vkDevicePropertiesForDefaultDeviceDefs.inl"))
4530    writeDevicePropertiesContextDecl            (dpd, os.path.join(outputPath, "vkDevicePropertiesForContextDecl.inl"))
4531    writeDevicePropertiesContextDefs            (dpd, os.path.join(outputPath, "vkDevicePropertiesForContextDefs.inl"))
4532
4533    writeHandleType                             (api, os.path.join(outputPath, "vkHandleType.inl"))
4534    writeBasicTypes                             (api, os.path.join(outputPath, "vkBasicTypes.inl"))
4535    writeCompositeTypes                         (api, os.path.join(outputPath, "vkStructTypes.inl"))
4536    writeInterfaceDecl                          (api, os.path.join(outputPath, "vkVirtualPlatformInterface.inl"), platformFuncs, False)
4537    writeInterfaceDecl                          (api, os.path.join(outputPath, "vkVirtualInstanceInterface.inl"), instanceFuncs, False)
4538    writeInterfaceDecl                          (api, os.path.join(outputPath, "vkVirtualDeviceInterface.inl"), deviceFuncs, False)
4539    writeInterfaceDecl                          (api, os.path.join(outputPath, "vkConcretePlatformInterface.inl"), platformFuncs, True)
4540    writeInterfaceDecl                          (api, os.path.join(outputPath, "vkConcreteInstanceInterface.inl"), instanceFuncs, True)
4541    writeInterfaceDecl                          (api, os.path.join(outputPath, "vkConcreteDeviceInterface.inl"), deviceFuncs, True)
4542    writeFunctionPtrTypes                       (api, os.path.join(outputPath, "vkFunctionPointerTypes.inl"))
4543    writeFunctionPointers                       (api, os.path.join(outputPath, "vkPlatformFunctionPointers.inl"), platformFuncs)
4544    writeFunctionPointers                       (api, os.path.join(outputPath, "vkInstanceFunctionPointers.inl"), instanceFuncs)
4545    writeFunctionPointers                       (api, os.path.join(outputPath, "vkDeviceFunctionPointers.inl"), deviceFuncs)
4546    writeInitFunctionPointers                   (api, os.path.join(outputPath, "vkInitPlatformFunctionPointers.inl"), platformFuncs, lambda f: f.name != "vkGetInstanceProcAddr")
4547    writeInitFunctionPointers                   (api, os.path.join(outputPath, "vkInitInstanceFunctionPointers.inl"), instanceFuncs)
4548    writeInitFunctionPointers                   (api, os.path.join(outputPath, "vkInitDeviceFunctionPointers.inl"), deviceFuncs)
4549    writeFuncPtrInterfaceImpl                   (api, os.path.join(outputPath, "vkPlatformDriverImpl.inl"), platformFuncs, "PlatformDriver")
4550    writeFuncPtrInterfaceImpl                   (api, os.path.join(outputPath, "vkInstanceDriverImpl.inl"), instanceFuncs, "InstanceDriver")
4551    writeFuncPtrInterfaceImpl                   (api, os.path.join(outputPath, "vkDeviceDriverImpl.inl"), deviceFuncs, "DeviceDriver")
4552    writeStrUtilProto                           (api, os.path.join(outputPath, "vkStrUtil.inl"))
4553    writeStrUtilImpl                            (api, os.path.join(outputPath, "vkStrUtilImpl.inl"))
4554    writeRefUtilProto                           (api, os.path.join(outputPath, "vkRefUtil.inl"))
4555    writeRefUtilImpl                            (api, os.path.join(outputPath, "vkRefUtilImpl.inl"))
4556    writeStructTraitsImpl                       (api, os.path.join(outputPath, "vkGetStructureTypeImpl.inl"))
4557    writeNullDriverImpl                         (api, os.path.join(outputPath, "vkNullDriverImpl.inl"))
4558    writeTypeUtil                               (api, os.path.join(outputPath, "vkTypeUtil.inl"))
4559    writeSupportedExtensions                    (api, os.path.join(outputPath, "vkSupportedExtensions.inl"))
4560    writeCoreFunctionalities                    (api, os.path.join(outputPath, "vkCoreFunctionalities.inl"))
4561    writeExtensionFunctions                     (api, os.path.join(outputPath, "vkExtensionFunctions.inl"))
4562    writeDeviceFeatures2                        (api, os.path.join(outputPath, "vkDeviceFeatures2.inl"))
4563    writeMandatoryFeatures                      (api, os.path.join(outputPath, "vkMandatoryFeatures.inl"))
4564    writeExtensionList                          (api, os.path.join(outputPath, "vkInstanceExtensions.inl"), 'instance')
4565    writeExtensionList                          (api, os.path.join(outputPath, "vkDeviceExtensions.inl"), 'device')
4566    writeDriverIds                              (api, os.path.join(outputPath, "vkKnownDriverIds.inl"))
4567    writeObjTypeImpl                            (api, os.path.join(outputPath, "vkObjTypeImpl.inl"))
4568    writeApiExtensionDependencyInfo             (api, os.path.join(outputPath, "vkApiExtensionDependencyInfo.inl"))
4569    writeEntryPointValidation                   (api, os.path.join(outputPath, "vkEntryPointValidation.inl"))
4570    writeGetDeviceProcAddr                      (api, os.path.join(outputPath, "vkGetDeviceProcAddr.inl"))
4571    #writeConformanceVersions                    (api, os.path.join(outputPath, "vkKnownConformanceVersions.inl"))
4572    if args.api=='SC':
4573        writeFuncPtrInterfaceSCImpl(api, os.path.join(outputPath, "vkDeviceDriverSCImpl.inl"), deviceFuncs, "DeviceDriverSC")
4574    else:
4575        profileList = [os.path.join(VULKAN_XML_DIR, "profiles", "VP_KHR_roadmap.json")]
4576        #profileList += [os.path.join(VULKAN_XML_DIR, "profiles", "VP_ANDROID_baseline_2022.json"]
4577        writeProfileTests(os.path.join(outputPath, "vkProfileTests.inl"), profileList)
4578
4579    # NOTE: when new files are generated then they should also be added to the
4580    # vk-gl-cts\external\vulkancts\framework\vulkan\CMakeLists.txt outputs list
4581