tests: Fix LayoutFromPresentWithoutAccessMemoryRead
[platform/upstream/Vulkan-LoaderAndValidationLayers.git] / generator.py
1 #!/usr/bin/python3 -i
2 #
3 # Copyright (c) 2013-2016 The Khronos Group Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import os,re,sys
18 from collections import namedtuple
19 import xml.etree.ElementTree as etree
20
21 def write( *args, **kwargs ):
22     file = kwargs.pop('file',sys.stdout)
23     end = kwargs.pop( 'end','\n')
24     file.write( ' '.join([str(arg) for arg in args]) )
25     file.write( end )
26
27 # noneStr - returns string argument, or "" if argument is None.
28 # Used in converting etree Elements into text.
29 #   str - string to convert
30 def noneStr(str):
31     if (str):
32         return str
33     else:
34         return ""
35
36 # enquote - returns string argument with surrounding quotes,
37 #   for serialization into Python code.
38 def enquote(str):
39     if (str):
40         return "'" + str + "'"
41     else:
42         return None
43
44 # Primary sort key for regSortFeatures.
45 # Sorts by category of the feature name string:
46 #   Core API features (those defined with a <feature> tag)
47 #   ARB/KHR/OES (Khronos extensions)
48 #   other       (EXT/vendor extensions)
49 # This will need changing for Vulkan!
50 def regSortCategoryKey(feature):
51     if (feature.elem.tag == 'feature'):
52         return 0
53     elif (feature.category == 'ARB' or
54           feature.category == 'KHR' or
55           feature.category == 'OES'):
56         return 1
57     else:
58         return 2
59
60 # Secondary sort key for regSortFeatures.
61 # Sorts by extension name.
62 def regSortNameKey(feature):
63     return feature.name
64
65 # Second sort key for regSortFeatures.
66 # Sorts by feature version. <extension> elements all have version number "0"
67 def regSortFeatureVersionKey(feature):
68     return float(feature.version)
69
70 # Tertiary sort key for regSortFeatures.
71 # Sorts by extension number. <feature> elements all have extension number 0.
72 def regSortExtensionNumberKey(feature):
73     return int(feature.number)
74
75 # regSortFeatures - default sort procedure for features.
76 # Sorts by primary key of feature category ('feature' or 'extension')
77 #   then by version number (for features)
78 #   then by extension number (for extensions)
79 def regSortFeatures(featureList):
80     featureList.sort(key = regSortExtensionNumberKey)
81     featureList.sort(key = regSortFeatureVersionKey)
82     featureList.sort(key = regSortCategoryKey)
83
84 # GeneratorOptions - base class for options used during header production
85 # These options are target language independent, and used by
86 # Registry.apiGen() and by base OutputGenerator objects.
87 #
88 # Members
89 #   filename - name of file to generate, or None to write to stdout.
90 #   apiname - string matching <api> 'apiname' attribute, e.g. 'gl'.
91 #   profile - string specifying API profile , e.g. 'core', or None.
92 #   versions - regex matching API versions to process interfaces for.
93 #     Normally '.*' or '[0-9]\.[0-9]' to match all defined versions.
94 #   emitversions - regex matching API versions to actually emit
95 #    interfaces for (though all requested versions are considered
96 #    when deciding which interfaces to generate). For GL 4.3 glext.h,
97 #     this might be '1\.[2-5]|[2-4]\.[0-9]'.
98 #   defaultExtensions - If not None, a string which must in its
99 #     entirety match the pattern in the "supported" attribute of
100 #     the <extension>. Defaults to None. Usually the same as apiname.
101 #   addExtensions - regex matching names of additional extensions
102 #     to include. Defaults to None.
103 #   removeExtensions - regex matching names of extensions to
104 #     remove (after defaultExtensions and addExtensions). Defaults
105 #     to None.
106 #   sortProcedure - takes a list of FeatureInfo objects and sorts
107 #     them in place to a preferred order in the generated output.
108 #     Default is core API versions, ARB/KHR/OES extensions, all
109 #     other extensions, alphabetically within each group.
110 # The regex patterns can be None or empty, in which case they match
111 #   nothing.
112 class GeneratorOptions:
113     """Represents options during header production from an API registry"""
114     def __init__(self,
115                  filename = None,
116                  apiname = None,
117                  profile = None,
118                  versions = '.*',
119                  emitversions = '.*',
120                  defaultExtensions = None,
121                  addExtensions = None,
122                  removeExtensions = None,
123                  sortProcedure = regSortFeatures):
124         self.filename          = filename
125         self.apiname           = apiname
126         self.profile           = profile
127         self.versions          = self.emptyRegex(versions)
128         self.emitversions      = self.emptyRegex(emitversions)
129         self.defaultExtensions = defaultExtensions
130         self.addExtensions     = self.emptyRegex(addExtensions)
131         self.removeExtensions  = self.emptyRegex(removeExtensions)
132         self.sortProcedure     = sortProcedure
133     #
134     # Substitute a regular expression which matches no version
135     # or extension names for None or the empty string.
136     def emptyRegex(self,pat):
137         if (pat == None or pat == ''):
138             return '_nomatch_^'
139         else:
140             return pat
141
142 # CGeneratorOptions - subclass of GeneratorOptions.
143 #
144 # Adds options used by COutputGenerator objects during C language header
145 # generation.
146 #
147 # Additional members
148 #   prefixText - list of strings to prefix generated header with
149 #     (usually a copyright statement + calling convention macros).
150 #   protectFile - True if multiple inclusion protection should be
151 #     generated (based on the filename) around the entire header.
152 #   protectFeature - True if #ifndef..#endif protection should be
153 #     generated around a feature interface in the header file.
154 #   genFuncPointers - True if function pointer typedefs should be
155 #     generated
156 #   protectProto - If conditional protection should be generated
157 #     around prototype declarations, set to either '#ifdef'
158 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
159 #     to require opt-out (#ifndef protectProtoStr). Otherwise
160 #     set to None.
161 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
162 #     declarations, if protectProto is set
163 #   apicall - string to use for the function declaration prefix,
164 #     such as APICALL on Windows.
165 #   apientry - string to use for the calling convention macro,
166 #     in typedefs, such as APIENTRY.
167 #   apientryp - string to use for the calling convention macro
168 #     in function pointer typedefs, such as APIENTRYP.
169 #   indentFuncProto - True if prototype declarations should put each
170 #     parameter on a separate line
171 #   indentFuncPointer - True if typedefed function pointers should put each
172 #     parameter on a separate line
173 #   alignFuncParam - if nonzero and parameters are being put on a
174 #     separate line, align parameter names at the specified column
175 class CGeneratorOptions(GeneratorOptions):
176     """Represents options during C interface generation for headers"""
177     def __init__(self,
178                  filename = None,
179                  apiname = None,
180                  profile = None,
181                  versions = '.*',
182                  emitversions = '.*',
183                  defaultExtensions = None,
184                  addExtensions = None,
185                  removeExtensions = None,
186                  sortProcedure = regSortFeatures,
187                  prefixText = "",
188                  genFuncPointers = True,
189                  protectFile = True,
190                  protectFeature = True,
191                  protectProto = None,
192                  protectProtoStr = None,
193                  apicall = '',
194                  apientry = '',
195                  apientryp = '',
196                  indentFuncProto = True,
197                  indentFuncPointer = False,
198                  alignFuncParam = 0):
199         GeneratorOptions.__init__(self, filename, apiname, profile,
200                                   versions, emitversions, defaultExtensions,
201                                   addExtensions, removeExtensions, sortProcedure)
202         self.prefixText      = prefixText
203         self.genFuncPointers = genFuncPointers
204         self.protectFile     = protectFile
205         self.protectFeature  = protectFeature
206         self.protectProto    = protectProto
207         self.protectProtoStr = protectProtoStr
208         self.apicall         = apicall
209         self.apientry        = apientry
210         self.apientryp       = apientryp
211         self.indentFuncProto = indentFuncProto
212         self.indentFuncPointer = indentFuncPointer
213         self.alignFuncParam  = alignFuncParam
214
215 # DocGeneratorOptions - subclass of GeneratorOptions.
216 #
217 # Shares many members with CGeneratorOptions, since
218 # both are writing C-style declarations:
219 #
220 #   prefixText - list of strings to prefix generated header with
221 #     (usually a copyright statement + calling convention macros).
222 #   apicall - string to use for the function declaration prefix,
223 #     such as APICALL on Windows.
224 #   apientry - string to use for the calling convention macro,
225 #     in typedefs, such as APIENTRY.
226 #   apientryp - string to use for the calling convention macro
227 #     in function pointer typedefs, such as APIENTRYP.
228 #   genDirectory - directory into which to generate include files
229 #   indentFuncProto - True if prototype declarations should put each
230 #     parameter on a separate line
231 #   indentFuncPointer - True if typedefed function pointers should put each
232 #     parameter on a separate line
233 #   alignFuncParam - if nonzero and parameters are being put on a
234 #     separate line, align parameter names at the specified column
235 #
236 # Additional members:
237 #
238 class DocGeneratorOptions(GeneratorOptions):
239     """Represents options during C interface generation for Asciidoc"""
240     def __init__(self,
241                  filename = None,
242                  apiname = None,
243                  profile = None,
244                  versions = '.*',
245                  emitversions = '.*',
246                  defaultExtensions = None,
247                  addExtensions = None,
248                  removeExtensions = None,
249                  sortProcedure = regSortFeatures,
250                  prefixText = "",
251                  apicall = '',
252                  apientry = '',
253                  apientryp = '',
254                  genDirectory = 'gen',
255                  indentFuncProto = True,
256                  indentFuncPointer = False,
257                  alignFuncParam = 0,
258                  expandEnumerants = True):
259         GeneratorOptions.__init__(self, filename, apiname, profile,
260                                   versions, emitversions, defaultExtensions,
261                                   addExtensions, removeExtensions, sortProcedure)
262         self.prefixText      = prefixText
263         self.apicall         = apicall
264         self.apientry        = apientry
265         self.apientryp       = apientryp
266         self.genDirectory    = genDirectory
267         self.indentFuncProto = indentFuncProto
268         self.indentFuncPointer = indentFuncPointer
269         self.alignFuncParam  = alignFuncParam
270         self.expandEnumerants = expandEnumerants
271
272 # ThreadGeneratorOptions - subclass of GeneratorOptions.
273 #
274 # Adds options used by COutputGenerator objects during C language header
275 # generation.
276 #
277 # Additional members
278 #   prefixText - list of strings to prefix generated header with
279 #     (usually a copyright statement + calling convention macros).
280 #   protectFile - True if multiple inclusion protection should be
281 #     generated (based on the filename) around the entire header.
282 #   protectFeature - True if #ifndef..#endif protection should be
283 #     generated around a feature interface in the header file.
284 #   genFuncPointers - True if function pointer typedefs should be
285 #     generated
286 #   protectProto - True if #ifdef..#endif protection should be
287 #     generated around prototype declarations
288 #   protectProtoStr - #ifdef symbol to use around prototype
289 #     declarations, if protected
290 #   apicall - string to use for the function declaration prefix,
291 #     such as APICALL on Windows.
292 #   apientry - string to use for the calling convention macro,
293 #     in typedefs, such as APIENTRY.
294 #   apientryp - string to use for the calling convention macro
295 #     in function pointer typedefs, such as APIENTRYP.
296 #   indentFuncProto - True if prototype declarations should put each
297 #     parameter on a separate line
298 #   indentFuncPointer - True if typedefed function pointers should put each
299 #     parameter on a separate line
300 #   alignFuncParam - if nonzero and parameters are being put on a
301 #     separate line, align parameter names at the specified column
302 class ThreadGeneratorOptions(GeneratorOptions):
303     """Represents options during C interface generation for headers"""
304     def __init__(self,
305                  filename = None,
306                  apiname = None,
307                  profile = None,
308                  versions = '.*',
309                  emitversions = '.*',
310                  defaultExtensions = None,
311                  addExtensions = None,
312                  removeExtensions = None,
313                  sortProcedure = regSortFeatures,
314                  prefixText = "",
315                  genFuncPointers = True,
316                  protectFile = True,
317                  protectFeature = True,
318                  protectProto = True,
319                  protectProtoStr = True,
320                  apicall = '',
321                  apientry = '',
322                  apientryp = '',
323                  indentFuncProto = True,
324                  indentFuncPointer = False,
325                  alignFuncParam = 0,
326                  genDirectory = None):
327         GeneratorOptions.__init__(self, filename, apiname, profile,
328                                   versions, emitversions, defaultExtensions,
329                                   addExtensions, removeExtensions, sortProcedure)
330         self.prefixText      = prefixText
331         self.genFuncPointers = genFuncPointers
332         self.protectFile     = protectFile
333         self.protectFeature  = protectFeature
334         self.protectProto    = protectProto
335         self.protectProtoStr = protectProtoStr
336         self.apicall         = apicall
337         self.apientry        = apientry
338         self.apientryp       = apientryp
339         self.indentFuncProto = indentFuncProto
340         self.indentFuncPointer = indentFuncPointer
341         self.alignFuncParam  = alignFuncParam
342         self.genDirectory    = genDirectory
343
344
345 # ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
346 #
347 # Adds options used by ParamCheckerOutputGenerator objects during parameter validation
348 # generation.
349 #
350 # Additional members
351 #   prefixText - list of strings to prefix generated header with
352 #     (usually a copyright statement + calling convention macros).
353 #   protectFile - True if multiple inclusion protection should be
354 #     generated (based on the filename) around the entire header.
355 #   protectFeature - True if #ifndef..#endif protection should be
356 #     generated around a feature interface in the header file.
357 #   genFuncPointers - True if function pointer typedefs should be
358 #     generated
359 #   protectProto - If conditional protection should be generated
360 #     around prototype declarations, set to either '#ifdef'
361 #     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
362 #     to require opt-out (#ifndef protectProtoStr). Otherwise
363 #     set to None.
364 #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
365 #     declarations, if protectProto is set
366 #   apicall - string to use for the function declaration prefix,
367 #     such as APICALL on Windows.
368 #   apientry - string to use for the calling convention macro,
369 #     in typedefs, such as APIENTRY.
370 #   apientryp - string to use for the calling convention macro
371 #     in function pointer typedefs, such as APIENTRYP.
372 #   indentFuncProto - True if prototype declarations should put each
373 #     parameter on a separate line
374 #   indentFuncPointer - True if typedefed function pointers should put each
375 #     parameter on a separate line
376 #   alignFuncParam - if nonzero and parameters are being put on a
377 #     separate line, align parameter names at the specified column
378 class ParamCheckerGeneratorOptions(GeneratorOptions):
379     """Represents options during C interface generation for headers"""
380     def __init__(self,
381                  filename = None,
382                  apiname = None,
383                  profile = None,
384                  versions = '.*',
385                  emitversions = '.*',
386                  defaultExtensions = None,
387                  addExtensions = None,
388                  removeExtensions = None,
389                  sortProcedure = regSortFeatures,
390                  prefixText = "",
391                  genFuncPointers = True,
392                  protectFile = True,
393                  protectFeature = True,
394                  protectProto = None,
395                  protectProtoStr = None,
396                  apicall = '',
397                  apientry = '',
398                  apientryp = '',
399                  indentFuncProto = True,
400                  indentFuncPointer = False,
401                  alignFuncParam = 0,
402                  genDirectory = None):
403         GeneratorOptions.__init__(self, filename, apiname, profile,
404                                   versions, emitversions, defaultExtensions,
405                                   addExtensions, removeExtensions, sortProcedure)
406         self.prefixText      = prefixText
407         self.genFuncPointers = genFuncPointers
408         self.protectFile     = protectFile
409         self.protectFeature  = protectFeature
410         self.protectProto    = protectProto
411         self.protectProtoStr = protectProtoStr
412         self.apicall         = apicall
413         self.apientry        = apientry
414         self.apientryp       = apientryp
415         self.indentFuncProto = indentFuncProto
416         self.indentFuncPointer = indentFuncPointer
417         self.alignFuncParam  = alignFuncParam
418         self.genDirectory    = genDirectory
419
420
421 # OutputGenerator - base class for generating API interfaces.
422 # Manages basic logic, logging, and output file control
423 # Derived classes actually generate formatted output.
424 #
425 # ---- methods ----
426 # OutputGenerator(errFile, warnFile, diagFile)
427 #   errFile, warnFile, diagFile - file handles to write errors,
428 #     warnings, diagnostics to. May be None to not write.
429 # logMsg(level, *args) - log messages of different categories
430 #   level - 'error', 'warn', or 'diag'. 'error' will also
431 #     raise a UserWarning exception
432 #   *args - print()-style arguments
433 # setExtMap(map) - specify a dictionary map from extension names to
434 #   numbers, used in creating values for extension enumerants.
435 # beginFile(genOpts) - start a new interface file
436 #   genOpts - GeneratorOptions controlling what's generated and how
437 # endFile() - finish an interface file, closing it when done
438 # beginFeature(interface, emit) - write interface for a feature
439 # and tag generated features as having been done.
440 #   interface - element for the <version> / <extension> to generate
441 #   emit - actually write to the header only when True
442 # endFeature() - finish an interface.
443 # genType(typeinfo,name) - generate interface for a type
444 #   typeinfo - TypeInfo for a type
445 # genStruct(typeinfo,name) - generate interface for a C "struct" type.
446 #   typeinfo - TypeInfo for a type interpreted as a struct
447 # genGroup(groupinfo,name) - generate interface for a group of enums (C "enum")
448 #   groupinfo - GroupInfo for a group
449 # genEnum(enuminfo, name) - generate interface for an enum (constant)
450 #   enuminfo - EnumInfo for an enum
451 #   name - enum name
452 # genCmd(cmdinfo) - generate interface for a command
453 #   cmdinfo - CmdInfo for a command
454 # makeCDecls(cmd) - return C prototype and function pointer typedef for a
455 #     <command> Element, as a list of two strings
456 #   cmd - Element for the <command>
457 # newline() - print a newline to the output file (utility function)
458 #
459 class OutputGenerator:
460     """Generate specified API interfaces in a specific style, such as a C header"""
461     def __init__(self,
462                  errFile = sys.stderr,
463                  warnFile = sys.stderr,
464                  diagFile = sys.stdout):
465         self.outFile = None
466         self.errFile = errFile
467         self.warnFile = warnFile
468         self.diagFile = diagFile
469         # Internal state
470         self.featureName = None
471         self.genOpts = None
472         self.registry = None
473         # Used for extension enum value generation
474         self.extBase      = 1000000000
475         self.extBlockSize = 1000
476     #
477     # logMsg - write a message of different categories to different
478     #   destinations.
479     # level -
480     #   'diag' (diagnostic, voluminous)
481     #   'warn' (warning)
482     #   'error' (fatal error - raises exception after logging)
483     # *args - print()-style arguments to direct to corresponding log
484     def logMsg(self, level, *args):
485         """Log a message at the given level. Can be ignored or log to a file"""
486         if (level == 'error'):
487             strfile = io.StringIO()
488             write('ERROR:', *args, file=strfile)
489             if (self.errFile != None):
490                 write(strfile.getvalue(), file=self.errFile)
491             raise UserWarning(strfile.getvalue())
492         elif (level == 'warn'):
493             if (self.warnFile != None):
494                 write('WARNING:', *args, file=self.warnFile)
495         elif (level == 'diag'):
496             if (self.diagFile != None):
497                 write('DIAG:', *args, file=self.diagFile)
498         else:
499             raise UserWarning(
500                 '*** FATAL ERROR in Generator.logMsg: unknown level:' + level)
501     #
502     # enumToValue - parses and converts an <enum> tag into a value.
503     # Returns a list
504     #   first element - integer representation of the value, or None
505     #       if needsNum is False. The value must be a legal number
506     #       if needsNum is True.
507     #   second element - string representation of the value
508     # There are several possible representations of values.
509     #   A 'value' attribute simply contains the value.
510     #   A 'bitpos' attribute defines a value by specifying the bit
511     #       position which is set in that value.
512     #   A 'offset','extbase','extends' triplet specifies a value
513     #       as an offset to a base value defined by the specified
514     #       'extbase' extension name, which is then cast to the
515     #       typename specified by 'extends'. This requires probing
516     #       the registry database, and imbeds knowledge of the
517     #       Vulkan extension enum scheme in this function.
518     def enumToValue(self, elem, needsNum):
519         name = elem.get('name')
520         numVal = None
521         if ('value' in elem.keys()):
522             value = elem.get('value')
523             # print('About to translate value =', value, 'type =', type(value))
524             if (needsNum):
525                 numVal = int(value, 0)
526             # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or
527             # 'ull'), append it to the string value.
528             # t = enuminfo.elem.get('type')
529             # if (t != None and t != '' and t != 'i' and t != 's'):
530             #     value += enuminfo.type
531             self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']')
532             return [numVal, value]
533         if ('bitpos' in elem.keys()):
534             value = elem.get('bitpos')
535             numVal = int(value, 0)
536             numVal = 1 << numVal
537             value = '0x%08x' % numVal
538             self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']')
539             return [numVal, value]
540         if ('offset' in elem.keys()):
541             # Obtain values in the mapping from the attributes
542             enumNegative = False
543             offset = int(elem.get('offset'),0)
544             extnumber = int(elem.get('extnumber'),0)
545             extends = elem.get('extends')
546             if ('dir' in elem.keys()):
547                 enumNegative = True
548             self.logMsg('diag', 'Enum', name, 'offset =', offset,
549                 'extnumber =', extnumber, 'extends =', extends,
550                 'enumNegative =', enumNegative)
551             # Now determine the actual enumerant value, as defined
552             # in the "Layers and Extensions" appendix of the spec.
553             numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
554             if (enumNegative):
555                 numVal = -numVal
556             value = '%d' % numVal
557             # More logic needed!
558             self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']')
559             return [numVal, value]
560         return [None, None]
561     #
562     def beginFile(self, genOpts):
563         self.genOpts = genOpts
564         #
565         # Open specified output file. Not done in constructor since a
566         # Generator can be used without writing to a file.
567         if (self.genOpts.filename != None):
568             if (self.genOpts.genDirectory != None):
569                 self.outFile = open(os.path.join(self.genOpts.genDirectory, self.genOpts.filename), 'w')
570             else:
571                 self.outFile = open(self.genOpts.filename, 'w')
572         else:
573             self.outFile = sys.stdout
574     def endFile(self):
575         self.errFile and self.errFile.flush()
576         self.warnFile and self.warnFile.flush()
577         self.diagFile and self.diagFile.flush()
578         self.outFile.flush()
579         if (self.outFile != sys.stdout and self.outFile != sys.stderr):
580             self.outFile.close()
581         self.genOpts = None
582     #
583     def beginFeature(self, interface, emit):
584         self.emit = emit
585         self.featureName = interface.get('name')
586         # If there's an additional 'protect' attribute in the feature, save it
587         self.featureExtraProtect = interface.get('protect')
588     def endFeature(self):
589         # Derived classes responsible for emitting feature
590         self.featureName = None
591         self.featureExtraProtect = None
592     # Utility method to validate we're generating something only inside a
593     # <feature> tag
594     def validateFeature(self, featureType, featureName):
595         if (self.featureName == None):
596             raise UserWarning('Attempt to generate', featureType, name,
597                     'when not in feature')
598     #
599     # Type generation
600     def genType(self, typeinfo, name):
601         self.validateFeature('type', name)
602     #
603     # Struct (e.g. C "struct" type) generation
604     def genStruct(self, typeinfo, name):
605         self.validateFeature('struct', name)
606     #
607     # Group (e.g. C "enum" type) generation
608     def genGroup(self, groupinfo, name):
609         self.validateFeature('group', name)
610     #
611     # Enumerant (really, constant) generation
612     def genEnum(self, enuminfo, name):
613         self.validateFeature('enum', name)
614     #
615     # Command generation
616     def genCmd(self, cmd, name):
617         self.validateFeature('command', name)
618     #
619     # Utility functions - turn a <proto> <name> into C-language prototype
620     # and typedef declarations for that name.
621     # name - contents of <name> tag
622     # tail - whatever text follows that tag in the Element
623     def makeProtoName(self, name, tail):
624         return self.genOpts.apientry + name + tail
625     def makeTypedefName(self, name, tail):
626        return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
627     #
628     # makeCParamDecl - return a string which is an indented, formatted
629     # declaration for a <param> or <member> block (e.g. function parameter
630     # or structure/union member).
631     # param - Element (<param> or <member>) to format
632     # aligncol - if non-zero, attempt to align the nested <name> element
633     #   at this column
634     def makeCParamDecl(self, param, aligncol):
635         paramdecl = '    ' + noneStr(param.text)
636         for elem in param:
637             text = noneStr(elem.text)
638             tail = noneStr(elem.tail)
639             if (elem.tag == 'name' and aligncol > 0):
640                 self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam)
641                 # Align at specified column, if possible
642                 paramdecl = paramdecl.rstrip()
643                 oldLen = len(paramdecl)
644                 paramdecl = paramdecl.ljust(aligncol)
645                 newLen = len(paramdecl)
646                 self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl)
647             paramdecl += text + tail
648         return paramdecl
649     #
650     # getCParamTypeLength - return the length of the type field is an indented, formatted
651     # declaration for a <param> or <member> block (e.g. function parameter
652     # or structure/union member).
653     # param - Element (<param> or <member>) to identify
654     def getCParamTypeLength(self, param):
655         paramdecl = '    ' + noneStr(param.text)
656         for elem in param:
657             text = noneStr(elem.text)
658             tail = noneStr(elem.tail)
659             if (elem.tag == 'name'):
660                 # Align at specified column, if possible
661                 newLen = len(paramdecl.rstrip())
662                 self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen)
663             paramdecl += text + tail
664         return newLen
665     #
666     # makeCDecls - return C prototype and function pointer typedef for a
667     #   command, as a two-element list of strings.
668     # cmd - Element containing a <command> tag
669     def makeCDecls(self, cmd):
670         """Generate C function pointer typedef for <command> Element"""
671         proto = cmd.find('proto')
672         params = cmd.findall('param')
673         # Begin accumulating prototype and typedef strings
674         pdecl = self.genOpts.apicall
675         tdecl = 'typedef '
676         #
677         # Insert the function return type/name.
678         # For prototypes, add APIENTRY macro before the name
679         # For typedefs, add (APIENTRY *<name>) around the name and
680         #   use the PFN_cmdnameproc naming convention.
681         # Done by walking the tree for <proto> element by element.
682         # etree has elem.text followed by (elem[i], elem[i].tail)
683         #   for each child element and any following text
684         # Leading text
685         pdecl += noneStr(proto.text)
686         tdecl += noneStr(proto.text)
687         # For each child element, if it's a <name> wrap in appropriate
688         # declaration. Otherwise append its contents and tail contents.
689         for elem in proto:
690             text = noneStr(elem.text)
691             tail = noneStr(elem.tail)
692             if (elem.tag == 'name'):
693                 pdecl += self.makeProtoName(text, tail)
694                 tdecl += self.makeTypedefName(text, tail)
695             else:
696                 pdecl += text + tail
697                 tdecl += text + tail
698         # Now add the parameter declaration list, which is identical
699         # for prototypes and typedefs. Concatenate all the text from
700         # a <param> node without the tags. No tree walking required
701         # since all tags are ignored.
702         # Uses: self.indentFuncProto
703         # self.indentFuncPointer
704         # self.alignFuncParam
705         # Might be able to doubly-nest the joins, e.g.
706         #   ','.join(('_'.join([l[i] for i in range(0,len(l))])
707         n = len(params)
708         # Indented parameters
709         if n > 0:
710             indentdecl = '(\n'
711             for i in range(0,n):
712                 paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam)
713                 if (i < n - 1):
714                     paramdecl += ',\n'
715                 else:
716                     paramdecl += ');'
717                 indentdecl += paramdecl
718         else:
719             indentdecl = '(void);'
720         # Non-indented parameters
721         paramdecl = '('
722         if n > 0:
723             for i in range(0,n):
724                 paramdecl += ''.join([t for t in params[i].itertext()])
725                 if (i < n - 1):
726                     paramdecl += ', '
727         else:
728             paramdecl += 'void'
729         paramdecl += ");";
730         return [ pdecl + indentdecl, tdecl + paramdecl ]
731     #
732     def newline(self):
733         write('', file=self.outFile)
734
735     def setRegistry(self, registry):
736         self.registry = registry
737         #
738
739 # COutputGenerator - subclass of OutputGenerator.
740 # Generates C-language API interfaces.
741 #
742 # ---- methods ----
743 # COutputGenerator(errFile, warnFile, diagFile) - args as for
744 #   OutputGenerator. Defines additional internal state.
745 # ---- methods overriding base class ----
746 # beginFile(genOpts)
747 # endFile()
748 # beginFeature(interface, emit)
749 # endFeature()
750 # genType(typeinfo,name)
751 # genStruct(typeinfo,name)
752 # genGroup(groupinfo,name)
753 # genEnum(enuminfo, name)
754 # genCmd(cmdinfo)
755 class COutputGenerator(OutputGenerator):
756     """Generate specified API interfaces in a specific style, such as a C header"""
757     # This is an ordered list of sections in the header file.
758     TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
759                      'group', 'bitmask', 'funcpointer', 'struct']
760     ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
761     def __init__(self,
762                  errFile = sys.stderr,
763                  warnFile = sys.stderr,
764                  diagFile = sys.stdout):
765         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
766         # Internal state - accumulators for different inner block text
767         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
768     #
769     def beginFile(self, genOpts):
770         OutputGenerator.beginFile(self, genOpts)
771         # C-specific
772         #
773         # Multiple inclusion protection & C++ wrappers.
774         if (genOpts.protectFile and self.genOpts.filename):
775             headerSym = re.sub('\.h', '_h_',
776                                os.path.basename(self.genOpts.filename)).upper()
777             write('#ifndef', headerSym, file=self.outFile)
778             write('#define', headerSym, '1', file=self.outFile)
779             self.newline()
780         write('#ifdef __cplusplus', file=self.outFile)
781         write('extern "C" {', file=self.outFile)
782         write('#endif', file=self.outFile)
783         self.newline()
784         #
785         # User-supplied prefix text, if any (list of strings)
786         if (genOpts.prefixText):
787             for s in genOpts.prefixText:
788                 write(s, file=self.outFile)
789         #
790         # Some boilerplate describing what was generated - this
791         # will probably be removed later since the extensions
792         # pattern may be very long.
793         # write('/* Generated C header for:', file=self.outFile)
794         # write(' * API:', genOpts.apiname, file=self.outFile)
795         # if (genOpts.profile):
796         #     write(' * Profile:', genOpts.profile, file=self.outFile)
797         # write(' * Versions considered:', genOpts.versions, file=self.outFile)
798         # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile)
799         # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile)
800         # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile)
801         # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile)
802         # write(' */', file=self.outFile)
803     def endFile(self):
804         # C-specific
805         # Finish C++ wrapper and multiple inclusion protection
806         self.newline()
807         write('#ifdef __cplusplus', file=self.outFile)
808         write('}', file=self.outFile)
809         write('#endif', file=self.outFile)
810         if (self.genOpts.protectFile and self.genOpts.filename):
811             self.newline()
812             write('#endif', file=self.outFile)
813         # Finish processing in superclass
814         OutputGenerator.endFile(self)
815     def beginFeature(self, interface, emit):
816         # Start processing in superclass
817         OutputGenerator.beginFeature(self, interface, emit)
818         # C-specific
819         # Accumulate includes, defines, types, enums, function pointer typedefs,
820         # end function prototypes separately for this feature. They're only
821         # printed in endFeature().
822         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
823     def endFeature(self):
824         # C-specific
825         # Actually write the interface to the output file.
826         if (self.emit):
827             self.newline()
828             if (self.genOpts.protectFeature):
829                 write('#ifndef', self.featureName, file=self.outFile)
830             # If type declarations are needed by other features based on
831             # this one, it may be necessary to suppress the ExtraProtect,
832             # or move it below the 'for section...' loop.
833             if (self.featureExtraProtect != None):
834                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
835             write('#define', self.featureName, '1', file=self.outFile)
836             for section in self.TYPE_SECTIONS:
837                 contents = self.sections[section]
838                 if contents:
839                     write('\n'.join(contents), file=self.outFile)
840                     self.newline()
841             if (self.genOpts.genFuncPointers and self.sections['commandPointer']):
842                 write('\n'.join(self.sections['commandPointer']), file=self.outFile)
843                 self.newline()
844             if (self.sections['command']):
845                 if (self.genOpts.protectProto):
846                     write(self.genOpts.protectProto,
847                           self.genOpts.protectProtoStr, file=self.outFile)
848                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
849                 if (self.genOpts.protectProto):
850                     write('#endif', file=self.outFile)
851                 else:
852                     self.newline()
853             if (self.featureExtraProtect != None):
854                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
855             if (self.genOpts.protectFeature):
856                 write('#endif /*', self.featureName, '*/', file=self.outFile)
857         # Finish processing in superclass
858         OutputGenerator.endFeature(self)
859     #
860     # Append a definition to the specified section
861     def appendSection(self, section, text):
862         # self.sections[section].append('SECTION: ' + section + '\n')
863         self.sections[section].append(text)
864     #
865     # Type generation
866     def genType(self, typeinfo, name):
867         OutputGenerator.genType(self, typeinfo, name)
868         typeElem = typeinfo.elem
869         # If the type is a struct type, traverse the imbedded <member> tags
870         # generating a structure. Otherwise, emit the tag text.
871         category = typeElem.get('category')
872         if (category == 'struct' or category == 'union'):
873             self.genStruct(typeinfo, name)
874         else:
875             # Replace <apientry /> tags with an APIENTRY-style string
876             # (from self.genOpts). Copy other text through unchanged.
877             # If the resulting text is an empty string, don't emit it.
878             s = noneStr(typeElem.text)
879             for elem in typeElem:
880                 if (elem.tag == 'apientry'):
881                     s += self.genOpts.apientry + noneStr(elem.tail)
882                 else:
883                     s += noneStr(elem.text) + noneStr(elem.tail)
884             if s:
885                 # Add extra newline after multi-line entries.
886                 if '\n' in s:
887                     s += '\n'
888                 self.appendSection(category, s)
889     #
890     # Struct (e.g. C "struct" type) generation.
891     # This is a special case of the <type> tag where the contents are
892     # interpreted as a set of <member> tags instead of freeform C
893     # C type declarations. The <member> tags are just like <param>
894     # tags - they are a declaration of a struct or union member.
895     # Only simple member declarations are supported (no nested
896     # structs etc.)
897     def genStruct(self, typeinfo, typeName):
898         OutputGenerator.genStruct(self, typeinfo, typeName)
899         body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
900         # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
901         targetLen = 0;
902         for member in typeinfo.elem.findall('.//member'):
903             targetLen = max(targetLen, self.getCParamTypeLength(member))
904         for member in typeinfo.elem.findall('.//member'):
905             body += self.makeCParamDecl(member, targetLen + 4)
906             body += ';\n'
907         body += '} ' + typeName + ';\n'
908         self.appendSection('struct', body)
909     #
910     # Group (e.g. C "enum" type) generation.
911     # These are concatenated together with other types.
912     def genGroup(self, groupinfo, groupName):
913         OutputGenerator.genGroup(self, groupinfo, groupName)
914         groupElem = groupinfo.elem
915
916         expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
917
918         expandPrefix = expandName
919         expandSuffix = ''
920         expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
921         if expandSuffixMatch:
922             expandSuffix = '_' + expandSuffixMatch.group()
923             # Strip off the suffix from the prefix
924             expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
925
926         # Prefix
927         body = "\ntypedef enum " + groupName + " {\n"
928
929         isEnum = ('FLAG_BITS' not in expandPrefix)
930
931         # Loop over the nested 'enum' tags. Keep track of the minimum and
932         # maximum numeric values, if they can be determined; but only for
933         # core API enumerants, not extension enumerants. This is inferred
934         # by looking for 'extends' attributes.
935         minName = None
936         for elem in groupElem.findall('enum'):
937             # Convert the value to an integer and use that to track min/max.
938             # Values of form -(number) are accepted but nothing more complex.
939             # Should catch exceptions here for more complex constructs. Not yet.
940             (numVal,strVal) = self.enumToValue(elem, True)
941             name = elem.get('name')
942
943             # Extension enumerants are only included if they are requested
944             # in addExtensions or match defaultExtensions.
945             if (elem.get('extname') is None or
946               re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
947               self.genOpts.defaultExtensions == elem.get('supported')):
948                 body += "    " + name + " = " + strVal + ",\n"
949
950             if (isEnum  and elem.get('extends') is None):
951                 if (minName == None):
952                     minName = maxName = name
953                     minValue = maxValue = numVal
954                 elif (numVal < minValue):
955                     minName = name
956                     minValue = numVal
957                 elif (numVal > maxValue):
958                     maxName = name
959                     maxValue = numVal
960         # Generate min/max value tokens and a range-padding enum. Need some
961         # additional padding to generate correct names...
962         if isEnum:
963             body += "    " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
964             body += "    " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
965             body += "    " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
966
967         body += "    " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
968
969         # Postfix
970         body += "} " + groupName + ";"
971         if groupElem.get('type') == 'bitmask':
972             section = 'bitmask'
973         else:
974             section = 'group'
975         self.appendSection(section, body)
976     # Enumerant generation
977     # <enum> tags may specify their values in several ways, but are usually
978     # just integers.
979     def genEnum(self, enuminfo, name):
980         OutputGenerator.genEnum(self, enuminfo, name)
981         (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
982         body = '#define ' + name.ljust(33) + ' ' + strVal
983         self.appendSection('enum', body)
984     #
985     # Command generation
986     def genCmd(self, cmdinfo, name):
987         OutputGenerator.genCmd(self, cmdinfo, name)
988         #
989         decls = self.makeCDecls(cmdinfo.elem)
990         self.appendSection('command', decls[0] + '\n')
991         if (self.genOpts.genFuncPointers):
992             self.appendSection('commandPointer', decls[1])
993
994 # DocOutputGenerator - subclass of OutputGenerator.
995 # Generates AsciiDoc includes with C-language API interfaces, for reference
996 # pages and the Vulkan specification. Similar to COutputGenerator, but
997 # each interface is written into a different file as determined by the
998 # options, only actual C types are emitted, and none of the boilerplate
999 # preprocessor code is emitted.
1000 #
1001 # ---- methods ----
1002 # DocOutputGenerator(errFile, warnFile, diagFile) - args as for
1003 #   OutputGenerator. Defines additional internal state.
1004 # ---- methods overriding base class ----
1005 # beginFile(genOpts)
1006 # endFile()
1007 # beginFeature(interface, emit)
1008 # endFeature()
1009 # genType(typeinfo,name)
1010 # genStruct(typeinfo,name)
1011 # genGroup(groupinfo,name)
1012 # genEnum(enuminfo, name)
1013 # genCmd(cmdinfo)
1014 class DocOutputGenerator(OutputGenerator):
1015     """Generate specified API interfaces in a specific style, such as a C header"""
1016     def __init__(self,
1017                  errFile = sys.stderr,
1018                  warnFile = sys.stderr,
1019                  diagFile = sys.stdout):
1020         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1021     #
1022     def beginFile(self, genOpts):
1023         OutputGenerator.beginFile(self, genOpts)
1024     def endFile(self):
1025         OutputGenerator.endFile(self)
1026     def beginFeature(self, interface, emit):
1027         # Start processing in superclass
1028         OutputGenerator.beginFeature(self, interface, emit)
1029     def endFeature(self):
1030         # Finish processing in superclass
1031         OutputGenerator.endFeature(self)
1032     #
1033     # Generate an include file
1034     #
1035     # directory - subdirectory to put file in
1036     # basename - base name of the file
1037     # contents - contents of the file (Asciidoc boilerplate aside)
1038     def writeInclude(self, directory, basename, contents):
1039         # Create file
1040         filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1041         self.logMsg('diag', '# Generating include file:', filename)
1042         fp = open(filename, 'w')
1043         # Asciidoc anchor
1044         write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
1045         write('ifndef::doctype-manpage[]', file=fp)
1046         write('[[{0},{0}]]'.format(basename), file=fp)
1047         write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp)
1048         write('endif::doctype-manpage[]', file=fp)
1049         write('ifdef::doctype-manpage[]', file=fp)
1050         write('["source","{basebackend@docbook:c++:cpp}"]', file=fp)
1051         write('endif::doctype-manpage[]', file=fp)
1052         write('------------------------------------------------------------------------------', file=fp)
1053         write(contents, file=fp)
1054         write('------------------------------------------------------------------------------', file=fp)
1055         fp.close()
1056     #
1057     # Type generation
1058     def genType(self, typeinfo, name):
1059         OutputGenerator.genType(self, typeinfo, name)
1060         typeElem = typeinfo.elem
1061         # If the type is a struct type, traverse the imbedded <member> tags
1062         # generating a structure. Otherwise, emit the tag text.
1063         category = typeElem.get('category')
1064         if (category == 'struct' or category == 'union'):
1065             self.genStruct(typeinfo, name)
1066         else:
1067             # Replace <apientry /> tags with an APIENTRY-style string
1068             # (from self.genOpts). Copy other text through unchanged.
1069             # If the resulting text is an empty string, don't emit it.
1070             s = noneStr(typeElem.text)
1071             for elem in typeElem:
1072                 if (elem.tag == 'apientry'):
1073                     s += self.genOpts.apientry + noneStr(elem.tail)
1074                 else:
1075                     s += noneStr(elem.text) + noneStr(elem.tail)
1076             if (len(s) > 0):
1077                 if (category == 'bitmask'):
1078                     self.writeInclude('flags', name, s + '\n')
1079                 elif (category == 'enum'):
1080                     self.writeInclude('enums', name, s + '\n')
1081                 elif (category == 'funcpointer'):
1082                     self.writeInclude('funcpointers', name, s+ '\n')
1083                 else:
1084                     self.logMsg('diag', '# NOT writing include file for type:',
1085                         name, 'category: ', category)
1086             else:
1087                 self.logMsg('diag', '# NOT writing empty include file for type', name)
1088     #
1089     # Struct (e.g. C "struct" type) generation.
1090     # This is a special case of the <type> tag where the contents are
1091     # interpreted as a set of <member> tags instead of freeform C
1092     # C type declarations. The <member> tags are just like <param>
1093     # tags - they are a declaration of a struct or union member.
1094     # Only simple member declarations are supported (no nested
1095     # structs etc.)
1096     def genStruct(self, typeinfo, typeName):
1097         OutputGenerator.genStruct(self, typeinfo, typeName)
1098         s = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
1099         # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
1100         targetLen = 0;
1101         for member in typeinfo.elem.findall('.//member'):
1102             targetLen = max(targetLen, self.getCParamTypeLength(member))
1103         for member in typeinfo.elem.findall('.//member'):
1104             s += self.makeCParamDecl(member, targetLen + 4)
1105             s += ';\n'
1106         s += '} ' + typeName + ';'
1107         self.writeInclude('structs', typeName, s)
1108     #
1109     # Group (e.g. C "enum" type) generation.
1110     # These are concatenated together with other types.
1111     def genGroup(self, groupinfo, groupName):
1112         OutputGenerator.genGroup(self, groupinfo, groupName)
1113         groupElem = groupinfo.elem
1114
1115         # See if we need min/max/num/padding at end
1116         expand = self.genOpts.expandEnumerants
1117
1118         if expand:
1119             expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
1120             isEnum = ('FLAG_BITS' not in expandName)
1121
1122             expandPrefix = expandName
1123             expandSuffix = ''
1124
1125             # Look for a suffix
1126             expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
1127             if expandSuffixMatch:
1128                 expandSuffix = '_' + expandSuffixMatch.group()
1129                 # Strip off the suffix from the prefix
1130                 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
1131
1132         # Prefix
1133         s = "typedef enum " + groupName + " {\n"
1134
1135         # Loop over the nested 'enum' tags. Keep track of the minimum and
1136         # maximum numeric values, if they can be determined.
1137         minName = None
1138         for elem in groupElem.findall('enum'):
1139             # Convert the value to an integer and use that to track min/max.
1140             # Values of form -(number) are accepted but nothing more complex.
1141             # Should catch exceptions here for more complex constructs. Not yet.
1142             (numVal,strVal) = self.enumToValue(elem, True)
1143             name = elem.get('name')
1144
1145             # Extension enumerants are only included if they are requested
1146             # in addExtensions or match defaultExtensions.
1147             if (elem.get('extname') is None or
1148               re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or
1149               self.genOpts.defaultExtensions == elem.get('supported')):
1150                 s += "    " + name + " = " + strVal + ",\n"
1151
1152             if (expand and isEnum and elem.get('extends') is None):
1153                 if (minName == None):
1154                     minName = maxName = name
1155                     minValue = maxValue = numVal
1156                 elif (numVal < minValue):
1157                     minName = name
1158                     minValue = numVal
1159                 elif (numVal > maxValue):
1160                     maxName = name
1161                     maxValue = numVal
1162         # Generate min/max value tokens and a range-padding enum. Need some
1163         # additional padding to generate correct names...
1164         if (expand):
1165             s += "\n"
1166             if isEnum:
1167                 s += "    " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
1168                 s += "    " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
1169                 s += "    " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
1170
1171             s += "    " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
1172         # Postfix
1173         s += "} " + groupName + ";"
1174         self.writeInclude('enums', groupName, s)
1175     # Enumerant generation
1176     # <enum> tags may specify their values in several ways, but are usually
1177     # just integers.
1178     def genEnum(self, enuminfo, name):
1179         OutputGenerator.genEnum(self, enuminfo, name)
1180         (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
1181         s = '#define ' + name.ljust(33) + ' ' + strVal
1182         self.logMsg('diag', '# NOT writing compile-time constant', name)
1183         # self.writeInclude('consts', name, s)
1184     #
1185     # Command generation
1186     def genCmd(self, cmdinfo, name):
1187         OutputGenerator.genCmd(self, cmdinfo, name)
1188         #
1189         decls = self.makeCDecls(cmdinfo.elem)
1190         self.writeInclude('protos', name, decls[0])
1191
1192 # PyOutputGenerator - subclass of OutputGenerator.
1193 # Generates Python data structures describing API names.
1194 # Similar to DocOutputGenerator, but writes a single
1195 # file.
1196 #
1197 # ---- methods ----
1198 # PyOutputGenerator(errFile, warnFile, diagFile) - args as for
1199 #   OutputGenerator. Defines additional internal state.
1200 # ---- methods overriding base class ----
1201 # beginFile(genOpts)
1202 # endFile()
1203 # genType(typeinfo,name)
1204 # genStruct(typeinfo,name)
1205 # genGroup(groupinfo,name)
1206 # genEnum(enuminfo, name)
1207 # genCmd(cmdinfo)
1208 class PyOutputGenerator(OutputGenerator):
1209     """Generate specified API interfaces in a specific style, such as a C header"""
1210     def __init__(self,
1211                  errFile = sys.stderr,
1212                  warnFile = sys.stderr,
1213                  diagFile = sys.stdout):
1214         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1215     #
1216     def beginFile(self, genOpts):
1217         OutputGenerator.beginFile(self, genOpts)
1218         for dict in [ 'flags', 'enums', 'structs', 'consts', 'enums',
1219           'consts', 'protos', 'funcpointers' ]:
1220             write(dict, '= {}', file=self.outFile)
1221     def endFile(self):
1222         OutputGenerator.endFile(self)
1223     #
1224     # Add a name from the interface
1225     #
1226     # dict - type of name (see beginFile above)
1227     # name - name to add
1228     # value - A serializable Python value for the name
1229     def addName(self, dict, name, value=None):
1230         write(dict + "['" + name + "'] = ", value, file=self.outFile)
1231     #
1232     # Type generation
1233     # For 'struct' or 'union' types, defer to genStruct() to
1234     #   add to the dictionary.
1235     # For 'bitmask' types, add the type name to the 'flags' dictionary,
1236     #   with the value being the corresponding 'enums' name defining
1237     #   the acceptable flag bits.
1238     # For 'enum' types, add the type name to the 'enums' dictionary,
1239     #   with the value being '@STOPHERE@' (because this case seems
1240     #   never to happen).
1241     # For 'funcpointer' types, add the type name to the 'funcpointers'
1242     #   dictionary.
1243     # For 'handle' and 'define' types, add the handle or #define name
1244     #   to the 'struct' dictionary, because that's how the spec sources
1245     #   tag these types even though they aren't structs.
1246     def genType(self, typeinfo, name):
1247         OutputGenerator.genType(self, typeinfo, name)
1248         typeElem = typeinfo.elem
1249         # If the type is a struct type, traverse the imbedded <member> tags
1250         # generating a structure. Otherwise, emit the tag text.
1251         category = typeElem.get('category')
1252         if (category == 'struct' or category == 'union'):
1253             self.genStruct(typeinfo, name)
1254         else:
1255             # Extract the type name
1256             # (from self.genOpts). Copy other text through unchanged.
1257             # If the resulting text is an empty string, don't emit it.
1258             count = len(noneStr(typeElem.text))
1259             for elem in typeElem:
1260                 count += len(noneStr(elem.text)) + len(noneStr(elem.tail))
1261             if (count > 0):
1262                 if (category == 'bitmask'):
1263                     requiredEnum = typeElem.get('requires')
1264                     self.addName('flags', name, enquote(requiredEnum))
1265                 elif (category == 'enum'):
1266                     # This case never seems to come up!
1267                     # @enums   C 'enum' name           Dictionary of enumerant names
1268                     self.addName('enums', name, enquote('@STOPHERE@'))
1269                 elif (category == 'funcpointer'):
1270                     self.addName('funcpointers', name, None)
1271                 elif (category == 'handle' or category == 'define'):
1272                     self.addName('structs', name, None)
1273                 else:
1274                     write('# Unprocessed type:', name, 'category:', category, file=self.outFile)
1275             else:
1276                 write('# Unprocessed type:', name, file=self.outFile)
1277     #
1278     # Struct (e.g. C "struct" type) generation.
1279     #
1280     # Add the struct name to the 'structs' dictionary, with the
1281     # value being an ordered list of the struct member names.
1282     def genStruct(self, typeinfo, typeName):
1283         OutputGenerator.genStruct(self, typeinfo, typeName)
1284
1285         members = [member.text for member in typeinfo.elem.findall('.//member/name')]
1286         self.addName('structs', typeName, members)
1287     #
1288     # Group (e.g. C "enum" type) generation.
1289     # These are concatenated together with other types.
1290     #
1291     # Add the enum type name to the 'enums' dictionary, with
1292     #   the value being an ordered list of the enumerant names.
1293     # Add each enumerant name to the 'consts' dictionary, with
1294     #   the value being the enum type the enumerant is part of.
1295     def genGroup(self, groupinfo, groupName):
1296         OutputGenerator.genGroup(self, groupinfo, groupName)
1297         groupElem = groupinfo.elem
1298
1299         # @enums   C 'enum' name           Dictionary of enumerant names
1300         # @consts  C enumerant/const name  Name of corresponding 'enums' key
1301
1302         # Loop over the nested 'enum' tags. Keep track of the minimum and
1303         # maximum numeric values, if they can be determined.
1304         enumerants = [elem.get('name') for elem in groupElem.findall('enum')]
1305         for name in enumerants:
1306             self.addName('consts', name, enquote(groupName))
1307         self.addName('enums', groupName, enumerants)
1308     # Enumerant generation (compile-time constants)
1309     #
1310     # Add the constant name to the 'consts' dictionary, with the
1311     #   value being None to indicate that the constant isn't
1312     #   an enumeration value.
1313     def genEnum(self, enuminfo, name):
1314         OutputGenerator.genEnum(self, enuminfo, name)
1315
1316         # @consts  C enumerant/const name  Name of corresponding 'enums' key
1317
1318         self.addName('consts', name, None)
1319     #
1320     # Command generation
1321     #
1322     # Add the command name to the 'protos' dictionary, with the
1323     #   value being an ordered list of the parameter names.
1324     def genCmd(self, cmdinfo, name):
1325         OutputGenerator.genCmd(self, cmdinfo, name)
1326
1327         params = [param.text for param in cmdinfo.elem.findall('param/name')]
1328         self.addName('protos', name, params)
1329
1330 # ValidityOutputGenerator - subclass of OutputGenerator.
1331 # Generates AsciiDoc includes of valid usage information, for reference
1332 # pages and the Vulkan specification. Similar to DocOutputGenerator.
1333 #
1334 # ---- methods ----
1335 # ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
1336 #   OutputGenerator. Defines additional internal state.
1337 # ---- methods overriding base class ----
1338 # beginFile(genOpts)
1339 # endFile()
1340 # beginFeature(interface, emit)
1341 # endFeature()
1342 # genCmd(cmdinfo)
1343 class ValidityOutputGenerator(OutputGenerator):
1344     """Generate specified API interfaces in a specific style, such as a C header"""
1345     def __init__(self,
1346                  errFile = sys.stderr,
1347                  warnFile = sys.stderr,
1348                  diagFile = sys.stdout):
1349         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
1350
1351     def beginFile(self, genOpts):
1352         OutputGenerator.beginFile(self, genOpts)
1353     def endFile(self):
1354         OutputGenerator.endFile(self)
1355     def beginFeature(self, interface, emit):
1356         # Start processing in superclass
1357         OutputGenerator.beginFeature(self, interface, emit)
1358     def endFeature(self):
1359         # Finish processing in superclass
1360         OutputGenerator.endFeature(self)
1361
1362     def makeParameterName(self, name):
1363         return 'pname:' + name
1364
1365     def makeStructName(self, name):
1366         return 'sname:' + name
1367
1368     def makeBaseTypeName(self, name):
1369         return 'basetype:' + name
1370
1371     def makeEnumerationName(self, name):
1372         return 'elink:' + name
1373
1374     def makeEnumerantName(self, name):
1375         return 'ename:' + name
1376
1377     def makeFLink(self, name):
1378         return 'flink:' + name
1379
1380     #
1381     # Generate an include file
1382     #
1383     # directory - subdirectory to put file in
1384     # basename - base name of the file
1385     # contents - contents of the file (Asciidoc boilerplate aside)
1386     def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes):
1387         # Create file
1388         filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt'
1389         self.logMsg('diag', '# Generating include file:', filename)
1390         fp = open(filename, 'w')
1391         # Asciidoc anchor
1392         write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
1393
1394         # Valid Usage
1395         if validity is not None:
1396             write('ifndef::doctype-manpage[]', file=fp)
1397             write('.Valid Usage', file=fp)
1398             write('*' * 80, file=fp)
1399             write('endif::doctype-manpage[]', file=fp)
1400             write('ifdef::doctype-manpage[]', file=fp)
1401             write('Valid Usage', file=fp)
1402             write('-----------', file=fp)
1403             write('endif::doctype-manpage[]', file=fp)
1404             write(validity, file=fp, end='')
1405             write('ifndef::doctype-manpage[]', file=fp)
1406             write('*' * 80, file=fp)
1407             write('endif::doctype-manpage[]', file=fp)
1408             write('', file=fp)
1409
1410         # Host Synchronization
1411         if threadsafety is not None:
1412             write('ifndef::doctype-manpage[]', file=fp)
1413             write('.Host Synchronization', file=fp)
1414             write('*' * 80, file=fp)
1415             write('endif::doctype-manpage[]', file=fp)
1416             write('ifdef::doctype-manpage[]', file=fp)
1417             write('Host Synchronization', file=fp)
1418             write('--------------------', file=fp)
1419             write('endif::doctype-manpage[]', file=fp)
1420             write(threadsafety, file=fp, end='')
1421             write('ifndef::doctype-manpage[]', file=fp)
1422             write('*' * 80, file=fp)
1423             write('endif::doctype-manpage[]', file=fp)
1424             write('', file=fp)
1425
1426         # Command Properties - contained within a block, to avoid table numbering
1427         if commandpropertiesentry is not None:
1428             write('ifndef::doctype-manpage[]', file=fp)
1429             write('.Command Properties', file=fp)
1430             write('*' * 80, file=fp)
1431             write('endif::doctype-manpage[]', file=fp)
1432             write('ifdef::doctype-manpage[]', file=fp)
1433             write('Command Properties', file=fp)
1434             write('------------------', file=fp)
1435             write('endif::doctype-manpage[]', file=fp)
1436             write('[options="header", width="100%"]', file=fp)
1437             write('|=====================', file=fp)
1438             write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp)
1439             write(commandpropertiesentry, file=fp)
1440             write('|=====================', file=fp)
1441             write('ifndef::doctype-manpage[]', file=fp)
1442             write('*' * 80, file=fp)
1443             write('endif::doctype-manpage[]', file=fp)
1444             write('', file=fp)
1445
1446         # Success Codes - contained within a block, to avoid table numbering
1447         if successcodes is not None or errorcodes is not None:
1448             write('ifndef::doctype-manpage[]', file=fp)
1449             write('.Return Codes', file=fp)
1450             write('*' * 80, file=fp)
1451             write('endif::doctype-manpage[]', file=fp)
1452             write('ifdef::doctype-manpage[]', file=fp)
1453             write('Return Codes', file=fp)
1454             write('------------', file=fp)
1455             write('endif::doctype-manpage[]', file=fp)
1456             if successcodes is not None:
1457                 write('ifndef::doctype-manpage[]', file=fp)
1458                 write('<<fundamentals-successcodes,Success>>::', file=fp)
1459                 write('endif::doctype-manpage[]', file=fp)
1460                 write('ifdef::doctype-manpage[]', file=fp)
1461                 write('On success, this command returns::', file=fp)
1462                 write('endif::doctype-manpage[]', file=fp)
1463                 write(successcodes, file=fp)
1464             if errorcodes is not None:
1465                 write('ifndef::doctype-manpage[]', file=fp)
1466                 write('<<fundamentals-errorcodes,Failure>>::', file=fp)
1467                 write('endif::doctype-manpage[]', file=fp)
1468                 write('ifdef::doctype-manpage[]', file=fp)
1469                 write('On failure, this command returns::', file=fp)
1470                 write('endif::doctype-manpage[]', file=fp)
1471                 write(errorcodes, file=fp)
1472             write('ifndef::doctype-manpage[]', file=fp)
1473             write('*' * 80, file=fp)
1474             write('endif::doctype-manpage[]', file=fp)
1475             write('', file=fp)
1476
1477         fp.close()
1478
1479     #
1480     # Check if the parameter passed in is a pointer
1481     def paramIsPointer(self, param):
1482         ispointer = False
1483         paramtype = param.find('type')
1484         if paramtype.tail is not None and '*' in paramtype.tail:
1485             ispointer = True
1486
1487         return ispointer
1488
1489     #
1490     # Check if the parameter passed in is a static array
1491     def paramIsStaticArray(self, param):
1492         if param.find('name').tail is not None:
1493             if param.find('name').tail[0] == '[':
1494                 return True
1495
1496     #
1497     # Get the length of a parameter that's been identified as a static array
1498     def staticArrayLength(self, param):
1499         paramname = param.find('name')
1500         paramenumsize = param.find('enum')
1501
1502         if paramenumsize is not None:
1503             return paramenumsize.text
1504         else:
1505             return paramname.tail[1:-1]
1506
1507     #
1508     # Check if the parameter passed in is a pointer to an array
1509     def paramIsArray(self, param):
1510         return param.attrib.get('len') is not None
1511
1512     #
1513     # Get the parent of a handle object
1514     def getHandleParent(self, typename):
1515         types = self.registry.findall("types/type")
1516         for elem in types:
1517             if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1518                 return elem.attrib.get('parent')
1519
1520     #
1521     # Check if a parent object is dispatchable or not
1522     def isHandleTypeDispatchable(self, handlename):
1523         handle = self.registry.find("types/type/[name='" + handlename + "'][@category='handle']")
1524         if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE':
1525             return True
1526         else:
1527             return False
1528
1529     def isHandleOptional(self, param, params):
1530
1531         # See if the handle is optional
1532         isOptional = False
1533
1534         # Simple, if it's optional, return true
1535         if param.attrib.get('optional') is not None:
1536             return True
1537
1538         # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
1539         if param.attrib.get('noautovalidity') is not None:
1540             return True
1541
1542         # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
1543         if self.paramIsArray(param):
1544             lengths = param.attrib.get('len').split(',')
1545             for length in lengths:
1546                 if (length) != 'null-terminated' and (length) != '1':
1547                     for otherparam in params:
1548                         if otherparam.find('name').text == length:
1549                             if otherparam.attrib.get('optional') is not None:
1550                                 return True
1551
1552         return False
1553     #
1554     # Get the category of a type
1555     def getTypeCategory(self, typename):
1556         types = self.registry.findall("types/type")
1557         for elem in types:
1558             if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename:
1559                 return elem.attrib.get('category')
1560
1561     #
1562     # Make a chunk of text for the end of a parameter if it is an array
1563     def makeAsciiDocPreChunk(self, param, params):
1564         paramname = param.find('name')
1565         paramtype = param.find('type')
1566
1567         # General pre-amble. Check optionality and add stuff.
1568         asciidoc = '* '
1569
1570         if self.paramIsStaticArray(param):
1571             asciidoc += 'Any given element of '
1572
1573         elif self.paramIsArray(param):
1574             lengths = param.attrib.get('len').split(',')
1575
1576             # Find all the parameters that are called out as optional, so we can document that they might be zero, and the array may be ignored
1577             optionallengths = []
1578             for length in lengths:
1579                 if (length) != 'null-terminated' and (length) != '1':
1580                     for otherparam in params:
1581                         if otherparam.find('name').text == length:
1582                             if otherparam.attrib.get('optional') is not None:
1583                                 if self.paramIsPointer(otherparam):
1584                                     optionallengths.append('the value referenced by ' + self.makeParameterName(length))
1585                                 else:
1586                                     optionallengths.append(self.makeParameterName(length))
1587
1588             # Document that these arrays may be ignored if any of the length values are 0
1589             if len(optionallengths) != 0 or param.attrib.get('optional') is not None:
1590                 asciidoc += 'If '
1591
1592
1593                 if len(optionallengths) != 0:
1594                     if len(optionallengths) == 1:
1595
1596                         asciidoc += optionallengths[0]
1597                         asciidoc += ' is '
1598
1599                     else:
1600                         asciidoc += ' or '.join(optionallengths)
1601                         asciidoc += ' are '
1602
1603                     asciidoc += 'not `0`, '
1604
1605                 if len(optionallengths) != 0 and param.attrib.get('optional') is not None:
1606                     asciidoc += 'and '
1607
1608                 if param.attrib.get('optional') is not None:
1609                     asciidoc += self.makeParameterName(paramname.text)
1610                     asciidoc += ' is not `NULL`, '
1611
1612         elif param.attrib.get('optional') is not None:
1613             # Don't generate this stub for bitflags
1614             if self.getTypeCategory(paramtype.text) != 'bitmask':
1615                 if param.attrib.get('optional').split(',')[0] == 'true':
1616                     asciidoc += 'If '
1617                     asciidoc += self.makeParameterName(paramname.text)
1618                     asciidoc += ' is not '
1619                     if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text):
1620                         asciidoc += '`NULL`'
1621                     elif self.getTypeCategory(paramtype.text) == 'handle':
1622                         asciidoc += 'sname:VK_NULL_HANDLE'
1623                     else:
1624                         asciidoc += '`0`'
1625
1626                     asciidoc += ', '
1627
1628         return asciidoc
1629
1630     #
1631     # Make the generic asciidoc line chunk portion used for all parameters.
1632     # May return an empty string if nothing to validate.
1633     def createValidationLineForParameterIntroChunk(self, param, params, typetext):
1634         asciidoc = ''
1635         paramname = param.find('name')
1636         paramtype = param.find('type')
1637
1638         asciidoc += self.makeAsciiDocPreChunk(param, params)
1639
1640         asciidoc += self.makeParameterName(paramname.text)
1641         asciidoc += ' must: be '
1642
1643         if self.paramIsArray(param):
1644             # Arrays. These are hard to get right, apparently
1645
1646             lengths = param.attrib.get('len').split(',')
1647
1648             if (lengths[0]) == 'null-terminated':
1649                 asciidoc += 'a null-terminated '
1650             elif (lengths[0]) == '1':
1651                 asciidoc += 'a pointer to '
1652             else:
1653                 asciidoc += 'a pointer to an array of '
1654
1655                 # Handle equations, which are currently denoted with latex
1656                 if 'latexmath:' in lengths[0]:
1657                     asciidoc += lengths[0]
1658                 else:
1659                     asciidoc += self.makeParameterName(lengths[0])
1660                 asciidoc += ' '
1661
1662             for length in lengths[1:]:
1663                 if (length) == 'null-terminated': # This should always be the last thing. If it ever isn't for some bizarre reason, then this will need some massaging.
1664                     asciidoc += 'null-terminated '
1665                 elif (length) == '1':
1666                     asciidoc += 'pointers to '
1667                 else:
1668                     asciidoc += 'pointers to arrays of '
1669                     # Handle equations, which are currently denoted with latex
1670                     if 'latex:' in length:
1671                         asciidoc += length
1672                     else:
1673                         asciidoc += self.makeParameterName(length)
1674                     asciidoc += ' '
1675
1676             # Void pointers don't actually point at anything - remove the word "to"
1677             if paramtype.text == 'void':
1678                 if lengths[-1] == '1':
1679                     if len(lengths) > 1:
1680                         asciidoc = asciidoc[:-5]    # Take care of the extra s added by the post array chunk function. #HACK#
1681                     else:
1682                         asciidoc = asciidoc[:-4]
1683                 else:
1684                     # An array of void values is a byte array.
1685                     asciidoc += 'byte'
1686
1687             elif paramtype.text == 'char':
1688                 # A null terminated array of chars is a string
1689                 if lengths[-1] == 'null-terminated':
1690                     asciidoc += 'string'
1691                 else:
1692                     # Else it's just a bunch of chars
1693                     asciidoc += 'char value'
1694             elif param.text is not None:
1695                 # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1696                 if 'const' in param.text:
1697                     typecategory = self.getTypeCategory(paramtype.text)
1698                     if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(paramtype.text):
1699                         asciidoc += 'valid '
1700
1701             asciidoc += typetext
1702
1703             # pluralize
1704             if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'):
1705                 asciidoc += 's'
1706
1707         elif self.paramIsPointer(param):
1708             # Handle pointers - which are really special case arrays (i.e. they don't have a length)
1709             pointercount = paramtype.tail.count('*')
1710
1711             # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
1712             for i in range(0, pointercount):
1713                 asciidoc += 'a pointer to '
1714
1715             if paramtype.text == 'void':
1716                 # If there's only one pointer, it's optional, and it doesn't point at anything in particular - we don't need any language.
1717                 if pointercount == 1 and param.attrib.get('optional') is not None:
1718                     return '' # early return
1719                 else:
1720                     # Pointer to nothing in particular - delete the " to " portion
1721                     asciidoc = asciidoc[:-4]
1722             else:
1723                 # Add an article for English semantic win
1724                 asciidoc += 'a '
1725
1726             # If a value is "const" that means it won't get modified, so it must be valid going into the function.
1727             if param.text is not None and paramtype.text != 'void':
1728                 if 'const' in param.text:
1729                     asciidoc += 'valid '
1730
1731             asciidoc += typetext
1732
1733         else:
1734             # Non-pointer, non-optional things must be valid
1735             asciidoc += 'a valid '
1736             asciidoc += typetext
1737
1738         if asciidoc != '':
1739             asciidoc += '\n'
1740
1741             # Add additional line for non-optional bitmasks
1742             if self.getTypeCategory(paramtype.text) == 'bitmask':
1743                 if param.attrib.get('optional') is None:
1744                     asciidoc += '* '
1745                     if self.paramIsArray(param):
1746                         asciidoc += 'Each element of '
1747                     asciidoc += 'pname:'
1748                     asciidoc += paramname.text
1749                     asciidoc += ' mustnot: be `0`'
1750                     asciidoc += '\n'
1751
1752         return asciidoc
1753
1754     def makeAsciiDocLineForParameter(self, param, params, typetext):
1755         if param.attrib.get('noautovalidity') is not None:
1756             return ''
1757         asciidoc  = self.createValidationLineForParameterIntroChunk(param, params, typetext)
1758
1759         return asciidoc
1760
1761     # Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance)
1762     def isStructAlwaysValid(self, structname):
1763
1764         struct = self.registry.find("types/type[@name='" + structname + "']")
1765
1766         params = struct.findall('member')
1767         validity = struct.find('validity')
1768
1769         if validity is not None:
1770             return False
1771
1772         for param in params:
1773             paramname = param.find('name')
1774             paramtype = param.find('type')
1775             typecategory = self.getTypeCategory(paramtype.text)
1776
1777             if paramname.text == 'pNext':
1778                 return False
1779
1780             if paramname.text == 'sType':
1781                 return False
1782
1783             if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param):
1784                 if self.makeAsciiDocLineForParameter(param, params, '') != '':
1785                     return False
1786             elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true':
1787                 return False
1788             elif typecategory == 'struct' or typecategory == 'union':
1789                 if self.isStructAlwaysValid(paramtype.text) is False:
1790                     return False
1791
1792         return True
1793
1794     #
1795     # Make an entire asciidoc line for a given parameter
1796     def createValidationLineForParameter(self, param, params, typecategory):
1797         asciidoc = ''
1798         paramname = param.find('name')
1799         paramtype = param.find('type')
1800
1801         if paramtype.text == 'void' or paramtype.text == 'char':
1802             # Chars and void are special cases - needs care inside the generator functions
1803             # A null-terminated char array is a string, else it's chars.
1804             # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
1805             asciidoc += self.makeAsciiDocLineForParameter(param, params, '')
1806         elif typecategory == 'bitmask':
1807             bitsname = paramtype.text.replace('Flags', 'FlagBits')
1808             if self.registry.find("enums[@name='" + bitsname + "']") is None:
1809                 asciidoc += '* '
1810                 asciidoc += self.makeParameterName(paramname.text)
1811                 asciidoc += ' must: be `0`'
1812                 asciidoc += '\n'
1813             else:
1814                 if self.paramIsArray(param):
1815                     asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value')
1816                 else:
1817                     asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values')
1818         elif typecategory == 'handle':
1819             asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' handle')
1820         elif typecategory == 'enum':
1821             asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeEnumerationName(paramtype.text) + ' value')
1822         elif typecategory == 'struct':
1823             if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1824                 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' structure')
1825         elif typecategory == 'union':
1826             if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text):
1827                 asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' union')
1828         elif self.paramIsArray(param) or self.paramIsPointer(param):
1829             asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeBaseTypeName(paramtype.text) + ' value')
1830
1831         return asciidoc
1832
1833     #
1834     # Make an asciidoc validity entry for a handle's parent object
1835     def makeAsciiDocHandleParent(self, param, params):
1836         asciidoc = ''
1837         paramname = param.find('name')
1838         paramtype = param.find('type')
1839
1840         # Deal with handle parents
1841         handleparent = self.getHandleParent(paramtype.text)
1842         if handleparent is not None:
1843             parentreference = None
1844             for otherparam in params:
1845                 if otherparam.find('type').text == handleparent:
1846                     parentreference = otherparam.find('name').text
1847             if parentreference is not None:
1848                 asciidoc += '* '
1849
1850                 if self.isHandleOptional(param, params):
1851                     if self.paramIsArray(param):
1852                         asciidoc += 'Each element of '
1853                         asciidoc += self.makeParameterName(paramname.text)
1854                         asciidoc += ' that is a valid handle'
1855                     else:
1856                         asciidoc += 'If '
1857                         asciidoc += self.makeParameterName(paramname.text)
1858                         asciidoc += ' is a valid handle, it'
1859                 else:
1860                     if self.paramIsArray(param):
1861                         asciidoc += 'Each element of '
1862                     asciidoc += self.makeParameterName(paramname.text)
1863                 asciidoc += ' must: have been created, allocated or retrieved from '
1864                 asciidoc += self.makeParameterName(parentreference)
1865
1866                 asciidoc += '\n'
1867         return asciidoc
1868
1869     #
1870     # Generate an asciidoc validity line for the sType value of a struct
1871     def makeStructureType(self, blockname, param):
1872         asciidoc = '* '
1873         paramname = param.find('name')
1874         paramtype = param.find('type')
1875
1876         asciidoc += self.makeParameterName(paramname.text)
1877         asciidoc += ' must: be '
1878
1879         structuretype = ''
1880         for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname):
1881             if elem[0] == 'Vk':
1882                 structuretype += 'VK_STRUCTURE_TYPE_'
1883             else:
1884                 structuretype += elem[0].upper()
1885                 structuretype += '_'
1886
1887         asciidoc += self.makeEnumerantName(structuretype[:-1])
1888         asciidoc += '\n'
1889
1890         return asciidoc
1891
1892     #
1893     # Generate an asciidoc validity line for the pNext value of a struct
1894     def makeStructureExtensionPointer(self, param):
1895         asciidoc = '* '
1896         paramname = param.find('name')
1897         paramtype = param.find('type')
1898
1899         asciidoc += self.makeParameterName(paramname.text)
1900
1901         validextensionstructs = param.attrib.get('validextensionstructs')
1902         asciidoc += ' must: be `NULL`'
1903         if validextensionstructs is not None:
1904             extensionstructs = ['slink:' + x for x in validextensionstructs.split(',')]
1905             asciidoc += ', or a pointer to a valid instance of '
1906             if len(extensionstructs) == 1:
1907                 asciidoc += validextensionstructs
1908             else:
1909                 asciidoc += (', ').join(extensionstructs[:-1]) + ' or ' + extensionstructs[-1]
1910
1911         asciidoc += '\n'
1912
1913         return asciidoc
1914
1915     #
1916     # Generate all the valid usage information for a given struct or command
1917     def makeValidUsageStatements(self, cmd, blockname, params, usages):
1918         # Start the asciidoc block for this
1919         asciidoc = ''
1920
1921         handles = []
1922         anyparentedhandlesoptional = False
1923         parentdictionary = {}
1924         arraylengths = set()
1925         for param in params:
1926             paramname = param.find('name')
1927             paramtype = param.find('type')
1928
1929             # Get the type's category
1930             typecategory = self.getTypeCategory(paramtype.text)
1931
1932             # Generate language to independently validate a parameter
1933             if paramtype.text == 'VkStructureType' and paramname.text == 'sType':
1934                 asciidoc += self.makeStructureType(blockname, param)
1935             elif paramtype.text == 'void' and paramname.text == 'pNext':
1936                 asciidoc += self.makeStructureExtensionPointer(param)
1937             else:
1938                 asciidoc += self.createValidationLineForParameter(param, params, typecategory)
1939
1940             # Ensure that any parenting is properly validated, and list that a handle was found
1941             if typecategory == 'handle':
1942                 # Don't detect a parent for return values!
1943                 if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text):
1944                     parent = self.getHandleParent(paramtype.text)
1945                     if parent is not None:
1946                         handles.append(param)
1947
1948                         # If any param is optional, it affects the output
1949                         if self.isHandleOptional(param, params):
1950                             anyparentedhandlesoptional = True
1951
1952                         # Find the first dispatchable parent
1953                         ancestor = parent
1954                         while ancestor is not None and not self.isHandleTypeDispatchable(ancestor):
1955                             ancestor = self.getHandleParent(ancestor)
1956
1957                         # If one was found, add this parameter to the parent dictionary
1958                         if ancestor is not None:
1959                             if ancestor not in parentdictionary:
1960                                 parentdictionary[ancestor] = []
1961
1962                             if self.paramIsArray(param):
1963                                 parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text))
1964                             else:
1965                                 parentdictionary[ancestor].append(self.makeParameterName(paramname.text))
1966
1967             # Get the array length for this parameter
1968             arraylength = param.attrib.get('len')
1969             if arraylength is not None:
1970                 for onelength in arraylength.split(','):
1971                     arraylengths.add(onelength)
1972
1973         # For any vkQueue* functions, there might be queue type data
1974         if 'vkQueue' in blockname:
1975             # The queue type must be valid
1976             queuetypes = cmd.attrib.get('queues')
1977             if queuetypes is not None:
1978                 queuebits = []
1979                 for queuetype in re.findall(r'([^,]+)', queuetypes):
1980                     queuebits.append(queuetype.replace('_',' '))
1981
1982                 asciidoc += '* '
1983                 asciidoc += 'The pname:queue must: support '
1984                 if len(queuebits) == 1:
1985                     asciidoc += queuebits[0]
1986                 else:
1987                     asciidoc += (', ').join(queuebits[:-1])
1988                     asciidoc += ' or '
1989                     asciidoc += queuebits[-1]
1990                 asciidoc += ' operations'
1991                 asciidoc += '\n'
1992
1993         if 'vkCmd' in blockname:
1994             # The commandBuffer parameter must be being recorded
1995             asciidoc += '* '
1996             asciidoc += 'pname:commandBuffer must: be in the recording state'
1997             asciidoc += '\n'
1998
1999             # The queue type must be valid
2000             queuetypes = cmd.attrib.get('queues')
2001             queuebits = []
2002             for queuetype in re.findall(r'([^,]+)', queuetypes):
2003                 queuebits.append(queuetype.replace('_',' '))
2004
2005             asciidoc += '* '
2006             asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
2007             if len(queuebits) == 1:
2008                 asciidoc += queuebits[0]
2009             else:
2010                 asciidoc += (', ').join(queuebits[:-1])
2011                 asciidoc += ' or '
2012                 asciidoc += queuebits[-1]
2013             asciidoc += ' operations'
2014             asciidoc += '\n'
2015
2016             # Must be called inside/outside a renderpass appropriately
2017             renderpass = cmd.attrib.get('renderpass')
2018
2019             if renderpass != 'both':
2020                 asciidoc += '* This command must: only be called '
2021                 asciidoc += renderpass
2022                 asciidoc += ' of a render pass instance'
2023                 asciidoc += '\n'
2024
2025             # Must be in the right level command buffer
2026             cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2027
2028             if cmdbufferlevel != 'primary,secondary':
2029                 asciidoc += '* pname:commandBuffer must: be a '
2030                 asciidoc += cmdbufferlevel
2031                 asciidoc += ' sname:VkCommandBuffer'
2032                 asciidoc += '\n'
2033
2034         # Any non-optional arraylengths should specify they must be greater than 0
2035         for param in params:
2036             paramname = param.find('name')
2037
2038             for arraylength in arraylengths:
2039                 if paramname.text == arraylength and param.attrib.get('optional') is None:
2040                     # Get all the array dependencies
2041                     arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2042
2043                     # Get all the optional array dependencies, including those not generating validity for some reason
2044                     optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']")
2045                     optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']"))
2046
2047                     asciidoc += '* '
2048
2049                     # Allow lengths to be arbitrary if all their dependents are optional
2050                     if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0:
2051                         asciidoc += 'If '
2052                         if len(optionalarrays) > 1:
2053                             asciidoc += 'any of '
2054
2055                         for array in optionalarrays[:-1]:
2056                             asciidoc += self.makeParameterName(optionalarrays.find('name').text)
2057                             asciidoc += ', '
2058
2059                         if len(optionalarrays) > 1:
2060                             asciidoc += 'and '
2061                             asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2062                             asciidoc += ' are '
2063                         else:
2064                             asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text)
2065                             asciidoc += ' is '
2066
2067                         asciidoc += 'not `NULL`, '
2068
2069                         if self.paramIsPointer(param):
2070                             asciidoc += 'the value referenced by '
2071
2072                     elif self.paramIsPointer(param):
2073                         asciidoc += 'The value referenced by '
2074
2075                     asciidoc += self.makeParameterName(arraylength)
2076                     asciidoc += ' must: be greater than `0`'
2077                     asciidoc += '\n'
2078
2079         # Find the parents of all objects referenced in this command
2080         for param in handles:
2081             asciidoc += self.makeAsciiDocHandleParent(param, params)
2082
2083         # Find the common ancestors of objects
2084         noancestorscount = 0
2085         while noancestorscount < len(parentdictionary):
2086             noancestorscount = 0
2087             oldparentdictionary = parentdictionary.copy()
2088             for parent in oldparentdictionary.items():
2089                 ancestor = self.getHandleParent(parent[0])
2090
2091                 while ancestor is not None and ancestor not in parentdictionary:
2092                     ancestor = self.getHandleParent(ancestor)
2093
2094                 if ancestor is not None:
2095                     parentdictionary[ancestor] += parentdictionary.pop(parent[0])
2096                 else:
2097                     # No ancestors possible - so count it up
2098                     noancestorscount += 1
2099
2100         # Add validation language about common ancestors
2101         for parent in parentdictionary.items():
2102             if len(parent[1]) > 1:
2103                 parentlanguage = '* '
2104
2105                 parentlanguage += 'Each of '
2106                 parentlanguage += ", ".join(parent[1][:-1])
2107                 parentlanguage += ' and '
2108                 parentlanguage += parent[1][-1]
2109                 if anyparentedhandlesoptional is True:
2110                     parentlanguage += ' that are valid handles'
2111                 parentlanguage += ' must: have been created, allocated or retrieved from the same '
2112                 parentlanguage += self.makeStructName(parent[0])
2113                 parentlanguage += '\n'
2114
2115                 # Capitalize and add to the main language
2116                 asciidoc += parentlanguage
2117
2118         # Add in any plain-text validation language that should be added
2119         for usage in usages:
2120             asciidoc += '* '
2121             asciidoc += usage
2122             asciidoc += '\n'
2123
2124         # In case there's nothing to report, return None
2125         if asciidoc == '':
2126             return None
2127         # Delimit the asciidoc block
2128         return asciidoc
2129
2130     def makeThreadSafetyBlock(self, cmd, paramtext):
2131         """Generate C function pointer typedef for <command> Element"""
2132         paramdecl = ''
2133
2134         # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2135         if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'):
2136             paramdecl += '* '
2137             paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from'
2138             paramdecl += '\n'
2139
2140         # Find and add any parameters that are thread unsafe
2141         explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2142         if (explicitexternsyncparams is not None):
2143             for param in explicitexternsyncparams:
2144                 externsyncattribs = param.attrib.get('externsync')
2145                 paramname = param.find('name')
2146                 for externsyncattrib in externsyncattribs.split(','):
2147                     paramdecl += '* '
2148                     paramdecl += 'Host access to '
2149                     if externsyncattrib == 'true':
2150                         if self.paramIsArray(param):
2151                             paramdecl += 'each member of ' + self.makeParameterName(paramname.text)
2152                         elif self.paramIsPointer(param):
2153                             paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text)
2154                         else:
2155                             paramdecl += self.makeParameterName(paramname.text)
2156                     else:
2157                         paramdecl += 'pname:'
2158                         paramdecl += externsyncattrib
2159                     paramdecl += ' must: be externally synchronized\n'
2160
2161         # Find and add any "implicit" parameters that are thread unsafe
2162         implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2163         if (implicitexternsyncparams is not None):
2164             for elem in implicitexternsyncparams:
2165                 paramdecl += '* '
2166                 paramdecl += 'Host access to '
2167                 paramdecl += elem.text
2168                 paramdecl += ' must: be externally synchronized\n'
2169
2170         if (paramdecl == ''):
2171             return None
2172         else:
2173             return paramdecl
2174
2175     def makeCommandPropertiesTableEntry(self, cmd, name):
2176
2177         if 'vkCmd' in name:
2178             # Must be called inside/outside a renderpass appropriately
2179             cmdbufferlevel = cmd.attrib.get('cmdbufferlevel')
2180             cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
2181
2182             renderpass = cmd.attrib.get('renderpass')
2183             renderpass = renderpass.capitalize()
2184
2185             queues = cmd.attrib.get('queues')
2186             queues = (' + \n').join(queues.upper().split(','))
2187
2188             return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues
2189         elif 'vkQueue' in name:
2190             # Must be called inside/outside a renderpass appropriately
2191
2192             queues = cmd.attrib.get('queues')
2193             if queues is None:
2194                 queues = 'Any'
2195             else:
2196                 queues = (' + \n').join(queues.upper().split(','))
2197
2198             return '|-|-|' + queues
2199
2200         return None
2201
2202     def makeSuccessCodes(self, cmd, name):
2203
2204         successcodes = cmd.attrib.get('successcodes')
2205         if successcodes is not None:
2206
2207             successcodeentry = ''
2208             successcodes = successcodes.split(',')
2209             return '* ename:' + '\n* ename:'.join(successcodes)
2210
2211         return None
2212
2213     def makeErrorCodes(self, cmd, name):
2214
2215         errorcodes = cmd.attrib.get('errorcodes')
2216         if errorcodes is not None:
2217
2218             errorcodeentry = ''
2219             errorcodes = errorcodes.split(',')
2220             return '* ename:' + '\n* ename:'.join(errorcodes)
2221
2222         return None
2223
2224     #
2225     # Command generation
2226     def genCmd(self, cmdinfo, name):
2227         OutputGenerator.genCmd(self, cmdinfo, name)
2228         #
2229         # Get all the parameters
2230         params = cmdinfo.elem.findall('param')
2231         usageelements = cmdinfo.elem.findall('validity/usage')
2232         usages = []
2233
2234         for usage in usageelements:
2235             usages.append(usage.text)
2236         for usage in cmdinfo.additionalValidity:
2237             usages.append(usage.text)
2238         for usage in cmdinfo.removedValidity:
2239             usages.remove(usage.text)
2240
2241         validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages)
2242         threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
2243         commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name)
2244         successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
2245         errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
2246
2247         self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes)
2248
2249     #
2250     # Struct Generation
2251     def genStruct(self, typeinfo, typename):
2252         OutputGenerator.genStruct(self, typeinfo, typename)
2253
2254         # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information.
2255         if typeinfo.elem.attrib.get('returnedonly') is None:
2256             params = typeinfo.elem.findall('member')
2257
2258             usageelements = typeinfo.elem.findall('validity/usage')
2259             usages = []
2260
2261             for usage in usageelements:
2262                 usages.append(usage.text)
2263             for usage in typeinfo.additionalValidity:
2264                 usages.append(usage.text)
2265             for usage in typeinfo.removedValidity:
2266                 usages.remove(usage.text)
2267
2268             validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages)
2269             threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
2270
2271             self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None)
2272         else:
2273             # Still generate files for return only structs, in case this state changes later
2274             self.writeInclude('validity/structs', typename, None, None, None, None, None)
2275
2276     #
2277     # Type Generation
2278     def genType(self, typeinfo, typename):
2279         OutputGenerator.genType(self, typeinfo, typename)
2280
2281         category = typeinfo.elem.get('category')
2282         if (category == 'struct' or category == 'union'):
2283             self.genStruct(typeinfo, typename)
2284
2285 # HostSynchronizationOutputGenerator - subclass of OutputGenerator.
2286 # Generates AsciiDoc includes of the externsync parameter table for the
2287 # fundamentals chapter of the Vulkan specification. Similar to
2288 # DocOutputGenerator.
2289 #
2290 # ---- methods ----
2291 # HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for
2292 #   OutputGenerator. Defines additional internal state.
2293 # ---- methods overriding base class ----
2294 # genCmd(cmdinfo)
2295 class HostSynchronizationOutputGenerator(OutputGenerator):
2296     # Generate Host Synchronized Parameters in a table at the top of the spec
2297     def __init__(self,
2298                  errFile = sys.stderr,
2299                  warnFile = sys.stderr,
2300                  diagFile = sys.stdout):
2301         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2302
2303     threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''}
2304
2305     def makeParameterName(self, name):
2306         return 'pname:' + name
2307
2308     def makeFLink(self, name):
2309         return 'flink:' + name
2310
2311     #
2312     # Generate an include file
2313     #
2314     # directory - subdirectory to put file in
2315     # basename - base name of the file
2316     # contents - contents of the file (Asciidoc boilerplate aside)
2317     def writeInclude(self):
2318
2319         if self.threadsafety['parameters'] is not None:
2320             # Create file
2321             filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt'
2322             self.logMsg('diag', '# Generating include file:', filename)
2323             fp = open(filename, 'w')
2324
2325             # Host Synchronization
2326             write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
2327             write('.Externally Synchronized Parameters', file=fp)
2328             write('*' * 80, file=fp)
2329             write(self.threadsafety['parameters'], file=fp, end='')
2330             write('*' * 80, file=fp)
2331             write('', file=fp)
2332
2333         if self.threadsafety['parameterlists'] is not None:
2334             # Create file
2335             filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt'
2336             self.logMsg('diag', '# Generating include file:', filename)
2337             fp = open(filename, 'w')
2338
2339             # Host Synchronization
2340             write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
2341             write('.Externally Synchronized Parameter Lists', file=fp)
2342             write('*' * 80, file=fp)
2343             write(self.threadsafety['parameterlists'], file=fp, end='')
2344             write('*' * 80, file=fp)
2345             write('', file=fp)
2346
2347         if self.threadsafety['implicit'] is not None:
2348             # Create file
2349             filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt'
2350             self.logMsg('diag', '# Generating include file:', filename)
2351             fp = open(filename, 'w')
2352
2353             # Host Synchronization
2354             write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp)
2355             write('.Implicit Externally Synchronized Parameters', file=fp)
2356             write('*' * 80, file=fp)
2357             write(self.threadsafety['implicit'], file=fp, end='')
2358             write('*' * 80, file=fp)
2359             write('', file=fp)
2360
2361         fp.close()
2362
2363     #
2364     # Check if the parameter passed in is a pointer to an array
2365     def paramIsArray(self, param):
2366         return param.attrib.get('len') is not None
2367
2368     # Check if the parameter passed in is a pointer
2369     def paramIsPointer(self, param):
2370         ispointer = False
2371         paramtype = param.find('type')
2372         if paramtype.tail is not None and '*' in paramtype.tail:
2373             ispointer = True
2374
2375         return ispointer
2376
2377     # Turn the "name[].member[]" notation into plain English.
2378     def makeThreadDereferenceHumanReadable(self, dereference):
2379         matches = re.findall(r"[\w]+[^\w]*",dereference)
2380         stringval = ''
2381         for match in reversed(matches):
2382             if '->' in match or '.' in match:
2383                 stringval += 'member of '
2384             if '[]' in match:
2385                 stringval += 'each element of '
2386
2387             stringval += 'the '
2388             stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0])
2389             stringval += ' '
2390
2391         stringval += 'parameter'
2392
2393         return stringval[0].upper() + stringval[1:]
2394
2395     def makeThreadSafetyBlocks(self, cmd, paramtext):
2396         protoname = cmd.find('proto/name').text
2397
2398         # Find and add any parameters that are thread unsafe
2399         explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
2400         if (explicitexternsyncparams is not None):
2401             for param in explicitexternsyncparams:
2402                 externsyncattribs = param.attrib.get('externsync')
2403                 paramname = param.find('name')
2404                 for externsyncattrib in externsyncattribs.split(','):
2405
2406                     tempstring = '* '
2407                     if externsyncattrib == 'true':
2408                         if self.paramIsArray(param):
2409                             tempstring += 'Each element of the '
2410                         elif self.paramIsPointer(param):
2411                             tempstring += 'The object referenced by the '
2412                         else:
2413                             tempstring += 'The '
2414
2415                         tempstring += self.makeParameterName(paramname.text)
2416                         tempstring += ' parameter'
2417
2418                     else:
2419                         tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib)
2420
2421                     tempstring += ' in '
2422                     tempstring += self.makeFLink(protoname)
2423                     tempstring += '\n'
2424
2425
2426                     if ' element of ' in tempstring:
2427                         self.threadsafety['parameterlists'] += tempstring
2428                     else:
2429                         self.threadsafety['parameters'] += tempstring
2430
2431
2432         # Find and add any "implicit" parameters that are thread unsafe
2433         implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2434         if (implicitexternsyncparams is not None):
2435             for elem in implicitexternsyncparams:
2436                 self.threadsafety['implicit'] += '* '
2437                 self.threadsafety['implicit'] += elem.text[0].upper()
2438                 self.threadsafety['implicit'] += elem.text[1:]
2439                 self.threadsafety['implicit'] += ' in '
2440                 self.threadsafety['implicit'] += self.makeFLink(protoname)
2441                 self.threadsafety['implicit'] += '\n'
2442
2443
2444         # For any vkCmd* functions, the commandBuffer parameter must be being recorded
2445         if protoname is not None and 'vkCmd' in protoname:
2446             self.threadsafety['implicit'] += '* '
2447             self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in '
2448             self.threadsafety['implicit'] += self.makeFLink(protoname)
2449
2450             self.threadsafety['implicit'] += '\n'
2451
2452     #
2453     # Command generation
2454     def genCmd(self, cmdinfo, name):
2455         OutputGenerator.genCmd(self, cmdinfo, name)
2456         #
2457         # Get all thh parameters
2458         params = cmdinfo.elem.findall('param')
2459         usages = cmdinfo.elem.findall('validity/usage')
2460
2461         self.makeThreadSafetyBlocks(cmdinfo.elem, 'param')
2462
2463         self.writeInclude()
2464
2465 # ThreadOutputGenerator - subclass of OutputGenerator.
2466 # Generates Thread checking framework
2467 #
2468 # ---- methods ----
2469 # ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for
2470 #   OutputGenerator. Defines additional internal state.
2471 # ---- methods overriding base class ----
2472 # beginFile(genOpts)
2473 # endFile()
2474 # beginFeature(interface, emit)
2475 # endFeature()
2476 # genType(typeinfo,name)
2477 # genStruct(typeinfo,name)
2478 # genGroup(groupinfo,name)
2479 # genEnum(enuminfo, name)
2480 # genCmd(cmdinfo)
2481 class ThreadOutputGenerator(OutputGenerator):
2482     """Generate specified API interfaces in a specific style, such as a C header"""
2483     # This is an ordered list of sections in the header file.
2484     TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
2485                      'group', 'bitmask', 'funcpointer', 'struct']
2486     ALL_SECTIONS = TYPE_SECTIONS + ['command']
2487     def __init__(self,
2488                  errFile = sys.stderr,
2489                  warnFile = sys.stderr,
2490                  diagFile = sys.stdout):
2491         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2492         # Internal state - accumulators for different inner block text
2493         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2494         self.intercepts = []
2495
2496     # Check if the parameter passed in is a pointer to an array
2497     def paramIsArray(self, param):
2498         return param.attrib.get('len') is not None
2499
2500     # Check if the parameter passed in is a pointer
2501     def paramIsPointer(self, param):
2502         ispointer = False
2503         for elem in param:
2504             #write('paramIsPointer '+elem.text, file=sys.stderr)
2505             #write('elem.tag '+elem.tag, file=sys.stderr)
2506             #if (elem.tail is None):
2507             #    write('elem.tail is None', file=sys.stderr)
2508             #else:
2509             #    write('elem.tail '+elem.tail, file=sys.stderr)
2510             if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail:
2511                 ispointer = True
2512             #    write('is pointer', file=sys.stderr)
2513         return ispointer
2514     def makeThreadUseBlock(self, cmd, functionprefix):
2515         """Generate C function pointer typedef for <command> Element"""
2516         paramdecl = ''
2517         thread_check_dispatchable_objects = [
2518             "VkCommandBuffer",
2519             "VkDevice",
2520             "VkInstance",
2521             "VkQueue",
2522         ]
2523         thread_check_nondispatchable_objects = [
2524             "VkBuffer",
2525             "VkBufferView",
2526             "VkCommandPool",
2527             "VkDescriptorPool",
2528             "VkDescriptorSetLayout",
2529             "VkDeviceMemory",
2530             "VkEvent",
2531             "VkFence",
2532             "VkFramebuffer",
2533             "VkImage",
2534             "VkImageView",
2535             "VkPipeline",
2536             "VkPipelineCache",
2537             "VkPipelineLayout",
2538             "VkQueryPool",
2539             "VkRenderPass",
2540             "VkSampler",
2541             "VkSemaphore",
2542             "VkShaderModule",
2543         ]
2544
2545         # Find and add any parameters that are thread unsafe
2546         params = cmd.findall('param')
2547         for param in params:
2548             paramname = param.find('name')
2549             if False: # self.paramIsPointer(param):
2550                 paramdecl += '    // not watching use of pointer ' + paramname.text + '\n'
2551             else:
2552                 externsync = param.attrib.get('externsync')
2553                 if externsync == 'true':
2554                     if self.paramIsArray(param):
2555                         paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
2556                         paramdecl += '        ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n'
2557                         paramdecl += '    }\n'
2558                     else:
2559                         paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n'
2560                 elif (param.attrib.get('externsync')):
2561                     if self.paramIsArray(param):
2562                         # Externsync can list pointers to arrays of members to synchronize
2563                         paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
2564                         for member in externsync.split(","):
2565                             # Replace first empty [] in member name with index
2566                             element = member.replace('[]','[index]',1)
2567                             if '[]' in element:
2568                                 # Replace any second empty [] in element name with
2569                                 # inner array index based on mapping array names like
2570                                 # "pSomeThings[]" to "someThingCount" array size.
2571                                 # This could be more robust by mapping a param member
2572                                 # name to a struct type and "len" attribute.
2573                                 limit = element[0:element.find('s[]')] + 'Count'
2574                                 dotp = limit.rfind('.p')
2575                                 limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:]
2576                                 paramdecl += '        for(uint32_t index2=0;index2<'+limit+';index2++)\n'
2577                                 element = element.replace('[]','[index2]')
2578                             paramdecl += '            ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n'
2579                         paramdecl += '    }\n'
2580                     else:
2581                         # externsync can list members to synchronize
2582                         for member in externsync.split(","):
2583                             member = str(member).replace("::", "->")
2584                             paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2585                 else:
2586                     paramtype = param.find('type')
2587                     if paramtype is not None:
2588                         paramtype = paramtype.text
2589                     else:
2590                         paramtype = 'None'
2591                     if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
2592                         if self.paramIsArray(param) and ('pPipelines' != paramname.text):
2593                             paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
2594                             paramdecl += '        ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
2595                             paramdecl += '    }\n'
2596                         elif not self.paramIsPointer(param):
2597                             # Pointer params are often being created.
2598                             # They are not being read from.
2599                             paramdecl += '    ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
2600         explicitexternsyncparams = cmd.findall("param[@externsync]")
2601         if (explicitexternsyncparams is not None):
2602             for param in explicitexternsyncparams:
2603                 externsyncattrib = param.attrib.get('externsync')
2604                 paramname = param.find('name')
2605                 paramdecl += '    // Host access to '
2606                 if externsyncattrib == 'true':
2607                     if self.paramIsArray(param):
2608                         paramdecl += 'each member of ' + paramname.text
2609                     elif self.paramIsPointer(param):
2610                         paramdecl += 'the object referenced by ' + paramname.text
2611                     else:
2612                         paramdecl += paramname.text
2613                 else:
2614                     paramdecl += externsyncattrib
2615                 paramdecl += ' must be externally synchronized\n'
2616
2617         # Find and add any "implicit" parameters that are thread unsafe
2618         implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2619         if (implicitexternsyncparams is not None):
2620             for elem in implicitexternsyncparams:
2621                 paramdecl += '    // '
2622                 paramdecl += elem.text
2623                 paramdecl += ' must be externally synchronized between host accesses\n'
2624
2625         if (paramdecl == ''):
2626             return None
2627         else:
2628             return paramdecl
2629     def beginFile(self, genOpts):
2630         OutputGenerator.beginFile(self, genOpts)
2631         # C-specific
2632         #
2633         # Multiple inclusion protection & C++ namespace.
2634         if (genOpts.protectFile and self.genOpts.filename):
2635             headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
2636             write('#ifndef', headerSym, file=self.outFile)
2637             write('#define', headerSym, '1', file=self.outFile)
2638             self.newline()
2639         write('namespace threading {', file=self.outFile)
2640         self.newline()
2641         #
2642         # User-supplied prefix text, if any (list of strings)
2643         if (genOpts.prefixText):
2644             for s in genOpts.prefixText:
2645                 write(s, file=self.outFile)
2646     def endFile(self):
2647         # C-specific
2648         # Finish C++ namespace and multiple inclusion protection
2649         self.newline()
2650         # record intercepted procedures
2651         write('// intercepts', file=self.outFile)
2652         write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
2653         write('\n'.join(self.intercepts), file=self.outFile)
2654         write('};\n', file=self.outFile)
2655         self.newline()
2656         write('} // namespace threading', file=self.outFile)
2657         if (self.genOpts.protectFile and self.genOpts.filename):
2658             self.newline()
2659             write('#endif', file=self.outFile)
2660         # Finish processing in superclass
2661         OutputGenerator.endFile(self)
2662     def beginFeature(self, interface, emit):
2663         #write('// starting beginFeature', file=self.outFile)
2664         # Start processing in superclass
2665         OutputGenerator.beginFeature(self, interface, emit)
2666         # C-specific
2667         # Accumulate includes, defines, types, enums, function pointer typedefs,
2668         # end function prototypes separately for this feature. They're only
2669         # printed in endFeature().
2670         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2671         #write('// ending beginFeature', file=self.outFile)
2672     def endFeature(self):
2673         # C-specific
2674         # Actually write the interface to the output file.
2675         #write('// starting endFeature', file=self.outFile)
2676         if (self.emit):
2677             self.newline()
2678             if (self.genOpts.protectFeature):
2679                 write('#ifndef', self.featureName, file=self.outFile)
2680             # If type declarations are needed by other features based on
2681             # this one, it may be necessary to suppress the ExtraProtect,
2682             # or move it below the 'for section...' loop.
2683             #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
2684             if (self.featureExtraProtect != None):
2685                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2686             #write('#define', self.featureName, '1', file=self.outFile)
2687             for section in self.TYPE_SECTIONS:
2688                 #write('// endFeature writing section'+section, file=self.outFile)
2689                 contents = self.sections[section]
2690                 if contents:
2691                     write('\n'.join(contents), file=self.outFile)
2692                     self.newline()
2693             #write('// endFeature looking at self.sections[command]', file=self.outFile)
2694             if (self.sections['command']):
2695                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2696                 self.newline()
2697             if (self.featureExtraProtect != None):
2698                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2699             if (self.genOpts.protectFeature):
2700                 write('#endif /*', self.featureName, '*/', file=self.outFile)
2701         # Finish processing in superclass
2702         OutputGenerator.endFeature(self)
2703         #write('// ending endFeature', file=self.outFile)
2704     #
2705     # Append a definition to the specified section
2706     def appendSection(self, section, text):
2707         # self.sections[section].append('SECTION: ' + section + '\n')
2708         self.sections[section].append(text)
2709     #
2710     # Type generation
2711     def genType(self, typeinfo, name):
2712         pass
2713     #
2714     # Struct (e.g. C "struct" type) generation.
2715     # This is a special case of the <type> tag where the contents are
2716     # interpreted as a set of <member> tags instead of freeform C
2717     # C type declarations. The <member> tags are just like <param>
2718     # tags - they are a declaration of a struct or union member.
2719     # Only simple member declarations are supported (no nested
2720     # structs etc.)
2721     def genStruct(self, typeinfo, typeName):
2722         OutputGenerator.genStruct(self, typeinfo, typeName)
2723         body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
2724         # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
2725         for member in typeinfo.elem.findall('.//member'):
2726             body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
2727             body += ';\n'
2728         body += '} ' + typeName + ';\n'
2729         self.appendSection('struct', body)
2730     #
2731     # Group (e.g. C "enum" type) generation.
2732     # These are concatenated together with other types.
2733     def genGroup(self, groupinfo, groupName):
2734         pass
2735     # Enumerant generation
2736     # <enum> tags may specify their values in several ways, but are usually
2737     # just integers.
2738     def genEnum(self, enuminfo, name):
2739         pass
2740     #
2741     # Command generation
2742     def genCmd(self, cmdinfo, name):
2743         # Commands shadowed by interface functions and are not implemented
2744         interface_functions = [
2745             'vkEnumerateInstanceLayerProperties',
2746             'vkEnumerateInstanceExtensionProperties',
2747             'vkEnumerateDeviceLayerProperties',
2748         ]
2749         if name in interface_functions:
2750             return
2751         special_functions = [
2752             'vkGetDeviceProcAddr',
2753             'vkGetInstanceProcAddr',
2754             'vkCreateDevice',
2755             'vkDestroyDevice',
2756             'vkCreateInstance',
2757             'vkDestroyInstance',
2758             'vkAllocateCommandBuffers',
2759             'vkFreeCommandBuffers',
2760             'vkCreateDebugReportCallbackEXT',
2761             'vkDestroyDebugReportCallbackEXT',
2762         ]
2763         if name in special_functions:
2764             decls = self.makeCDecls(cmdinfo.elem)
2765             self.appendSection('command', '')
2766             self.appendSection('command', '// declare only')
2767             self.appendSection('command', decls[0])
2768             self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
2769             return
2770         if "KHR" in name:
2771             self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2772             return
2773         if ("DebugMarker" in name) and ("EXT" in name):
2774             self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
2775             return
2776         # Determine first if this function needs to be intercepted
2777         startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2778         if startthreadsafety is None:
2779             return
2780         finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
2781         # record that the function will be intercepted
2782         if (self.featureExtraProtect != None):
2783             self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
2784         self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
2785         if (self.featureExtraProtect != None):
2786             self.intercepts += [ '#endif' ]
2787
2788         OutputGenerator.genCmd(self, cmdinfo, name)
2789         #
2790         decls = self.makeCDecls(cmdinfo.elem)
2791         self.appendSection('command', '')
2792         self.appendSection('command', decls[0][:-1])
2793         self.appendSection('command', '{')
2794         # setup common to call wrappers
2795         # first parameter is always dispatchable
2796         dispatchable_type = cmdinfo.elem.find('param/type').text
2797         dispatchable_name = cmdinfo.elem.find('param/name').text
2798         self.appendSection('command', '    dispatch_key key = get_dispatch_key('+dispatchable_name+');')
2799         self.appendSection('command', '    layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
2800         if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
2801             self.appendSection('command', '    VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
2802         else:
2803             self.appendSection('command', '    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
2804         # Declare result variable, if any.
2805         resulttype = cmdinfo.elem.find('proto/type')
2806         if (resulttype != None and resulttype.text == 'void'):
2807           resulttype = None
2808         if (resulttype != None):
2809             self.appendSection('command', '    ' + resulttype.text + ' result;')
2810             assignresult = 'result = '
2811         else:
2812             assignresult = ''
2813
2814         self.appendSection('command', '    bool threadChecks = startMultiThread();')
2815         self.appendSection('command', '    if (threadChecks) {')
2816         self.appendSection('command', "    "+"\n    ".join(str(startthreadsafety).rstrip().split("\n")))
2817         self.appendSection('command', '    }')
2818         params = cmdinfo.elem.findall('param/name')
2819         paramstext = ','.join([str(param.text) for param in params])
2820         API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2821         self.appendSection('command', '    ' + assignresult + API + '(' + paramstext + ');')
2822         self.appendSection('command', '    if (threadChecks) {')
2823         self.appendSection('command', "    "+"\n    ".join(str(finishthreadsafety).rstrip().split("\n")))
2824         self.appendSection('command', '    } else {')
2825         self.appendSection('command', '        finishMultiThread();')
2826         self.appendSection('command', '    }')
2827         # Return result variable, if any.
2828         if (resulttype != None):
2829             self.appendSection('command', '    return result;')
2830         self.appendSection('command', '}')
2831     #
2832     # override makeProtoName to drop the "vk" prefix
2833     def makeProtoName(self, name, tail):
2834         return self.genOpts.apientry + name[2:] + tail
2835
2836 # ParamCheckerOutputGenerator - subclass of OutputGenerator.
2837 # Generates param checker layer code.
2838 #
2839 # ---- methods ----
2840 # ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
2841 #   OutputGenerator. Defines additional internal state.
2842 # ---- methods overriding base class ----
2843 # beginFile(genOpts)
2844 # endFile()
2845 # beginFeature(interface, emit)
2846 # endFeature()
2847 # genType(typeinfo,name)
2848 # genStruct(typeinfo,name)
2849 # genGroup(groupinfo,name)
2850 # genEnum(enuminfo, name)
2851 # genCmd(cmdinfo)
2852 class ParamCheckerOutputGenerator(OutputGenerator):
2853     """Generate ParamChecker code based on XML element attributes"""
2854     # This is an ordered list of sections in the header file.
2855     ALL_SECTIONS = ['command']
2856     def __init__(self,
2857                  errFile = sys.stderr,
2858                  warnFile = sys.stderr,
2859                  diagFile = sys.stdout):
2860         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2861         self.INDENT_SPACES = 4
2862         # Commands to ignore
2863         self.blacklist = [
2864             'vkGetInstanceProcAddr',
2865             'vkGetDeviceProcAddr',
2866             'vkEnumerateInstanceLayerProperties',
2867             'vkEnumerateInstanceExtensionsProperties',
2868             'vkEnumerateDeviceLayerProperties',
2869             'vkEnumerateDeviceExtensionsProperties',
2870             'vkCreateDebugReportCallbackEXT',
2871             'vkDebugReportMessageEXT']
2872         # Validation conditions for some special case struct members that are conditionally validated
2873         self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
2874         # Header version
2875         self.headerVersion = None
2876         # Internal state - accumulators for different inner block text
2877         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2878         self.structNames = []                             # List of Vulkan struct typenames
2879         self.stypes = []                                  # Values from the VkStructureType enumeration
2880         self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType
2881         self.handleTypes = set()                          # Set of handle type names
2882         self.commands = []                                # List of CommandData records for all Vulkan commands
2883         self.structMembers = []                           # List of StructMemberData records for all Vulkan structs
2884         self.validatedStructs = dict()                    # Map of structs type names to generated validation code for that struct type
2885         self.enumRanges = dict()                          # Map of enum name to BEGIN/END range values
2886         self.flags = set()                                # Map of flags typenames
2887         self.flagBits = dict()                            # Map of flag bits typename to list of values
2888         # Named tuples to store struct and command data
2889         self.StructType = namedtuple('StructType', ['name', 'value'])
2890         self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
2891                                                         'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
2892                                                         'condition', 'cdecl'])
2893         self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
2894         self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
2895     #
2896     def incIndent(self, indent):
2897         inc = ' ' * self.INDENT_SPACES
2898         if indent:
2899             return indent + inc
2900         return inc
2901     #
2902     def decIndent(self, indent):
2903         if indent and (len(indent) > self.INDENT_SPACES):
2904             return indent[:-self.INDENT_SPACES]
2905         return ''
2906     #
2907     def beginFile(self, genOpts):
2908         OutputGenerator.beginFile(self, genOpts)
2909         # C-specific
2910         #
2911         # User-supplied prefix text, if any (list of strings)
2912         if (genOpts.prefixText):
2913             for s in genOpts.prefixText:
2914                 write(s, file=self.outFile)
2915         #
2916         # Multiple inclusion protection & C++ wrappers.
2917         if (genOpts.protectFile and self.genOpts.filename):
2918             headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2919             write('#ifndef', headerSym, file=self.outFile)
2920             write('#define', headerSym, '1', file=self.outFile)
2921             self.newline()
2922         #
2923         # Headers
2924         write('#include <string>', file=self.outFile)
2925         self.newline()
2926         write('#include "vulkan/vulkan.h"', file=self.outFile)
2927         write('#include "vk_layer_extension_utils.h"', file=self.outFile)
2928         write('#include "parameter_validation_utils.h"', file=self.outFile)
2929         #
2930         # Macros
2931         self.newline()
2932         write('#ifndef UNUSED_PARAMETER', file=self.outFile)
2933         write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
2934         write('#endif // UNUSED_PARAMETER', file=self.outFile)
2935         #
2936         # Namespace
2937         self.newline()
2938         write('namespace parameter_validation {', file = self.outFile)
2939     def endFile(self):
2940         # C-specific
2941         self.newline()
2942         # Namespace
2943         write('} // namespace parameter_validation', file = self.outFile)
2944         # Finish C++ wrapper and multiple inclusion protection
2945         if (self.genOpts.protectFile and self.genOpts.filename):
2946             self.newline()
2947             write('#endif', file=self.outFile)
2948         # Finish processing in superclass
2949         OutputGenerator.endFile(self)
2950     def beginFeature(self, interface, emit):
2951         # Start processing in superclass
2952         OutputGenerator.beginFeature(self, interface, emit)
2953         # C-specific
2954         # Accumulate includes, defines, types, enums, function pointer typedefs,
2955         # end function prototypes separately for this feature. They're only
2956         # printed in endFeature().
2957         self.headerVersion = None
2958         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2959         self.structNames = []
2960         self.stypes = []
2961         self.structTypes = dict()
2962         self.handleTypes = set()
2963         self.commands = []
2964         self.structMembers = []
2965         self.validatedStructs = dict()
2966         self.enumRanges = dict()
2967         self.flags = set()
2968         self.flagBits = dict()
2969     def endFeature(self):
2970         # C-specific
2971         # Actually write the interface to the output file.
2972         if (self.emit):
2973             self.newline()
2974             # If type declarations are needed by other features based on
2975             # this one, it may be necessary to suppress the ExtraProtect,
2976             # or move it below the 'for section...' loop.
2977             if (self.featureExtraProtect != None):
2978                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2979             # Generate the struct member checking code from the captured data
2980             self.processStructMemberData()
2981             # Generate the command parameter checking code from the captured data
2982             self.processCmdData()
2983             # Write the declaration for the HeaderVersion
2984             if self.headerVersion:
2985                 write('const uint32_t GeneratedHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
2986                 self.newline()
2987             # Write the declarations for the VkFlags values combining all flag bits
2988             for flag in sorted(self.flags):
2989                 flagBits = flag.replace('Flags', 'FlagBits')
2990                 if flagBits in self.flagBits:
2991                     bits = self.flagBits[flagBits]
2992                     decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
2993                     for bit in bits[1:]:
2994                         decl += '|' + bit
2995                     decl += ';'
2996                     write(decl, file=self.outFile)
2997             self.newline()
2998             # Write the parameter validation code to the file
2999             if (self.sections['command']):
3000                 if (self.genOpts.protectProto):
3001                     write(self.genOpts.protectProto,
3002                           self.genOpts.protectProtoStr, file=self.outFile)
3003                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
3004             if (self.featureExtraProtect != None):
3005                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
3006             else:
3007                 self.newline()
3008         # Finish processing in superclass
3009         OutputGenerator.endFeature(self)
3010     #
3011     # Append a definition to the specified section
3012     def appendSection(self, section, text):
3013         # self.sections[section].append('SECTION: ' + section + '\n')
3014         self.sections[section].append(text)
3015     #
3016     # Type generation
3017     def genType(self, typeinfo, name):
3018         OutputGenerator.genType(self, typeinfo, name)
3019         typeElem = typeinfo.elem
3020         # If the type is a struct type, traverse the imbedded <member> tags
3021         # generating a structure. Otherwise, emit the tag text.
3022         category = typeElem.get('category')
3023         if (category == 'struct' or category == 'union'):
3024             self.structNames.append(name)
3025             self.genStruct(typeinfo, name)
3026         elif (category == 'handle'):
3027             self.handleTypes.add(name)
3028         elif (category == 'bitmask'):
3029             self.flags.add(name)
3030         elif (category == 'define'):
3031             if name == 'VK_HEADER_VERSION':
3032                 nameElem = typeElem.find('name')
3033                 self.headerVersion = noneStr(nameElem.tail).strip()
3034     #
3035     # Struct parameter check generation.
3036     # This is a special case of the <type> tag where the contents are
3037     # interpreted as a set of <member> tags instead of freeform C
3038     # C type declarations. The <member> tags are just like <param>
3039     # tags - they are a declaration of a struct or union member.
3040     # Only simple member declarations are supported (no nested
3041     # structs etc.)
3042     def genStruct(self, typeinfo, typeName):
3043         OutputGenerator.genStruct(self, typeinfo, typeName)
3044         conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
3045         members = typeinfo.elem.findall('.//member')
3046         #
3047         # Iterate over members once to get length parameters for arrays
3048         lens = set()
3049         for member in members:
3050             len = self.getLen(member)
3051             if len:
3052                 lens.add(len)
3053         #
3054         # Generate member info
3055         membersInfo = []
3056         for member in members:
3057             # Get the member's type and name
3058             info = self.getTypeNameTuple(member)
3059             type = info[0]
3060             name = info[1]
3061             stypeValue = ''
3062             cdecl = self.makeCParamDecl(member, 0)
3063             # Process VkStructureType
3064             if type == 'VkStructureType':
3065                 # Extract the required struct type value from the comments
3066                 # embedded in the original text defining the 'typeinfo' element
3067                 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
3068                 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
3069                 if result:
3070                     value = result.group(0)
3071                 else:
3072                     value = self.genVkStructureType(typeName)
3073                 # Store the required type value
3074                 self.structTypes[typeName] = self.StructType(name=name, value=value)
3075             #
3076             # Store pointer/array/string info
3077             # Check for parameter name in lens set
3078             iscount = False
3079             if name in lens:
3080                 iscount = True
3081             # The pNext members are not tagged as optional, but are treated as
3082             # optional for parameter NULL checks.  Static array members
3083             # are also treated as optional to skip NULL pointer validation, as
3084             # they won't be NULL.
3085             isstaticarray = self.paramIsStaticArray(member)
3086             isoptional = False
3087             if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3088                 isoptional = True
3089             membersInfo.append(self.CommandParam(type=type, name=name,
3090                                                 ispointer=self.paramIsPointer(member),
3091                                                 isstaticarray=isstaticarray,
3092                                                 isbool=True if type == 'VkBool32' else False,
3093                                                 israngedenum=True if type in self.enumRanges else False,
3094                                                 isconst=True if 'const' in cdecl else False,
3095                                                 isoptional=isoptional,
3096                                                 iscount=iscount,
3097                                                 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
3098                                                 len=self.getLen(member),
3099                                                 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
3100                                                 condition=conditions[name] if conditions and name in conditions else None,
3101                                                 cdecl=cdecl))
3102         self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
3103     #
3104     # Capture group (e.g. C "enum" type) info to be used for
3105     # param check code generation.
3106     # These are concatenated together with other types.
3107     def genGroup(self, groupinfo, groupName):
3108         OutputGenerator.genGroup(self, groupinfo, groupName)
3109         groupElem = groupinfo.elem
3110         #
3111         # Store the sType values
3112         if groupName == 'VkStructureType':
3113             for elem in groupElem.findall('enum'):
3114                 self.stypes.append(elem.get('name'))
3115         elif 'FlagBits' in groupName:
3116             bits = []
3117             for elem in groupElem.findall('enum'):
3118                 bits.append(elem.get('name'))
3119             if bits:
3120                 self.flagBits[groupName] = bits
3121         else:
3122             # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3123             expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3124             expandPrefix = expandName
3125             expandSuffix = ''
3126             expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3127             if expandSuffixMatch:
3128                 expandSuffix = '_' + expandSuffixMatch.group()
3129                 # Strip off the suffix from the prefix
3130                 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3131             isEnum = ('FLAG_BITS' not in expandPrefix)
3132             if isEnum:
3133                 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
3134     #
3135     # Capture command parameter info to be used for param
3136     # check code generation.
3137     def genCmd(self, cmdinfo, name):
3138         OutputGenerator.genCmd(self, cmdinfo, name)
3139         if name not in self.blacklist:
3140             params = cmdinfo.elem.findall('param')
3141             # Get list of array lengths
3142             lens = set()
3143             for param in params:
3144                 len = self.getLen(param)
3145                 if len:
3146                     lens.add(len)
3147             # Get param info
3148             paramsInfo = []
3149             for param in params:
3150                 paramInfo = self.getTypeNameTuple(param)
3151                 cdecl = self.makeCParamDecl(param, 0)
3152                 # Check for parameter name in lens set
3153                 iscount = False
3154                 if paramInfo[1] in lens:
3155                     iscount = True
3156                 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3157                                                     ispointer=self.paramIsPointer(param),
3158                                                     isstaticarray=self.paramIsStaticArray(param),
3159                                                     isbool=True if paramInfo[0] == 'VkBool32' else False,
3160                                                     israngedenum=True if paramInfo[0] in self.enumRanges else False,
3161                                                     isconst=True if 'const' in cdecl else False,
3162                                                     isoptional=self.paramIsOptional(param),
3163                                                     iscount=iscount,
3164                                                     noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
3165                                                     len=self.getLen(param),
3166                                                     extstructs=None,
3167                                                     condition=None,
3168                                                     cdecl=cdecl))
3169             self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3170     #
3171     # Check if the parameter passed in is a pointer
3172     def paramIsPointer(self, param):
3173         ispointer = 0
3174         paramtype = param.find('type')
3175         if (paramtype.tail is not None) and ('*' in paramtype.tail):
3176             ispointer = paramtype.tail.count('*')
3177         elif paramtype.text[:4] == 'PFN_':
3178             # Treat function pointer typedefs as a pointer to a single value
3179             ispointer = 1
3180         return ispointer
3181     #
3182     # Check if the parameter passed in is a static array
3183     def paramIsStaticArray(self, param):
3184         isstaticarray = 0
3185         paramname = param.find('name')
3186         if (paramname.tail is not None) and ('[' in paramname.tail):
3187             isstaticarray = paramname.tail.count('[')
3188         return isstaticarray
3189     #
3190     # Check if the parameter passed in is optional
3191     # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3192     def paramIsOptional(self, param):
3193         # See if the handle is optional
3194         isoptional = False
3195         # Simple, if it's optional, return true
3196         optString = param.attrib.get('optional')
3197         if optString:
3198             if optString == 'true':
3199                 isoptional = True
3200             elif ',' in optString:
3201                 opts = []
3202                 for opt in optString.split(','):
3203                     val = opt.strip()
3204                     if val == 'true':
3205                         opts.append(True)
3206                     elif val == 'false':
3207                         opts.append(False)
3208                     else:
3209                         print('Unrecognized len attribute value',val)
3210                 isoptional = opts
3211         return isoptional
3212     #
3213     # Check if the handle passed in is optional
3214     # Uses the same logic as ValidityOutputGenerator.isHandleOptional
3215     def isHandleOptional(self, param, lenParam):
3216         # Simple, if it's optional, return true
3217         if param.isoptional:
3218             return True
3219         # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
3220         if param.noautovalidity:
3221             return True
3222         # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
3223         if lenParam and lenParam.isoptional:
3224             return True
3225         return False
3226     #
3227     # Generate a VkStructureType based on a structure typename
3228     def genVkStructureType(self, typename):
3229         # Add underscore between lowercase then uppercase
3230         value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
3231         # Change to uppercase
3232         value = value.upper()
3233         # Add STRUCTURE_TYPE_
3234         return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
3235     #
3236     # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
3237     # value assuming the struct is defined by a different feature
3238     def getStructType(self, typename):
3239         value = None
3240         if typename in self.structTypes:
3241             value = self.structTypes[typename].value
3242         else:
3243             value = self.genVkStructureType(typename)
3244             self.logMsg('diag', 'ParameterValidation: Generating {} for {} structure type that was not defined by the current feature'.format(value, typename))
3245         return value
3246     #
3247     # Retrieve the value of the len tag
3248     def getLen(self, param):
3249         result = None
3250         len = param.attrib.get('len')
3251         if len and len != 'null-terminated':
3252             # For string arrays, 'len' can look like 'count,null-terminated',
3253             # indicating that we have a null terminated array of strings.  We
3254             # strip the null-terminated from the 'len' field and only return
3255             # the parameter specifying the string count
3256             if 'null-terminated' in len:
3257                 result = len.split(',')[0]
3258             else:
3259                 result = len
3260             result = str(result).replace('::', '->')
3261         return result
3262     #
3263     # Retrieve the type and name for a parameter
3264     def getTypeNameTuple(self, param):
3265         type = ''
3266         name = ''
3267         for elem in param:
3268             if elem.tag == 'type':
3269                 type = noneStr(elem.text)
3270             elif elem.tag == 'name':
3271                 name = noneStr(elem.text)
3272         return (type, name)
3273     #
3274     # Find a named parameter in a parameter list
3275     def getParamByName(self, params, name):
3276         for param in params:
3277             if param.name == name:
3278                 return param
3279         return None
3280     #
3281     # Extract length values from latexmath.  Currently an inflexible solution that looks for specific
3282     # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced.
3283     def parseLateXMath(self, source):
3284         name = 'ERROR'
3285         decoratedName = 'ERROR'
3286         if 'mathit' in source:
3287             # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3288             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)
3289             if not match or match.group(1) != match.group(4):
3290                 raise 'Unrecognized latexmath expression'
3291             name = match.group(2)
3292             decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3293         else:
3294             # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3295             match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3296             name = match.group(1)
3297             decoratedName = '{}/{}'.format(*match.group(1, 2))
3298         return name, decoratedName
3299     #
3300     # Get the length paramater record for the specified parameter name
3301     def getLenParam(self, params, name):
3302         lenParam = None
3303         if name:
3304             if '->' in name:
3305                 # The count is obtained by dereferencing a member of a struct parameter
3306                 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
3307                                              isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
3308                                              condition=None, cdecl=None)
3309             elif 'latexmath' in name:
3310                 lenName, decoratedName = self.parseLateXMath(name)
3311                 lenParam = self.getParamByName(params, lenName)
3312                 # TODO: Zero-check the result produced by the equation?
3313                 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3314                 #param = self.getParamByName(params, lenName)
3315                 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3316                 #                             isoptional=param.isoptional, type=param.type, len=param.len,
3317                 #                             isstaticarray=param.isstaticarray, extstructs=param.extstructs,
3318                 #                             noautovalidity=True, condition=None, cdecl=param.cdecl)
3319             else:
3320                 lenParam = self.getParamByName(params, name)
3321         return lenParam
3322     #
3323     # Convert a vulkan.h command declaration into a parameter_validation.h definition
3324     def getCmdDef(self, cmd):
3325         #
3326         # Strip the trailing ';' and split into individual lines
3327         lines = cmd.cdecl[:-1].split('\n')
3328         # Replace Vulkan prototype
3329         lines[0] = 'static bool parameter_validation_' + cmd.name + '('
3330         # Replace the first argument with debug_report_data, when the first
3331         # argument is a handle (not vkCreateInstance)
3332         reportData = '    debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3333         if cmd.name != 'vkCreateInstance':
3334             lines[1] = reportData
3335         else:
3336             lines.insert(1, reportData)
3337         return '\n'.join(lines)
3338     #
3339     # Generate the code to check for a NULL dereference before calling the
3340     # validation function
3341     def genCheckedLengthCall(self, name, exprs):
3342         count = name.count('->')
3343         if count:
3344             checkedExpr = []
3345             localIndent = ''
3346             elements = name.split('->')
3347             # Open the if expression blocks
3348             for i in range(0, count):
3349                 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
3350                 localIndent = self.incIndent(localIndent)
3351             # Add the validation expression
3352             for expr in exprs:
3353                 checkedExpr.append(localIndent + expr)
3354             # Close the if blocks
3355             for i in range(0, count):
3356                 localIndent = self.decIndent(localIndent)
3357                 checkedExpr.append(localIndent + '}\n')
3358             return [checkedExpr]
3359         # No if statements were required
3360         return exprs
3361     #
3362     # Generate code to check for a specific condition before executing validation code
3363     def genConditionalCall(self, prefix, condition, exprs):
3364         checkedExpr = []
3365         localIndent = ''
3366         formattedCondition = condition.format(prefix)
3367         checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
3368         checkedExpr.append(localIndent + '{\n')
3369         localIndent = self.incIndent(localIndent)
3370         for expr in exprs:
3371             checkedExpr.append(localIndent + expr)
3372         localIndent = self.decIndent(localIndent)
3373         checkedExpr.append(localIndent + '}\n')
3374         return [checkedExpr]
3375     #
3376     # Generate the sType check string
3377     def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3378         checkExpr = []
3379         stype = self.structTypes[value.type]
3380         if lenValue:
3381             # This is an array with a pointer to a count value
3382             if lenValue.ispointer:
3383                 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
3384                 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
3385                     funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
3386             # This is an array with an integer count value
3387             else:
3388                 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
3389                     funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
3390         # This is an individual struct
3391         else:
3392             checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {});\n'.format(
3393                 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value, **postProcSpec))
3394         return checkExpr
3395     #
3396     # Generate the handle check string
3397     def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3398         checkExpr = []
3399         if lenValue:
3400             if lenValue.ispointer:
3401                 # This is assumed to be an output array with a pointer to a count value
3402                 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
3403             else:
3404                 # This is an array with an integer count value
3405                 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3406                     funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3407         else:
3408             # This is assumed to be an output handle pointer
3409             raise('Unsupported parameter validation case: Output handles are not NULL checked')
3410         return checkExpr
3411     #
3412     # Generate check string for an array of VkFlags values
3413     def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3414         checkExpr = []
3415         flagBitsName = value.type.replace('Flags', 'FlagBits')
3416         if not flagBitsName in self.flagBits:
3417             raise('Unsupported parameter validation case: array of reserved VkFlags')
3418         else:
3419             allFlags = 'All' + flagBitsName
3420             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))
3421         return checkExpr
3422     #
3423     # Generate pNext check string
3424     def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec):
3425         checkExpr = []
3426         # Generate an array of acceptable VkStructureType values for pNext
3427         extStructCount = 0
3428         extStructVar = 'NULL'
3429         extStructNames = 'NULL'
3430         if value.extstructs:
3431             structs = value.extstructs.split(',')
3432             checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
3433             extStructCount = 'ARRAY_SIZE(allowedStructs)'
3434             extStructVar = 'allowedStructs'
3435             extStructNames = '"' + ', '.join(structs) + '"'
3436         checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion);\n'.format(
3437             funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, **postProcSpec))
3438         return checkExpr
3439     #
3440     # Generate the pointer check string
3441     def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3442         checkExpr = []
3443         if lenValue:
3444             # This is an array with a pointer to a count value
3445             if lenValue.ispointer:
3446                 # If count and array parameters are optional, there will be no validation
3447                 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
3448                     # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
3449                     checkExpr.append('skipCall |= validate_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
3450                         funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3451             # This is an array with an integer count value
3452             else:
3453                 # If count and array parameters are optional, there will be no validation
3454                 if valueRequired == 'true' or lenValueRequired == 'true':
3455                     # Arrays of strings receive special processing
3456                     validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
3457                     checkExpr.append('skipCall |= {}(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3458                         validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3459             if checkExpr:
3460                 if lenValue and ('->' in lenValue.name):
3461                     # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3462                     checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
3463         # This is an individual struct that is not allowed to be NULL
3464         elif not value.isoptional:
3465             # Function pointers need a reinterpret_cast to void*
3466             if value.type[:4] == 'PFN_':
3467                 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
3468             else:
3469                 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
3470         return checkExpr
3471     #
3472     # Process struct member validation code, performing name suibstitution if required
3473     def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
3474         # Build format specifier list
3475         kwargs = {}
3476         if '{postProcPrefix}' in line:
3477             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3478             if type(memberDisplayNamePrefix) is tuple:
3479                 kwargs['postProcPrefix'] = 'ParameterName('
3480             else:
3481                 kwargs['postProcPrefix'] = postProcSpec['ppp']
3482         if '{postProcSuffix}' in line:
3483             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3484             if type(memberDisplayNamePrefix) is tuple:
3485                 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3486             else:
3487                 kwargs['postProcSuffix'] = postProcSpec['pps']
3488         if '{postProcInsert}' in line:
3489             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3490             if type(memberDisplayNamePrefix) is tuple:
3491                 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3492             else:
3493                 kwargs['postProcInsert'] = postProcSpec['ppi']
3494         if '{funcName}' in line:
3495             kwargs['funcName'] = funcName
3496         if '{valuePrefix}' in line:
3497             kwargs['valuePrefix'] = memberNamePrefix
3498         if '{displayNamePrefix}' in line:
3499             # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
3500             if type(memberDisplayNamePrefix) is tuple:
3501                 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
3502             else:
3503                 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
3504
3505         if kwargs:
3506             # Need to escape the C++ curly braces
3507             if 'IndexVector' in line:
3508                 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
3509                 line = line.replace(' }),', ' }}),')
3510             return line.format(**kwargs)
3511         return line
3512     #
3513     # Process struct validation code for inclusion in function or parent struct validation code
3514     def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
3515         for line in lines:
3516             if output:
3517                 output[-1] += '\n'
3518             if type(line) is list:
3519                 for sub in line:
3520                     output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
3521             else:
3522                 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
3523         return output
3524     #
3525     # Process struct pointer/array validation code, perfoeming name substitution if required
3526     def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
3527         expr = []
3528         expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
3529         expr.append('{')
3530         indent = self.incIndent(None)
3531         if lenValue:
3532             # Need to process all elements in the array
3533             indexName = lenValue.name.replace('Count', 'Index')
3534             expr[-1] += '\n'
3535             expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
3536             expr.append(indent + '{')
3537             indent = self.incIndent(indent)
3538             # Prefix for value name to display in error message
3539             memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
3540             memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
3541         else:
3542             memberNamePrefix = '{}{}->'.format(prefix, value.name)
3543             memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
3544         #
3545         # Expand the struct validation lines
3546         expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
3547         #
3548         if lenValue:
3549             # Close if and for scopes
3550             indent = self.decIndent(indent)
3551             expr.append(indent + '}\n')
3552         expr.append('}\n')
3553         return expr
3554     #
3555     # Generate the parameter checking code
3556     def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
3557         lines = []    # Generated lines of code
3558         unused = []   # Unused variable names
3559         for value in values:
3560             usedLines = []
3561             lenParam = None
3562             #
3563             # 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.
3564             postProcSpec = {}
3565             postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
3566             postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
3567             postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
3568             #
3569             # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
3570             valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
3571             #
3572             # Check for NULL pointers, ignore the inout count parameters that
3573             # will be validated with their associated array
3574             if (value.ispointer or value.isstaticarray) and not value.iscount:
3575                 #
3576                 # Parameters for function argument generation
3577                 req = 'true'    # Paramerter cannot be NULL
3578                 cpReq = 'true'  # Count pointer cannot be NULL
3579                 cvReq = 'true'  # Count value cannot be 0
3580                 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
3581                 #
3582                 # Generate required/optional parameter strings for the pointer and count values
3583                 if value.isoptional:
3584                     req = 'false'
3585                 if value.len:
3586                     # The parameter is an array with an explicit count parameter
3587                     lenParam = self.getLenParam(values, value.len)
3588                     lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
3589                     if lenParam.ispointer:
3590                         # Count parameters that are pointers are inout
3591                         if type(lenParam.isoptional) is list:
3592                             if lenParam.isoptional[0]:
3593                                 cpReq = 'false'
3594                             if lenParam.isoptional[1]:
3595                                 cvReq = 'false'
3596                         else:
3597                             if lenParam.isoptional:
3598                                 cpReq = 'false'
3599                     else:
3600                         if lenParam.isoptional:
3601                             cvReq = 'false'
3602                 #
3603                 # The parameter will not be processes when tagged as 'noautovalidity'
3604                 # For the pointer to struct case, the struct pointer will not be validated, but any
3605                 # members not tagged as 'noatuvalidity' will be validated
3606                 if value.noautovalidity:
3607                     # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3608                     self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3609                 else:
3610                     #
3611                     # If this is a pointer to a struct with an sType field, verify the type
3612                     if value.type in self.structTypes:
3613                         usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3614                     # 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
3615                     elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
3616                         usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3617                     elif value.type in self.flags and value.isconst:
3618                         usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3619                     elif value.isbool and value.isconst:
3620                         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))
3621                     elif value.israngedenum and value.isconst:
3622                         enumRange = self.enumRanges[value.type]
3623                         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))
3624                     elif value.name == 'pNext':
3625                         # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
3626                         if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
3627                             usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec)
3628                     else:
3629                         usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3630                     #
3631                     # If this is a pointer to a struct (input), see if it contains members that need to be checked
3632                     if value.type in self.validatedStructs and value.isconst:
3633                         usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
3634             # Non-pointer types
3635             else:
3636                 #
3637                 # The parameter will not be processes when tagged as 'noautovalidity'
3638                 # For the struct case, the struct type will not be validated, but any
3639                 # members not tagged as 'noatuvalidity' will be validated
3640                 if value.noautovalidity:
3641                     # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3642                     self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3643                 else:
3644                     if value.type in self.structTypes:
3645                         stype = self.structTypes[value.type]
3646                         usedLines.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false);\n'.format(
3647                             funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value, **postProcSpec))
3648                     elif value.type in self.handleTypes:
3649                         if not self.isHandleOptional(value, None):
3650                             usedLines.append('skipCall |= validate_required_handle(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
3651                     elif value.type in self.flags:
3652                         flagBitsName = value.type.replace('Flags', 'FlagBits')
3653                         if not flagBitsName in self.flagBits:
3654                             usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", {ppp}"{}"{pps}, {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix, **postProcSpec))
3655                         else:
3656                             flagsRequired = 'false' if value.isoptional else 'true'
3657                             allFlagsName = 'All' + flagBitsName
3658                             usedLines.append('skipCall |= validate_flags(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix, **postProcSpec))
3659                     elif value.isbool:
3660                         usedLines.append('skipCall |= validate_bool32(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
3661                     elif value.israngedenum:
3662                         enumRange = self.enumRanges[value.type]
3663                         usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name, **postProcSpec))
3664                     #
3665                     # If this is a struct, see if it contains members that need to be checked
3666                     if value.type in self.validatedStructs:
3667                         memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
3668                         memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
3669                         usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
3670             #
3671             # Append the parameter check to the function body for the current command
3672             if usedLines:
3673                 # Apply special conditional checks
3674                 if value.condition:
3675                     usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
3676                 lines += usedLines
3677             elif not value.iscount:
3678                 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3679                 # it is an array count, which is indirectly referenced for array valiadation.
3680                 unused.append(value.name)
3681         return lines, unused
3682     #
3683     # Generate the struct member check code from the captured data
3684     def processStructMemberData(self):
3685         indent = self.incIndent(None)
3686         for struct in self.structMembers:
3687             #
3688             # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
3689             lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
3690             if lines:
3691                 self.validatedStructs[struct.name] = lines
3692     #
3693     # Generate the command param check code from the captured data
3694     def processCmdData(self):
3695         indent = self.incIndent(None)
3696         for command in self.commands:
3697             # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
3698             startIndex = 0 if command.name == 'vkCreateInstance' else 1
3699             lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
3700             if lines:
3701                 cmdDef = self.getCmdDef(command) + '\n'
3702                 cmdDef += '{\n'
3703                 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
3704                 # processed by parameter_validation (except for vkCreateInstance, which does not have a
3705                 # handle as its first parameter)
3706                 if unused:
3707                     for name in unused:
3708                         cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
3709                     if len(unused) > 0:
3710                         cmdDef += '\n'
3711                 cmdDef += indent + 'bool skipCall = false;\n'
3712                 for line in lines:
3713                     cmdDef += '\n'
3714                     if type(line) is list:
3715                         for sub in line:
3716                             cmdDef += indent + sub
3717                     else:
3718                         cmdDef += indent + line
3719                 cmdDef += '\n'
3720                 cmdDef += indent + 'return skipCall;\n'
3721                 cmdDef += '}\n'
3722                 self.appendSection('command', cmdDef)