Home | History | Annotate | Download | only in vulkan-validation-layers
      1 #!/usr/bin/python3 -i
      2 #
      3 # Copyright (c) 2015-2016 The Khronos Group Inc.
      4 # Copyright (c) 2015-2016 Valve Corporation
      5 # Copyright (c) 2015-2016 LunarG, Inc.
      6 # Copyright (c) 2015-2016 Google Inc.
      7 #
      8 # Licensed under the Apache License, Version 2.0 (the "License");
      9 # you may not use this file except in compliance with the License.
     10 # You may obtain a copy of the License at
     11 #
     12 #     http://www.apache.org/licenses/LICENSE-2.0
     13 #
     14 # Unless required by applicable law or agreed to in writing, software
     15 # distributed under the License is distributed on an "AS IS" BASIS,
     16 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     17 # See the License for the specific language governing permissions and
     18 # limitations under the License.
     19 #
     20 # Author: Dustin Graves <dustin (at] lunarg.com>
     21 
     22 import os,re,sys
     23 import xml.etree.ElementTree as etree
     24 from generator import *
     25 from collections import namedtuple
     26 
     27 
     28 # ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
     29 #
     30 # Adds options used by ParamCheckerOutputGenerator object during Parameter
     31 # validation layer generation.
     32 #
     33 # Additional members
     34 #   prefixText - list of strings to prefix generated header with
     35 #     (usually a copyright statement + calling convention macros).
     36 #   protectFile - True if multiple inclusion protection should be
     37 #     generated (based on the filename) around the entire header.
     38 #   protectFeature - True if #ifndef..#endif protection should be
     39 #     generated around a feature interface in the header file.
     40 #   genFuncPointers - True if function pointer typedefs should be
     41 #     generated
     42 #   protectProto - If conditional protection should be generated
     43 #     around prototype declarations, set to either '#ifdef'
     44 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
     45 #     to require opt-out (#ifndef protectProtoStr). Otherwise
     46 #     set to None.
     47 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
     48 #     declarations, if protectProto is set
     49 #   apicall - string to use for the function declaration prefix,
     50 #     such as APICALL on Windows.
     51 #   apientry - string to use for the calling convention macro,
     52 #     in typedefs, such as APIENTRY.
     53 #   apientryp - string to use for the calling convention macro
     54 #     in function pointer typedefs, such as APIENTRYP.
     55 #   indentFuncProto - True if prototype declarations should put each
     56 #     parameter on a separate line
     57 #   indentFuncPointer - True if typedefed function pointers should put each
     58 #     parameter on a separate line
     59 #   alignFuncParam - if nonzero and parameters are being put on a
     60 #     separate line, align parameter names at the specified column
     61 class ParamCheckerGeneratorOptions(GeneratorOptions):
     62     def __init__(self,
     63                  filename = None,
     64                  directory = '.',
     65                  apiname = None,
     66                  profile = None,
     67                  versions = '.*',
     68                  emitversions = '.*',
     69                  defaultExtensions = None,
     70                  addExtensions = None,
     71                  removeExtensions = None,
     72                  sortProcedure = regSortFeatures,
     73                  prefixText = "",
     74                  genFuncPointers = True,
     75                  protectFile = True,
     76                  protectFeature = True,
     77                  protectProto = None,
     78                  protectProtoStr = None,
     79                  apicall = '',
     80                  apientry = '',
     81                  apientryp = '',
     82                  indentFuncProto = True,
     83                  indentFuncPointer = False,
     84                  alignFuncParam = 0):
     85         GeneratorOptions.__init__(self, filename, directory, apiname, profile,
     86                                   versions, emitversions, defaultExtensions,
     87                                   addExtensions, removeExtensions, sortProcedure)
     88         self.prefixText      = prefixText
     89         self.genFuncPointers = genFuncPointers
     90         self.protectFile     = protectFile
     91         self.protectFeature  = protectFeature
     92         self.protectProto    = protectProto
     93         self.protectProtoStr = protectProtoStr
     94         self.apicall         = apicall
     95         self.apientry        = apientry
     96         self.apientryp       = apientryp
     97         self.indentFuncProto = indentFuncProto
     98         self.indentFuncPointer = indentFuncPointer
     99         self.alignFuncParam  = alignFuncParam
    100 
    101 # ParamCheckerOutputGenerator - subclass of OutputGenerator.
    102 # Generates param checker layer code.
    103 #
    104 # ---- methods ----
    105 # ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
    106 #   OutputGenerator. Defines additional internal state.
    107 # ---- methods overriding base class ----
    108 # beginFile(genOpts)
    109 # endFile()
    110 # beginFeature(interface, emit)
    111 # endFeature()
    112 # genType(typeinfo,name)
    113 # genStruct(typeinfo,name)
    114 # genGroup(groupinfo,name)
    115 # genEnum(enuminfo, name)
    116 # genCmd(cmdinfo)
    117 class ParamCheckerOutputGenerator(OutputGenerator):
    118     """Generate ParamChecker code based on XML element attributes"""
    119     # This is an ordered list of sections in the header file.
    120     ALL_SECTIONS = ['command']
    121     def __init__(self,
    122                  errFile = sys.stderr,
    123                  warnFile = sys.stderr,
    124                  diagFile = sys.stdout):
    125         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
    126         self.INDENT_SPACES = 4
    127         # Commands to ignore
    128         self.blacklist = [
    129             'vkGetInstanceProcAddr',
    130             'vkGetDeviceProcAddr',
    131             'vkEnumerateInstanceLayerProperties',
    132             'vkEnumerateInstanceExtensionsProperties',
    133             'vkEnumerateDeviceLayerProperties',
    134             'vkEnumerateDeviceExtensionsProperties',
    135             'vkCreateDebugReportCallbackEXT',
    136             'vkDebugReportMessageEXT']
    137         # Validation conditions for some special case struct members that are conditionally validated
    138         self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
    139         # Header version
    140         self.headerVersion = None
    141         # Internal state - accumulators for different inner block text
    142         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    143         self.structNames = []                             # List of Vulkan struct typenames
    144         self.stypes = []                                  # Values from the VkStructureType enumeration
    145         self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType
    146         self.handleTypes = set()                          # Set of handle type names
    147         self.commands = []                                # List of CommandData records for all Vulkan commands
    148         self.structMembers = []                           # List of StructMemberData records for all Vulkan structs
    149         self.validatedStructs = dict()                    # Map of structs type names to generated validation code for that struct type
    150         self.enumRanges = dict()                          # Map of enum name to BEGIN/END range values
    151         self.flags = set()                                # Map of flags typenames
    152         self.flagBits = dict()                            # Map of flag bits typename to list of values
    153         # Named tuples to store struct and command data
    154         self.StructType = namedtuple('StructType', ['name', 'value'])
    155         self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
    156                                                         'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
    157                                                         'condition', 'cdecl'])
    158         self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
    159         self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
    160     #
    161     def incIndent(self, indent):
    162         inc = ' ' * self.INDENT_SPACES
    163         if indent:
    164             return indent + inc
    165         return inc
    166     #
    167     def decIndent(self, indent):
    168         if indent and (len(indent) > self.INDENT_SPACES):
    169             return indent[:-self.INDENT_SPACES]
    170         return ''
    171     #
    172     def beginFile(self, genOpts):
    173         OutputGenerator.beginFile(self, genOpts)
    174         # C-specific
    175         #
    176         # User-supplied prefix text, if any (list of strings)
    177         if (genOpts.prefixText):
    178             for s in genOpts.prefixText:
    179                 write(s, file=self.outFile)
    180         #
    181         # Multiple inclusion protection & C++ wrappers.
    182         if (genOpts.protectFile and self.genOpts.filename):
    183             headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
    184             write('#ifndef', headerSym, file=self.outFile)
    185             write('#define', headerSym, '1', file=self.outFile)
    186             self.newline()
    187         #
    188         # Headers
    189         write('#include <string>', file=self.outFile)
    190         self.newline()
    191         write('#include "vulkan/vulkan.h"', file=self.outFile)
    192         write('#include "vk_layer_extension_utils.h"', file=self.outFile)
    193         write('#include "parameter_validation_utils.h"', file=self.outFile)
    194         #
    195         # Macros
    196         self.newline()
    197         write('#ifndef UNUSED_PARAMETER', file=self.outFile)
    198         write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
    199         write('#endif // UNUSED_PARAMETER', file=self.outFile)
    200         #
    201         # Namespace
    202         self.newline()
    203         write('namespace parameter_validation {', file = self.outFile)
    204     def endFile(self):
    205         # C-specific
    206         self.newline()
    207         # Namespace
    208         write('} // namespace parameter_validation', file = self.outFile)
    209         # Finish C++ wrapper and multiple inclusion protection
    210         if (self.genOpts.protectFile and self.genOpts.filename):
    211             self.newline()
    212             write('#endif', file=self.outFile)
    213         # Finish processing in superclass
    214         OutputGenerator.endFile(self)
    215     def beginFeature(self, interface, emit):
    216         # Start processing in superclass
    217         OutputGenerator.beginFeature(self, interface, emit)
    218         # C-specific
    219         # Accumulate includes, defines, types, enums, function pointer typedefs,
    220         # end function prototypes separately for this feature. They're only
    221         # printed in endFeature().
    222         self.headerVersion = None
    223         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
    224         self.structNames = []
    225         self.stypes = []
    226         self.structTypes = dict()
    227         self.handleTypes = set()
    228         self.commands = []
    229         self.structMembers = []
    230         self.validatedStructs = dict()
    231         self.enumRanges = dict()
    232         self.flags = set()
    233         self.flagBits = dict()
    234     def endFeature(self):
    235         # C-specific
    236         # Actually write the interface to the output file.
    237         if (self.emit):
    238             self.newline()
    239             # If type declarations are needed by other features based on
    240             # this one, it may be necessary to suppress the ExtraProtect,
    241             # or move it below the 'for section...' loop.
    242             if (self.featureExtraProtect != None):
    243                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
    244             # Generate the struct member checking code from the captured data
    245             self.processStructMemberData()
    246             # Generate the command parameter checking code from the captured data
    247             self.processCmdData()
    248             # Write the declaration for the HeaderVersion
    249             if self.headerVersion:
    250                 write('const uint32_t GeneratedHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
    251                 self.newline()
    252             # Write the declarations for the VkFlags values combining all flag bits
    253             for flag in sorted(self.flags):
    254                 flagBits = flag.replace('Flags', 'FlagBits')
    255                 if flagBits in self.flagBits:
    256                     bits = self.flagBits[flagBits]
    257                     decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
    258                     for bit in bits[1:]:
    259                         decl += '|' + bit
    260                     decl += ';'
    261                     write(decl, file=self.outFile)
    262             self.newline()
    263             # Write the parameter validation code to the file
    264             if (self.sections['command']):
    265                 if (self.genOpts.protectProto):
    266                     write(self.genOpts.protectProto,
    267                           self.genOpts.protectProtoStr, file=self.outFile)
    268                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
    269             if (self.featureExtraProtect != None):
    270                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
    271             else:
    272                 self.newline()
    273         # Finish processing in superclass
    274         OutputGenerator.endFeature(self)
    275     #
    276     # Append a definition to the specified section
    277     def appendSection(self, section, text):
    278         # self.sections[section].append('SECTION: ' + section + '\n')
    279         self.sections[section].append(text)
    280     #
    281     # Type generation
    282     def genType(self, typeinfo, name):
    283         OutputGenerator.genType(self, typeinfo, name)
    284         typeElem = typeinfo.elem
    285         # If the type is a struct type, traverse the imbedded <member> tags
    286         # generating a structure. Otherwise, emit the tag text.
    287         category = typeElem.get('category')
    288         if (category == 'struct' or category == 'union'):
    289             self.structNames.append(name)
    290             self.genStruct(typeinfo, name)
    291         elif (category == 'handle'):
    292             self.handleTypes.add(name)
    293         elif (category == 'bitmask'):
    294             self.flags.add(name)
    295         elif (category == 'define'):
    296             if name == 'VK_HEADER_VERSION':
    297                 nameElem = typeElem.find('name')
    298                 self.headerVersion = noneStr(nameElem.tail).strip()
    299     #
    300     # Struct parameter check generation.
    301     # This is a special case of the <type> tag where the contents are
    302     # interpreted as a set of <member> tags instead of freeform C
    303     # C type declarations. The <member> tags are just like <param>
    304     # tags - they are a declaration of a struct or union member.
    305     # Only simple member declarations are supported (no nested
    306     # structs etc.)
    307     def genStruct(self, typeinfo, typeName):
    308         OutputGenerator.genStruct(self, typeinfo, typeName)
    309         conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
    310         members = typeinfo.elem.findall('.//member')
    311         #
    312         # Iterate over members once to get length parameters for arrays
    313         lens = set()
    314         for member in members:
    315             len = self.getLen(member)
    316             if len:
    317                 lens.add(len)
    318         #
    319         # Generate member info
    320         membersInfo = []
    321         for member in members:
    322             # Get the member's type and name
    323             info = self.getTypeNameTuple(member)
    324             type = info[0]
    325             name = info[1]
    326             stypeValue = ''
    327             cdecl = self.makeCParamDecl(member, 0)
    328             # Process VkStructureType
    329             if type == 'VkStructureType':
    330                 # Extract the required struct type value from the comments
    331                 # embedded in the original text defining the 'typeinfo' element
    332                 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
    333                 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
    334                 if result:
    335                     value = result.group(0)
    336                 else:
    337                     value = self.genVkStructureType(typeName)
    338                 # Store the required type value
    339                 self.structTypes[typeName] = self.StructType(name=name, value=value)
    340             #
    341             # Store pointer/array/string info
    342             # Check for parameter name in lens set
    343             iscount = False
    344             if name in lens:
    345                 iscount = True
    346             # The pNext members are not tagged as optional, but are treated as
    347             # optional for parameter NULL checks.  Static array members
    348             # are also treated as optional to skip NULL pointer validation, as
    349             # they won't be NULL.
    350             isstaticarray = self.paramIsStaticArray(member)
    351             isoptional = False
    352             if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
    353                 isoptional = True
    354             membersInfo.append(self.CommandParam(type=type, name=name,
    355                                                 ispointer=self.paramIsPointer(member),
    356                                                 isstaticarray=isstaticarray,
    357                                                 isbool=True if type == 'VkBool32' else False,
    358                                                 israngedenum=True if type in self.enumRanges else False,
    359                                                 isconst=True if 'const' in cdecl else False,
    360                                                 isoptional=isoptional,
    361                                                 iscount=iscount,
    362                                                 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
    363                                                 len=self.getLen(member),
    364                                                 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
    365                                                 condition=conditions[name] if conditions and name in conditions else None,
    366                                                 cdecl=cdecl))
    367         self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
    368     #
    369     # Capture group (e.g. C "enum" type) info to be used for
    370     # param check code generation.
    371     # These are concatenated together with other types.
    372     def genGroup(self, groupinfo, groupName):
    373         OutputGenerator.genGroup(self, groupinfo, groupName)
    374         groupElem = groupinfo.elem
    375         #
    376         # Store the sType values
    377         if groupName == 'VkStructureType':
    378             for elem in groupElem.findall('enum'):
    379                 self.stypes.append(elem.get('name'))
    380         elif 'FlagBits' in groupName:
    381             bits = []
    382             for elem in groupElem.findall('enum'):
    383                 bits.append(elem.get('name'))
    384             if bits:
    385                 self.flagBits[groupName] = bits
    386         else:
    387             # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
    388             expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
    389             expandPrefix = expandName
    390             expandSuffix = ''
    391             expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
    392             if expandSuffixMatch:
    393                 expandSuffix = '_' + expandSuffixMatch.group()
    394                 # Strip off the suffix from the prefix
    395                 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
    396             isEnum = ('FLAG_BITS' not in expandPrefix)
    397             if isEnum:
    398                 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
    399     #
    400     # Capture command parameter info to be used for param
    401     # check code generation.
    402     def genCmd(self, cmdinfo, name):
    403         OutputGenerator.genCmd(self, cmdinfo, name)
    404         if name not in self.blacklist:
    405             params = cmdinfo.elem.findall('param')
    406             # Get list of array lengths
    407             lens = set()
    408             for param in params:
    409                 len = self.getLen(param)
    410                 if len:
    411                     lens.add(len)
    412             # Get param info
    413             paramsInfo = []
    414             for param in params:
    415                 paramInfo = self.getTypeNameTuple(param)
    416                 cdecl = self.makeCParamDecl(param, 0)
    417                 # Check for parameter name in lens set
    418                 iscount = False
    419                 if paramInfo[1] in lens:
    420                     iscount = True
    421                 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
    422                                                     ispointer=self.paramIsPointer(param),
    423                                                     isstaticarray=self.paramIsStaticArray(param),
    424                                                     isbool=True if paramInfo[0] == 'VkBool32' else False,
    425                                                     israngedenum=True if paramInfo[0] in self.enumRanges else False,
    426                                                     isconst=True if 'const' in cdecl else False,
    427                                                     isoptional=self.paramIsOptional(param),
    428                                                     iscount=iscount,
    429                                                     noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
    430                                                     len=self.getLen(param),
    431                                                     extstructs=None,
    432                                                     condition=None,
    433                                                     cdecl=cdecl))
    434             self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
    435     #
    436     # Check if the parameter passed in is a pointer
    437     def paramIsPointer(self, param):
    438         ispointer = 0
    439         paramtype = param.find('type')
    440         if (paramtype.tail is not None) and ('*' in paramtype.tail):
    441             ispointer = paramtype.tail.count('*')
    442         elif paramtype.text[:4] == 'PFN_':
    443             # Treat function pointer typedefs as a pointer to a single value
    444             ispointer = 1
    445         return ispointer
    446     #
    447     # Check if the parameter passed in is a static array
    448     def paramIsStaticArray(self, param):
    449         isstaticarray = 0
    450         paramname = param.find('name')
    451         if (paramname.tail is not None) and ('[' in paramname.tail):
    452             isstaticarray = paramname.tail.count('[')
    453         return isstaticarray
    454     #
    455     # Check if the parameter passed in is optional
    456     # Returns a list of Boolean values for comma separated len attributes (len='false,true')
    457     def paramIsOptional(self, param):
    458         # See if the handle is optional
    459         isoptional = False
    460         # Simple, if it's optional, return true
    461         optString = param.attrib.get('optional')
    462         if optString:
    463             if optString == 'true':
    464                 isoptional = True
    465             elif ',' in optString:
    466                 opts = []
    467                 for opt in optString.split(','):
    468                     val = opt.strip()
    469                     if val == 'true':
    470                         opts.append(True)
    471                     elif val == 'false':
    472                         opts.append(False)
    473                     else:
    474                         print('Unrecognized len attribute value',val)
    475                 isoptional = opts
    476         return isoptional
    477     #
    478     # Check if the handle passed in is optional
    479     # Uses the same logic as ValidityOutputGenerator.isHandleOptional
    480     def isHandleOptional(self, param, lenParam):
    481         # Simple, if it's optional, return true
    482         if param.isoptional:
    483             return True
    484         # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
    485         if param.noautovalidity:
    486             return True
    487         # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
    488         if lenParam and lenParam.isoptional:
    489             return True
    490         return False
    491     #
    492     # Generate a VkStructureType based on a structure typename
    493     def genVkStructureType(self, typename):
    494         # Add underscore between lowercase then uppercase
    495         value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
    496         # Change to uppercase
    497         value = value.upper()
    498         # Add STRUCTURE_TYPE_
    499         return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
    500     #
    501     # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
    502     # value assuming the struct is defined by a different feature
    503     def getStructType(self, typename):
    504         value = None
    505         if typename in self.structTypes:
    506             value = self.structTypes[typename].value
    507         else:
    508             value = self.genVkStructureType(typename)
    509             self.logMsg('diag', 'ParameterValidation: Generating {} for {} structure type that was not defined by the current feature'.format(value, typename))
    510         return value
    511     #
    512     # Retrieve the value of the len tag
    513     def getLen(self, param):
    514         result = None
    515         len = param.attrib.get('len')
    516         if len and len != 'null-terminated':
    517             # For string arrays, 'len' can look like 'count,null-terminated',
    518             # indicating that we have a null terminated array of strings.  We
    519             # strip the null-terminated from the 'len' field and only return
    520             # the parameter specifying the string count
    521             if 'null-terminated' in len:
    522                 result = len.split(',')[0]
    523             else:
    524                 result = len
    525             result = str(result).replace('::', '->')
    526         return result
    527     #
    528     # Retrieve the type and name for a parameter
    529     def getTypeNameTuple(self, param):
    530         type = ''
    531         name = ''
    532         for elem in param:
    533             if elem.tag == 'type':
    534                 type = noneStr(elem.text)
    535             elif elem.tag == 'name':
    536                 name = noneStr(elem.text)
    537         return (type, name)
    538     #
    539     # Find a named parameter in a parameter list
    540     def getParamByName(self, params, name):
    541         for param in params:
    542             if param.name == name:
    543                 return param
    544         return None
    545     #
    546     # Extract length values from latexmath.  Currently an inflexible solution that looks for specific
    547     # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced.
    548     def parseLateXMath(self, source):
    549         name = 'ERROR'
    550         decoratedName = 'ERROR'
    551         if 'mathit' in source:
    552             # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
    553             match = re.match(r'latexmath\s*\:\s*\[\s*\$\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\$\s*\]', source)
    554             if not match or match.group(1) != match.group(4):
    555                 raise 'Unrecognized latexmath expression'
    556             name = match.group(2)
    557             decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
    558         else:
    559             # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
    560             match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
    561             name = match.group(1)
    562             decoratedName = '{}/{}'.format(*match.group(1, 2))
    563         return name, decoratedName
    564     #
    565     # Get the length paramater record for the specified parameter name
    566     def getLenParam(self, params, name):
    567         lenParam = None
    568         if name:
    569             if '->' in name:
    570                 # The count is obtained by dereferencing a member of a struct parameter
    571                 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
    572                                              isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
    573                                              condition=None, cdecl=None)
    574             elif 'latexmath' in name:
    575                 lenName, decoratedName = self.parseLateXMath(name)
    576                 lenParam = self.getParamByName(params, lenName)
    577                 # TODO: Zero-check the result produced by the equation?
    578                 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
    579                 #param = self.getParamByName(params, lenName)
    580                 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
    581                 #                             isoptional=param.isoptional, type=param.type, len=param.len,
    582                 #                             isstaticarray=param.isstaticarray, extstructs=param.extstructs,
    583                 #                             noautovalidity=True, condition=None, cdecl=param.cdecl)
    584             else:
    585                 lenParam = self.getParamByName(params, name)
    586         return lenParam
    587     #
    588     # Convert a vulkan.h command declaration into a parameter_validation.h definition
    589     def getCmdDef(self, cmd):
    590         #
    591         # Strip the trailing ';' and split into individual lines
    592         lines = cmd.cdecl[:-1].split('\n')
    593         # Replace Vulkan prototype
    594         lines[0] = 'static bool parameter_validation_' + cmd.name + '('
    595         # Replace the first argument with debug_report_data, when the first
    596         # argument is a handle (not vkCreateInstance)
    597         reportData = '    debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
    598         if cmd.name != 'vkCreateInstance':
    599             lines[1] = reportData
    600         else:
    601             lines.insert(1, reportData)
    602         return '\n'.join(lines)
    603     #
    604     # Generate the code to check for a NULL dereference before calling the
    605     # validation function
    606     def genCheckedLengthCall(self, name, exprs):
    607         count = name.count('->')
    608         if count:
    609             checkedExpr = []
    610             localIndent = ''
    611             elements = name.split('->')
    612             # Open the if expression blocks
    613             for i in range(0, count):
    614                 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
    615                 localIndent = self.incIndent(localIndent)
    616             # Add the validation expression
    617             for expr in exprs:
    618                 checkedExpr.append(localIndent + expr)
    619             # Close the if blocks
    620             for i in range(0, count):
    621                 localIndent = self.decIndent(localIndent)
    622                 checkedExpr.append(localIndent + '}\n')
    623             return [checkedExpr]
    624         # No if statements were required
    625         return exprs
    626     #
    627     # Generate code to check for a specific condition before executing validation code
    628     def genConditionalCall(self, prefix, condition, exprs):
    629         checkedExpr = []
    630         localIndent = ''
    631         formattedCondition = condition.format(prefix)
    632         checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
    633         checkedExpr.append(localIndent + '{\n')
    634         localIndent = self.incIndent(localIndent)
    635         for expr in exprs:
    636             checkedExpr.append(localIndent + expr)
    637         localIndent = self.decIndent(localIndent)
    638         checkedExpr.append(localIndent + '}\n')
    639         return [checkedExpr]
    640     #
    641     # Generate the sType check string
    642     def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
    643         checkExpr = []
    644         stype = self.structTypes[value.type]
    645         if lenValue:
    646             # This is an array with a pointer to a count value
    647             if lenValue.ispointer:
    648                 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
    649                 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
    650                     funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
    651             # This is an array with an integer count value
    652             else:
    653                 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
    654                     funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
    655         # This is an individual struct
    656         else:
    657             checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {});\n'.format(
    658                 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value, **postProcSpec))
    659         return checkExpr
    660     #
    661     # Generate the handle check string
    662     def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
    663         checkExpr = []
    664         if lenValue:
    665             if lenValue.ispointer:
    666                 # This is assumed to be an output array with a pointer to a count value
    667                 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
    668             else:
    669                 # This is an array with an integer count value
    670                 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
    671                     funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
    672         else:
    673             # This is assumed to be an output handle pointer
    674             raise('Unsupported parameter validation case: Output handles are not NULL checked')
    675         return checkExpr
    676     #
    677     # Generate check string for an array of VkFlags values
    678     def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
    679         checkExpr = []
    680         flagBitsName = value.type.replace('Flags', 'FlagBits')
    681         if not flagBitsName in self.flagBits:
    682             raise('Unsupported parameter validation case: array of reserved VkFlags')
    683         else:
    684             allFlags = 'All' + flagBitsName
    685             checkExpr.append('skipCall |= validate_flags_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
    686         return checkExpr
    687     #
    688     # Generate pNext check string
    689     def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec):
    690         checkExpr = []
    691         # Generate an array of acceptable VkStructureType values for pNext
    692         extStructCount = 0
    693         extStructVar = 'NULL'
    694         extStructNames = 'NULL'
    695         if value.extstructs:
    696             structs = value.extstructs.split(',')
    697             checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
    698             extStructCount = 'ARRAY_SIZE(allowedStructs)'
    699             extStructVar = 'allowedStructs'
    700             extStructNames = '"' + ', '.join(structs) + '"'
    701         checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion);\n'.format(
    702             funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, **postProcSpec))
    703         return checkExpr
    704     #
    705     # Generate the pointer check string
    706     def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
    707         checkExpr = []
    708         if lenValue:
    709             # This is an array with a pointer to a count value
    710             if lenValue.ispointer:
    711                 # If count and array parameters are optional, there will be no validation
    712                 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
    713                     # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
    714                     checkExpr.append('skipCall |= validate_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
    715                         funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
    716             # This is an array with an integer count value
    717             else:
    718                 # If count and array parameters are optional, there will be no validation
    719                 if valueRequired == 'true' or lenValueRequired == 'true':
    720                     # Arrays of strings receive special processing
    721                     validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
    722                     checkExpr.append('skipCall |= {}(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
    723                         validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
    724             if checkExpr:
    725                 if lenValue and ('->' in lenValue.name):
    726                     # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
    727                     checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
    728         # This is an individual struct that is not allowed to be NULL
    729         elif not value.isoptional:
    730             # Function pointers need a reinterpret_cast to void*
    731             if value.type[:4] == 'PFN_':
    732                 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
    733             else:
    734                 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
    735         return checkExpr
    736     #
    737     # Process struct member validation code, performing name suibstitution if required
    738     def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
    739         # Build format specifier list
    740         kwargs = {}
    741         if '{postProcPrefix}' in line:
    742             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
    743             if type(memberDisplayNamePrefix) is tuple:
    744                 kwargs['postProcPrefix'] = 'ParameterName('
    745             else:
    746                 kwargs['postProcPrefix'] = postProcSpec['ppp']
    747         if '{postProcSuffix}' in line:
    748             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
    749             if type(memberDisplayNamePrefix) is tuple:
    750                 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
    751             else:
    752                 kwargs['postProcSuffix'] = postProcSpec['pps']
    753         if '{postProcInsert}' in line:
    754             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
    755             if type(memberDisplayNamePrefix) is tuple:
    756                 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
    757             else:
    758                 kwargs['postProcInsert'] = postProcSpec['ppi']
    759         if '{funcName}' in line:
    760             kwargs['funcName'] = funcName
    761         if '{valuePrefix}' in line:
    762             kwargs['valuePrefix'] = memberNamePrefix
    763         if '{displayNamePrefix}' in line:
    764             # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
    765             if type(memberDisplayNamePrefix) is tuple:
    766                 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
    767             else:
    768                 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
    769 
    770         if kwargs:
    771             # Need to escape the C++ curly braces
    772             if 'IndexVector' in line:
    773                 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
    774                 line = line.replace(' }),', ' }}),')
    775             return line.format(**kwargs)
    776         return line
    777     #
    778     # Process struct validation code for inclusion in function or parent struct validation code
    779     def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
    780         for line in lines:
    781             if output:
    782                 output[-1] += '\n'
    783             if type(line) is list:
    784                 for sub in line:
    785                     output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
    786             else:
    787                 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
    788         return output
    789     #
    790     # Process struct pointer/array validation code, perfoeming name substitution if required
    791     def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
    792         expr = []
    793         expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
    794         expr.append('{')
    795         indent = self.incIndent(None)
    796         if lenValue:
    797             # Need to process all elements in the array
    798             indexName = lenValue.name.replace('Count', 'Index')
    799             expr[-1] += '\n'
    800             expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
    801             expr.append(indent + '{')
    802             indent = self.incIndent(indent)
    803             # Prefix for value name to display in error message
    804             memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
    805             memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
    806         else:
    807             memberNamePrefix = '{}{}->'.format(prefix, value.name)
    808             memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
    809         #
    810         # Expand the struct validation lines
    811         expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
    812         #
    813         if lenValue:
    814             # Close if and for scopes
    815             indent = self.decIndent(indent)
    816             expr.append(indent + '}\n')
    817         expr.append('}\n')
    818         return expr
    819     #
    820     # Generate the parameter checking code
    821     def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
    822         lines = []    # Generated lines of code
    823         unused = []   # Unused variable names
    824         for value in values:
    825             usedLines = []
    826             lenParam = None
    827             #
    828             # Prefix and suffix for post processing of parameter names for struct members.  Arrays of structures need special processing to include the array index in the full parameter name.
    829             postProcSpec = {}
    830             postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
    831             postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
    832             postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
    833             #
    834             # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
    835             valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
    836             #
    837             # Check for NULL pointers, ignore the inout count parameters that
    838             # will be validated with their associated array
    839             if (value.ispointer or value.isstaticarray) and not value.iscount:
    840                 #
    841                 # Parameters for function argument generation
    842                 req = 'true'    # Paramerter cannot be NULL
    843                 cpReq = 'true'  # Count pointer cannot be NULL
    844                 cvReq = 'true'  # Count value cannot be 0
    845                 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
    846                 #
    847                 # Generate required/optional parameter strings for the pointer and count values
    848                 if value.isoptional:
    849                     req = 'false'
    850                 if value.len:
    851                     # The parameter is an array with an explicit count parameter
    852                     lenParam = self.getLenParam(values, value.len)
    853                     lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
    854                     if lenParam.ispointer:
    855                         # Count parameters that are pointers are inout
    856                         if type(lenParam.isoptional) is list:
    857                             if lenParam.isoptional[0]:
    858                                 cpReq = 'false'
    859                             if lenParam.isoptional[1]:
    860                                 cvReq = 'false'
    861                         else:
    862                             if lenParam.isoptional:
    863                                 cpReq = 'false'
    864                     else:
    865                         if lenParam.isoptional:
    866                             cvReq = 'false'
    867                 #
    868                 # The parameter will not be processes when tagged as 'noautovalidity'
    869                 # For the pointer to struct case, the struct pointer will not be validated, but any
    870                 # members not tagged as 'noatuvalidity' will be validated
    871                 if value.noautovalidity:
    872                     # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
    873                     self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
    874                 else:
    875                     #
    876                     # If this is a pointer to a struct with an sType field, verify the type
    877                     if value.type in self.structTypes:
    878                         usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
    879                     # If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
    880                     elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
    881                         usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
    882                     elif value.type in self.flags and value.isconst:
    883                         usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
    884                     elif value.isbool and value.isconst:
    885                         usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
    886                     elif value.israngedenum and value.isconst:
    887                         enumRange = self.enumRanges[value.type]
    888                         usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
    889                     elif value.name == 'pNext':
    890                         # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
    891                         if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
    892                             usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec)
    893                     else:
    894                         usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
    895                     #
    896                     # If this is a pointer to a struct (input), see if it contains members that need to be checked
    897                     if value.type in self.validatedStructs and value.isconst:
    898                         usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
    899             # Non-pointer types
    900             else:
    901                 #
    902                 # The parameter will not be processes when tagged as 'noautovalidity'
    903                 # For the struct case, the struct type will not be validated, but any
    904                 # members not tagged as 'noatuvalidity' will be validated
    905                 if value.noautovalidity:
    906                     # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
    907                     self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
    908                 else:
    909                     if value.type in self.structTypes:
    910                         stype = self.structTypes[value.type]
    911                         usedLines.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false);\n'.format(
    912                             funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value, **postProcSpec))
    913                     elif value.type in self.handleTypes:
    914                         if not self.isHandleOptional(value, None):
    915                             usedLines.append('skipCall |= validate_required_handle(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
    916                     elif value.type in self.flags:
    917                         flagBitsName = value.type.replace('Flags', 'FlagBits')
    918                         if not flagBitsName in self.flagBits:
    919                             usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", {ppp}"{}"{pps}, {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix, **postProcSpec))
    920                         else:
    921                             flagsRequired = 'false' if value.isoptional else 'true'
    922                             allFlagsName = 'All' + flagBitsName
    923                             usedLines.append('skipCall |= validate_flags(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix, **postProcSpec))
    924                     elif value.isbool:
    925                         usedLines.append('skipCall |= validate_bool32(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
    926                     elif value.israngedenum:
    927                         enumRange = self.enumRanges[value.type]
    928                         usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name, **postProcSpec))
    929                     #
    930                     # If this is a struct, see if it contains members that need to be checked
    931                     if value.type in self.validatedStructs:
    932                         memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
    933                         memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
    934                         usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
    935             #
    936             # Append the parameter check to the function body for the current command
    937             if usedLines:
    938                 # Apply special conditional checks
    939                 if value.condition:
    940                     usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
    941                 lines += usedLines
    942             elif not value.iscount:
    943                 # If no expression was generated for this value, it is unreferenced by the validation function, unless
    944                 # it is an array count, which is indirectly referenced for array valiadation.
    945                 unused.append(value.name)
    946         return lines, unused
    947     #
    948     # Generate the struct member check code from the captured data
    949     def processStructMemberData(self):
    950         indent = self.incIndent(None)
    951         for struct in self.structMembers:
    952             #
    953             # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
    954             lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
    955             if lines:
    956                 self.validatedStructs[struct.name] = lines
    957     #
    958     # Generate the command param check code from the captured data
    959     def processCmdData(self):
    960         indent = self.incIndent(None)
    961         for command in self.commands:
    962             # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
    963             startIndex = 0 if command.name == 'vkCreateInstance' else 1
    964             lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
    965             if lines:
    966                 cmdDef = self.getCmdDef(command) + '\n'
    967                 cmdDef += '{\n'
    968                 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
    969                 # processed by parameter_validation (except for vkCreateInstance, which does not have a
    970                 # handle as its first parameter)
    971                 if unused:
    972                     for name in unused:
    973                         cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
    974                     if len(unused) > 0:
    975                         cmdDef += '\n'
    976                 cmdDef += indent + 'bool skipCall = false;\n'
    977                 for line in lines:
    978                     cmdDef += '\n'
    979                     if type(line) is list:
    980                         for sub in line:
    981                             cmdDef += indent + sub
    982                     else:
    983                         cmdDef += indent + line
    984                 cmdDef += '\n'
    985                 cmdDef += indent + 'return skipCall;\n'
    986                 cmdDef += '}\n'
    987                 self.appendSection('command', cmdDef)
    988