layers: Add binding between cmd buffer and bufferView
[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                             paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n'
2584                 else:
2585                     paramtype = param.find('type')
2586                     if paramtype is not None:
2587                         paramtype = paramtype.text
2588                     else:
2589                         paramtype = 'None'
2590                     if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects:
2591                         if self.paramIsArray(param) and ('pPipelines' != paramname.text):
2592                             paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n'
2593                             paramdecl += '        ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n'
2594                             paramdecl += '    }\n'
2595                         elif not self.paramIsPointer(param):
2596                             # Pointer params are often being created.
2597                             # They are not being read from.
2598                             paramdecl += '    ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n'
2599         explicitexternsyncparams = cmd.findall("param[@externsync]")
2600         if (explicitexternsyncparams is not None):
2601             for param in explicitexternsyncparams:
2602                 externsyncattrib = param.attrib.get('externsync')
2603                 paramname = param.find('name')
2604                 paramdecl += '    // Host access to '
2605                 if externsyncattrib == 'true':
2606                     if self.paramIsArray(param):
2607                         paramdecl += 'each member of ' + paramname.text
2608                     elif self.paramIsPointer(param):
2609                         paramdecl += 'the object referenced by ' + paramname.text
2610                     else:
2611                         paramdecl += paramname.text
2612                 else:
2613                     paramdecl += externsyncattrib
2614                 paramdecl += ' must be externally synchronized\n'
2615
2616         # Find and add any "implicit" parameters that are thread unsafe
2617         implicitexternsyncparams = cmd.find('implicitexternsyncparams')
2618         if (implicitexternsyncparams is not None):
2619             for elem in implicitexternsyncparams:
2620                 paramdecl += '    // '
2621                 paramdecl += elem.text
2622                 paramdecl += ' must be externally synchronized between host accesses\n'
2623
2624         if (paramdecl == ''):
2625             return None
2626         else:
2627             return paramdecl
2628     def beginFile(self, genOpts):
2629         OutputGenerator.beginFile(self, genOpts)
2630         # C-specific
2631         #
2632         # Multiple inclusion protection & C++ namespace.
2633         if (genOpts.protectFile and self.genOpts.filename):
2634             headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename))
2635             write('#ifndef', headerSym, file=self.outFile)
2636             write('#define', headerSym, '1', file=self.outFile)
2637             self.newline()
2638         write('namespace threading {', file=self.outFile)
2639         self.newline()
2640         #
2641         # User-supplied prefix text, if any (list of strings)
2642         if (genOpts.prefixText):
2643             for s in genOpts.prefixText:
2644                 write(s, file=self.outFile)
2645     def endFile(self):
2646         # C-specific
2647         # Finish C++ namespace and multiple inclusion protection
2648         self.newline()
2649         # record intercepted procedures
2650         write('// intercepts', file=self.outFile)
2651         write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile)
2652         write('\n'.join(self.intercepts), file=self.outFile)
2653         write('};\n', file=self.outFile)
2654         self.newline()
2655         write('} // namespace threading', file=self.outFile)
2656         if (self.genOpts.protectFile and self.genOpts.filename):
2657             self.newline()
2658             write('#endif', file=self.outFile)
2659         # Finish processing in superclass
2660         OutputGenerator.endFile(self)
2661     def beginFeature(self, interface, emit):
2662         #write('// starting beginFeature', file=self.outFile)
2663         # Start processing in superclass
2664         OutputGenerator.beginFeature(self, interface, emit)
2665         # C-specific
2666         # Accumulate includes, defines, types, enums, function pointer typedefs,
2667         # end function prototypes separately for this feature. They're only
2668         # printed in endFeature().
2669         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2670         #write('// ending beginFeature', file=self.outFile)
2671     def endFeature(self):
2672         # C-specific
2673         # Actually write the interface to the output file.
2674         #write('// starting endFeature', file=self.outFile)
2675         if (self.emit):
2676             self.newline()
2677             if (self.genOpts.protectFeature):
2678                 write('#ifndef', self.featureName, file=self.outFile)
2679             # If type declarations are needed by other features based on
2680             # this one, it may be necessary to suppress the ExtraProtect,
2681             # or move it below the 'for section...' loop.
2682             #write('// endFeature looking at self.featureExtraProtect', file=self.outFile)
2683             if (self.featureExtraProtect != None):
2684                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2685             #write('#define', self.featureName, '1', file=self.outFile)
2686             for section in self.TYPE_SECTIONS:
2687                 #write('// endFeature writing section'+section, file=self.outFile)
2688                 contents = self.sections[section]
2689                 if contents:
2690                     write('\n'.join(contents), file=self.outFile)
2691                     self.newline()
2692             #write('// endFeature looking at self.sections[command]', file=self.outFile)
2693             if (self.sections['command']):
2694                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
2695                 self.newline()
2696             if (self.featureExtraProtect != None):
2697                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
2698             if (self.genOpts.protectFeature):
2699                 write('#endif /*', self.featureName, '*/', file=self.outFile)
2700         # Finish processing in superclass
2701         OutputGenerator.endFeature(self)
2702         #write('// ending endFeature', file=self.outFile)
2703     #
2704     # Append a definition to the specified section
2705     def appendSection(self, section, text):
2706         # self.sections[section].append('SECTION: ' + section + '\n')
2707         self.sections[section].append(text)
2708     #
2709     # Type generation
2710     def genType(self, typeinfo, name):
2711         pass
2712     #
2713     # Struct (e.g. C "struct" type) generation.
2714     # This is a special case of the <type> tag where the contents are
2715     # interpreted as a set of <member> tags instead of freeform C
2716     # C type declarations. The <member> tags are just like <param>
2717     # tags - they are a declaration of a struct or union member.
2718     # Only simple member declarations are supported (no nested
2719     # structs etc.)
2720     def genStruct(self, typeinfo, typeName):
2721         OutputGenerator.genStruct(self, typeinfo, typeName)
2722         body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
2723         # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
2724         for member in typeinfo.elem.findall('.//member'):
2725             body += self.makeCParamDecl(member, self.genOpts.alignFuncParam)
2726             body += ';\n'
2727         body += '} ' + typeName + ';\n'
2728         self.appendSection('struct', body)
2729     #
2730     # Group (e.g. C "enum" type) generation.
2731     # These are concatenated together with other types.
2732     def genGroup(self, groupinfo, groupName):
2733         pass
2734     # Enumerant generation
2735     # <enum> tags may specify their values in several ways, but are usually
2736     # just integers.
2737     def genEnum(self, enuminfo, name):
2738         pass
2739     #
2740     # Command generation
2741     def genCmd(self, cmdinfo, name):
2742         # Commands shadowed by interface functions and are not implemented
2743         interface_functions = [
2744             'vkEnumerateInstanceLayerProperties',
2745             'vkEnumerateInstanceExtensionProperties',
2746             'vkEnumerateDeviceLayerProperties',
2747         ]
2748         if name in interface_functions:
2749             return
2750         special_functions = [
2751             'vkGetDeviceProcAddr',
2752             'vkGetInstanceProcAddr',
2753             'vkCreateDevice',
2754             'vkDestroyDevice',
2755             'vkCreateInstance',
2756             'vkDestroyInstance',
2757             'vkAllocateCommandBuffers',
2758             'vkFreeCommandBuffers',
2759             'vkCreateDebugReportCallbackEXT',
2760             'vkDestroyDebugReportCallbackEXT',
2761         ]
2762         if name in special_functions:
2763             decls = self.makeCDecls(cmdinfo.elem)
2764             self.appendSection('command', '')
2765             self.appendSection('command', '// declare only')
2766             self.appendSection('command', decls[0])
2767             self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
2768             return
2769         if "KHR" in name:
2770             self.appendSection('command', '// TODO - not wrapping KHR function ' + name)
2771             return
2772         if ("DebugMarker" in name) and ("EXT" in name):
2773             self.appendSection('command', '// TODO - not wrapping EXT function ' + name)
2774             return
2775         # Determine first if this function needs to be intercepted
2776         startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start')
2777         if startthreadsafety is None:
2778             return
2779         finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish')
2780         # record that the function will be intercepted
2781         if (self.featureExtraProtect != None):
2782             self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ]
2783         self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name[2:]) ]
2784         if (self.featureExtraProtect != None):
2785             self.intercepts += [ '#endif' ]
2786
2787         OutputGenerator.genCmd(self, cmdinfo, name)
2788         #
2789         decls = self.makeCDecls(cmdinfo.elem)
2790         self.appendSection('command', '')
2791         self.appendSection('command', decls[0][:-1])
2792         self.appendSection('command', '{')
2793         # setup common to call wrappers
2794         # first parameter is always dispatchable
2795         dispatchable_type = cmdinfo.elem.find('param/type').text
2796         dispatchable_name = cmdinfo.elem.find('param/name').text
2797         self.appendSection('command', '    dispatch_key key = get_dispatch_key('+dispatchable_name+');')
2798         self.appendSection('command', '    layer_data *my_data = get_my_data_ptr(key, layer_data_map);')
2799         if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]:
2800             self.appendSection('command', '    VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;')
2801         else:
2802             self.appendSection('command', '    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;')
2803         # Declare result variable, if any.
2804         resulttype = cmdinfo.elem.find('proto/type')
2805         if (resulttype != None and resulttype.text == 'void'):
2806           resulttype = None
2807         if (resulttype != None):
2808             self.appendSection('command', '    ' + resulttype.text + ' result;')
2809             assignresult = 'result = '
2810         else:
2811             assignresult = ''
2812
2813         self.appendSection('command', '    bool threadChecks = startMultiThread();')
2814         self.appendSection('command', '    if (threadChecks) {')
2815         self.appendSection('command', "    "+"\n    ".join(str(startthreadsafety).rstrip().split("\n")))
2816         self.appendSection('command', '    }')
2817         params = cmdinfo.elem.findall('param/name')
2818         paramstext = ','.join([str(param.text) for param in params])
2819         API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1)
2820         self.appendSection('command', '    ' + assignresult + API + '(' + paramstext + ');')
2821         self.appendSection('command', '    if (threadChecks) {')
2822         self.appendSection('command', "    "+"\n    ".join(str(finishthreadsafety).rstrip().split("\n")))
2823         self.appendSection('command', '    } else {')
2824         self.appendSection('command', '        finishMultiThread();')
2825         self.appendSection('command', '    }')
2826         # Return result variable, if any.
2827         if (resulttype != None):
2828             self.appendSection('command', '    return result;')
2829         self.appendSection('command', '}')
2830     #
2831     # override makeProtoName to drop the "vk" prefix
2832     def makeProtoName(self, name, tail):
2833         return self.genOpts.apientry + name[2:] + tail
2834
2835 # ParamCheckerOutputGenerator - subclass of OutputGenerator.
2836 # Generates param checker layer code.
2837 #
2838 # ---- methods ----
2839 # ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
2840 #   OutputGenerator. Defines additional internal state.
2841 # ---- methods overriding base class ----
2842 # beginFile(genOpts)
2843 # endFile()
2844 # beginFeature(interface, emit)
2845 # endFeature()
2846 # genType(typeinfo,name)
2847 # genStruct(typeinfo,name)
2848 # genGroup(groupinfo,name)
2849 # genEnum(enuminfo, name)
2850 # genCmd(cmdinfo)
2851 class ParamCheckerOutputGenerator(OutputGenerator):
2852     """Generate ParamChecker code based on XML element attributes"""
2853     # This is an ordered list of sections in the header file.
2854     ALL_SECTIONS = ['command']
2855     def __init__(self,
2856                  errFile = sys.stderr,
2857                  warnFile = sys.stderr,
2858                  diagFile = sys.stdout):
2859         OutputGenerator.__init__(self, errFile, warnFile, diagFile)
2860         self.INDENT_SPACES = 4
2861         # Commands to ignore
2862         self.blacklist = [
2863             'vkGetInstanceProcAddr',
2864             'vkGetDeviceProcAddr',
2865             'vkEnumerateInstanceLayerProperties',
2866             'vkEnumerateInstanceExtensionsProperties',
2867             'vkEnumerateDeviceLayerProperties',
2868             'vkEnumerateDeviceExtensionsProperties',
2869             'vkCreateDebugReportCallbackEXT',
2870             'vkDebugReportMessageEXT']
2871         # Validation conditions for some special case struct members that are conditionally validated
2872         self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
2873         # Header version
2874         self.headerVersion = None
2875         # Internal state - accumulators for different inner block text
2876         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2877         self.structNames = []                             # List of Vulkan struct typenames
2878         self.stypes = []                                  # Values from the VkStructureType enumeration
2879         self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType
2880         self.handleTypes = set()                          # Set of handle type names
2881         self.commands = []                                # List of CommandData records for all Vulkan commands
2882         self.structMembers = []                           # List of StructMemberData records for all Vulkan structs
2883         self.validatedStructs = dict()                    # Map of structs type names to generated validation code for that struct type
2884         self.enumRanges = dict()                          # Map of enum name to BEGIN/END range values
2885         self.flags = set()                                # Map of flags typenames
2886         self.flagBits = dict()                            # Map of flag bits typename to list of values
2887         # Named tuples to store struct and command data
2888         self.StructType = namedtuple('StructType', ['name', 'value'])
2889         self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
2890                                                         'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
2891                                                         'condition', 'cdecl'])
2892         self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
2893         self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
2894     #
2895     def incIndent(self, indent):
2896         inc = ' ' * self.INDENT_SPACES
2897         if indent:
2898             return indent + inc
2899         return inc
2900     #
2901     def decIndent(self, indent):
2902         if indent and (len(indent) > self.INDENT_SPACES):
2903             return indent[:-self.INDENT_SPACES]
2904         return ''
2905     #
2906     def beginFile(self, genOpts):
2907         OutputGenerator.beginFile(self, genOpts)
2908         # C-specific
2909         #
2910         # User-supplied prefix text, if any (list of strings)
2911         if (genOpts.prefixText):
2912             for s in genOpts.prefixText:
2913                 write(s, file=self.outFile)
2914         #
2915         # Multiple inclusion protection & C++ wrappers.
2916         if (genOpts.protectFile and self.genOpts.filename):
2917             headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
2918             write('#ifndef', headerSym, file=self.outFile)
2919             write('#define', headerSym, '1', file=self.outFile)
2920             self.newline()
2921         #
2922         # Headers
2923         write('#include <string>', file=self.outFile)
2924         self.newline()
2925         write('#include "vulkan/vulkan.h"', file=self.outFile)
2926         write('#include "vk_layer_extension_utils.h"', file=self.outFile)
2927         write('#include "parameter_validation_utils.h"', file=self.outFile)
2928         #
2929         # Macros
2930         self.newline()
2931         write('#ifndef UNUSED_PARAMETER', file=self.outFile)
2932         write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
2933         write('#endif // UNUSED_PARAMETER', file=self.outFile)
2934         #
2935         # Namespace
2936         self.newline()
2937         write('namespace parameter_validation {', file = self.outFile)
2938     def endFile(self):
2939         # C-specific
2940         self.newline()
2941         # Namespace
2942         write('} // namespace parameter_validation', file = self.outFile)
2943         # Finish C++ wrapper and multiple inclusion protection
2944         if (self.genOpts.protectFile and self.genOpts.filename):
2945             self.newline()
2946             write('#endif', file=self.outFile)
2947         # Finish processing in superclass
2948         OutputGenerator.endFile(self)
2949     def beginFeature(self, interface, emit):
2950         # Start processing in superclass
2951         OutputGenerator.beginFeature(self, interface, emit)
2952         # C-specific
2953         # Accumulate includes, defines, types, enums, function pointer typedefs,
2954         # end function prototypes separately for this feature. They're only
2955         # printed in endFeature().
2956         self.headerVersion = None
2957         self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
2958         self.structNames = []
2959         self.stypes = []
2960         self.structTypes = dict()
2961         self.handleTypes = set()
2962         self.commands = []
2963         self.structMembers = []
2964         self.validatedStructs = dict()
2965         self.enumRanges = dict()
2966         self.flags = set()
2967         self.flagBits = dict()
2968     def endFeature(self):
2969         # C-specific
2970         # Actually write the interface to the output file.
2971         if (self.emit):
2972             self.newline()
2973             # If type declarations are needed by other features based on
2974             # this one, it may be necessary to suppress the ExtraProtect,
2975             # or move it below the 'for section...' loop.
2976             if (self.featureExtraProtect != None):
2977                 write('#ifdef', self.featureExtraProtect, file=self.outFile)
2978             # Generate the struct member checking code from the captured data
2979             self.processStructMemberData()
2980             # Generate the command parameter checking code from the captured data
2981             self.processCmdData()
2982             # Write the declaration for the HeaderVersion
2983             if self.headerVersion:
2984                 write('const uint32_t GeneratedHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
2985                 self.newline()
2986             # Write the declarations for the VkFlags values combining all flag bits
2987             for flag in sorted(self.flags):
2988                 flagBits = flag.replace('Flags', 'FlagBits')
2989                 if flagBits in self.flagBits:
2990                     bits = self.flagBits[flagBits]
2991                     decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
2992                     for bit in bits[1:]:
2993                         decl += '|' + bit
2994                     decl += ';'
2995                     write(decl, file=self.outFile)
2996             self.newline()
2997             # Write the parameter validation code to the file
2998             if (self.sections['command']):
2999                 if (self.genOpts.protectProto):
3000                     write(self.genOpts.protectProto,
3001                           self.genOpts.protectProtoStr, file=self.outFile)
3002                 write('\n'.join(self.sections['command']), end='', file=self.outFile)
3003             if (self.featureExtraProtect != None):
3004                 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
3005             else:
3006                 self.newline()
3007         # Finish processing in superclass
3008         OutputGenerator.endFeature(self)
3009     #
3010     # Append a definition to the specified section
3011     def appendSection(self, section, text):
3012         # self.sections[section].append('SECTION: ' + section + '\n')
3013         self.sections[section].append(text)
3014     #
3015     # Type generation
3016     def genType(self, typeinfo, name):
3017         OutputGenerator.genType(self, typeinfo, name)
3018         typeElem = typeinfo.elem
3019         # If the type is a struct type, traverse the imbedded <member> tags
3020         # generating a structure. Otherwise, emit the tag text.
3021         category = typeElem.get('category')
3022         if (category == 'struct' or category == 'union'):
3023             self.structNames.append(name)
3024             self.genStruct(typeinfo, name)
3025         elif (category == 'handle'):
3026             self.handleTypes.add(name)
3027         elif (category == 'bitmask'):
3028             self.flags.add(name)
3029         elif (category == 'define'):
3030             if name == 'VK_HEADER_VERSION':
3031                 nameElem = typeElem.find('name')
3032                 self.headerVersion = noneStr(nameElem.tail).strip()
3033     #
3034     # Struct parameter check generation.
3035     # This is a special case of the <type> tag where the contents are
3036     # interpreted as a set of <member> tags instead of freeform C
3037     # C type declarations. The <member> tags are just like <param>
3038     # tags - they are a declaration of a struct or union member.
3039     # Only simple member declarations are supported (no nested
3040     # structs etc.)
3041     def genStruct(self, typeinfo, typeName):
3042         OutputGenerator.genStruct(self, typeinfo, typeName)
3043         conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
3044         members = typeinfo.elem.findall('.//member')
3045         #
3046         # Iterate over members once to get length parameters for arrays
3047         lens = set()
3048         for member in members:
3049             len = self.getLen(member)
3050             if len:
3051                 lens.add(len)
3052         #
3053         # Generate member info
3054         membersInfo = []
3055         for member in members:
3056             # Get the member's type and name
3057             info = self.getTypeNameTuple(member)
3058             type = info[0]
3059             name = info[1]
3060             stypeValue = ''
3061             cdecl = self.makeCParamDecl(member, 0)
3062             # Process VkStructureType
3063             if type == 'VkStructureType':
3064                 # Extract the required struct type value from the comments
3065                 # embedded in the original text defining the 'typeinfo' element
3066                 rawXml = etree.tostring(typeinfo.elem).decode('ascii')
3067                 result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
3068                 if result:
3069                     value = result.group(0)
3070                 else:
3071                     value = self.genVkStructureType(typeName)
3072                 # Store the required type value
3073                 self.structTypes[typeName] = self.StructType(name=name, value=value)
3074             #
3075             # Store pointer/array/string info
3076             # Check for parameter name in lens set
3077             iscount = False
3078             if name in lens:
3079                 iscount = True
3080             # The pNext members are not tagged as optional, but are treated as
3081             # optional for parameter NULL checks.  Static array members
3082             # are also treated as optional to skip NULL pointer validation, as
3083             # they won't be NULL.
3084             isstaticarray = self.paramIsStaticArray(member)
3085             isoptional = False
3086             if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
3087                 isoptional = True
3088             membersInfo.append(self.CommandParam(type=type, name=name,
3089                                                 ispointer=self.paramIsPointer(member),
3090                                                 isstaticarray=isstaticarray,
3091                                                 isbool=True if type == 'VkBool32' else False,
3092                                                 israngedenum=True if type in self.enumRanges else False,
3093                                                 isconst=True if 'const' in cdecl else False,
3094                                                 isoptional=isoptional,
3095                                                 iscount=iscount,
3096                                                 noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
3097                                                 len=self.getLen(member),
3098                                                 extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
3099                                                 condition=conditions[name] if conditions and name in conditions else None,
3100                                                 cdecl=cdecl))
3101         self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
3102     #
3103     # Capture group (e.g. C "enum" type) info to be used for
3104     # param check code generation.
3105     # These are concatenated together with other types.
3106     def genGroup(self, groupinfo, groupName):
3107         OutputGenerator.genGroup(self, groupinfo, groupName)
3108         groupElem = groupinfo.elem
3109         #
3110         # Store the sType values
3111         if groupName == 'VkStructureType':
3112             for elem in groupElem.findall('enum'):
3113                 self.stypes.append(elem.get('name'))
3114         elif 'FlagBits' in groupName:
3115             bits = []
3116             for elem in groupElem.findall('enum'):
3117                 bits.append(elem.get('name'))
3118             if bits:
3119                 self.flagBits[groupName] = bits
3120         else:
3121             # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
3122             expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
3123             expandPrefix = expandName
3124             expandSuffix = ''
3125             expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
3126             if expandSuffixMatch:
3127                 expandSuffix = '_' + expandSuffixMatch.group()
3128                 # Strip off the suffix from the prefix
3129                 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
3130             isEnum = ('FLAG_BITS' not in expandPrefix)
3131             if isEnum:
3132                 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
3133     #
3134     # Capture command parameter info to be used for param
3135     # check code generation.
3136     def genCmd(self, cmdinfo, name):
3137         OutputGenerator.genCmd(self, cmdinfo, name)
3138         if name not in self.blacklist:
3139             params = cmdinfo.elem.findall('param')
3140             # Get list of array lengths
3141             lens = set()
3142             for param in params:
3143                 len = self.getLen(param)
3144                 if len:
3145                     lens.add(len)
3146             # Get param info
3147             paramsInfo = []
3148             for param in params:
3149                 paramInfo = self.getTypeNameTuple(param)
3150                 cdecl = self.makeCParamDecl(param, 0)
3151                 # Check for parameter name in lens set
3152                 iscount = False
3153                 if paramInfo[1] in lens:
3154                     iscount = True
3155                 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
3156                                                     ispointer=self.paramIsPointer(param),
3157                                                     isstaticarray=self.paramIsStaticArray(param),
3158                                                     isbool=True if paramInfo[0] == 'VkBool32' else False,
3159                                                     israngedenum=True if paramInfo[0] in self.enumRanges else False,
3160                                                     isconst=True if 'const' in cdecl else False,
3161                                                     isoptional=self.paramIsOptional(param),
3162                                                     iscount=iscount,
3163                                                     noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
3164                                                     len=self.getLen(param),
3165                                                     extstructs=None,
3166                                                     condition=None,
3167                                                     cdecl=cdecl))
3168             self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
3169     #
3170     # Check if the parameter passed in is a pointer
3171     def paramIsPointer(self, param):
3172         ispointer = 0
3173         paramtype = param.find('type')
3174         if (paramtype.tail is not None) and ('*' in paramtype.tail):
3175             ispointer = paramtype.tail.count('*')
3176         elif paramtype.text[:4] == 'PFN_':
3177             # Treat function pointer typedefs as a pointer to a single value
3178             ispointer = 1
3179         return ispointer
3180     #
3181     # Check if the parameter passed in is a static array
3182     def paramIsStaticArray(self, param):
3183         isstaticarray = 0
3184         paramname = param.find('name')
3185         if (paramname.tail is not None) and ('[' in paramname.tail):
3186             isstaticarray = paramname.tail.count('[')
3187         return isstaticarray
3188     #
3189     # Check if the parameter passed in is optional
3190     # Returns a list of Boolean values for comma separated len attributes (len='false,true')
3191     def paramIsOptional(self, param):
3192         # See if the handle is optional
3193         isoptional = False
3194         # Simple, if it's optional, return true
3195         optString = param.attrib.get('optional')
3196         if optString:
3197             if optString == 'true':
3198                 isoptional = True
3199             elif ',' in optString:
3200                 opts = []
3201                 for opt in optString.split(','):
3202                     val = opt.strip()
3203                     if val == 'true':
3204                         opts.append(True)
3205                     elif val == 'false':
3206                         opts.append(False)
3207                     else:
3208                         print('Unrecognized len attribute value',val)
3209                 isoptional = opts
3210         return isoptional
3211     #
3212     # Check if the handle passed in is optional
3213     # Uses the same logic as ValidityOutputGenerator.isHandleOptional
3214     def isHandleOptional(self, param, lenParam):
3215         # Simple, if it's optional, return true
3216         if param.isoptional:
3217             return True
3218         # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
3219         if param.noautovalidity:
3220             return True
3221         # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
3222         if lenParam and lenParam.isoptional:
3223             return True
3224         return False
3225     #
3226     # Generate a VkStructureType based on a structure typename
3227     def genVkStructureType(self, typename):
3228         # Add underscore between lowercase then uppercase
3229         value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
3230         # Change to uppercase
3231         value = value.upper()
3232         # Add STRUCTURE_TYPE_
3233         return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
3234     #
3235     # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
3236     # value assuming the struct is defined by a different feature
3237     def getStructType(self, typename):
3238         value = None
3239         if typename in self.structTypes:
3240             value = self.structTypes[typename].value
3241         else:
3242             value = self.genVkStructureType(typename)
3243             self.logMsg('diag', 'ParameterValidation: Generating {} for {} structure type that was not defined by the current feature'.format(value, typename))
3244         return value
3245     #
3246     # Retrieve the value of the len tag
3247     def getLen(self, param):
3248         result = None
3249         len = param.attrib.get('len')
3250         if len and len != 'null-terminated':
3251             # For string arrays, 'len' can look like 'count,null-terminated',
3252             # indicating that we have a null terminated array of strings.  We
3253             # strip the null-terminated from the 'len' field and only return
3254             # the parameter specifying the string count
3255             if 'null-terminated' in len:
3256                 result = len.split(',')[0]
3257             else:
3258                 result = len
3259         return result
3260     #
3261     # Retrieve the type and name for a parameter
3262     def getTypeNameTuple(self, param):
3263         type = ''
3264         name = ''
3265         for elem in param:
3266             if elem.tag == 'type':
3267                 type = noneStr(elem.text)
3268             elif elem.tag == 'name':
3269                 name = noneStr(elem.text)
3270         return (type, name)
3271     #
3272     # Find a named parameter in a parameter list
3273     def getParamByName(self, params, name):
3274         for param in params:
3275             if param.name == name:
3276                 return param
3277         return None
3278     #
3279     # Extract length values from latexmath.  Currently an inflexible solution that looks for specific
3280     # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced.
3281     def parseLateXMath(self, source):
3282         name = 'ERROR'
3283         decoratedName = 'ERROR'
3284         if 'mathit' in source:
3285             # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
3286             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)
3287             if not match or match.group(1) != match.group(4):
3288                 raise 'Unrecognized latexmath expression'
3289             name = match.group(2)
3290             decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
3291         else:
3292             # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
3293             match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
3294             name = match.group(1)
3295             decoratedName = '{}/{}'.format(*match.group(1, 2))
3296         return name, decoratedName
3297     #
3298     # Get the length paramater record for the specified parameter name
3299     def getLenParam(self, params, name):
3300         lenParam = None
3301         if name:
3302             if '->' in name:
3303                 # The count is obtained by dereferencing a member of a struct parameter
3304                 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
3305                                              isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
3306                                              condition=None, cdecl=None)
3307             elif 'latexmath' in name:
3308                 lenName, decoratedName = self.parseLateXMath(name)
3309                 lenParam = self.getParamByName(params, lenName)
3310                 # TODO: Zero-check the result produced by the equation?
3311                 # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
3312                 #param = self.getParamByName(params, lenName)
3313                 #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
3314                 #                             isoptional=param.isoptional, type=param.type, len=param.len,
3315                 #                             isstaticarray=param.isstaticarray, extstructs=param.extstructs,
3316                 #                             noautovalidity=True, condition=None, cdecl=param.cdecl)
3317             else:
3318                 lenParam = self.getParamByName(params, name)
3319         return lenParam
3320     #
3321     # Convert a vulkan.h command declaration into a parameter_validation.h definition
3322     def getCmdDef(self, cmd):
3323         #
3324         # Strip the trailing ';' and split into individual lines
3325         lines = cmd.cdecl[:-1].split('\n')
3326         # Replace Vulkan prototype
3327         lines[0] = 'static bool parameter_validation_' + cmd.name + '('
3328         # Replace the first argument with debug_report_data, when the first
3329         # argument is a handle (not vkCreateInstance)
3330         reportData = '    debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
3331         if cmd.name != 'vkCreateInstance':
3332             lines[1] = reportData
3333         else:
3334             lines.insert(1, reportData)
3335         return '\n'.join(lines)
3336     #
3337     # Generate the code to check for a NULL dereference before calling the
3338     # validation function
3339     def genCheckedLengthCall(self, name, exprs):
3340         count = name.count('->')
3341         if count:
3342             checkedExpr = []
3343             localIndent = ''
3344             elements = name.split('->')
3345             # Open the if expression blocks
3346             for i in range(0, count):
3347                 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
3348                 localIndent = self.incIndent(localIndent)
3349             # Add the validation expression
3350             for expr in exprs:
3351                 checkedExpr.append(localIndent + expr)
3352             # Close the if blocks
3353             for i in range(0, count):
3354                 localIndent = self.decIndent(localIndent)
3355                 checkedExpr.append(localIndent + '}\n')
3356             return [checkedExpr]
3357         # No if statements were required
3358         return exprs
3359     #
3360     # Generate code to check for a specific condition before executing validation code
3361     def genConditionalCall(self, prefix, condition, exprs):
3362         checkedExpr = []
3363         localIndent = ''
3364         formattedCondition = condition.format(prefix)
3365         checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
3366         checkedExpr.append(localIndent + '{\n')
3367         localIndent = self.incIndent(localIndent)
3368         for expr in exprs:
3369             checkedExpr.append(localIndent + expr)
3370         localIndent = self.decIndent(localIndent)
3371         checkedExpr.append(localIndent + '}\n')
3372         return [checkedExpr]
3373     #
3374     # Generate the sType check string
3375     def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3376         checkExpr = []
3377         stype = self.structTypes[value.type]
3378         if lenValue:
3379             # This is an array with a pointer to a count value
3380             if lenValue.ispointer:
3381                 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
3382                 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
3383                     funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
3384             # This is an array with an integer count value
3385             else:
3386                 checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
3387                     funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
3388         # This is an individual struct
3389         else:
3390             checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {});\n'.format(
3391                 funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value, **postProcSpec))
3392         return checkExpr
3393     #
3394     # Generate the handle check string
3395     def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3396         checkExpr = []
3397         if lenValue:
3398             if lenValue.ispointer:
3399                 # This is assumed to be an output array with a pointer to a count value
3400                 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
3401             else:
3402                 # This is an array with an integer count value
3403                 checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3404                     funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3405         else:
3406             # This is assumed to be an output handle pointer
3407             raise('Unsupported parameter validation case: Output handles are not NULL checked')
3408         return checkExpr
3409     #
3410     # Generate check string for an array of VkFlags values
3411     def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3412         checkExpr = []
3413         flagBitsName = value.type.replace('Flags', 'FlagBits')
3414         if not flagBitsName in self.flagBits:
3415             raise('Unsupported parameter validation case: array of reserved VkFlags')
3416         else:
3417             allFlags = 'All' + flagBitsName
3418             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))
3419         return checkExpr
3420     #
3421     # Generate pNext check string
3422     def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec):
3423         checkExpr = []
3424         # Generate an array of acceptable VkStructureType values for pNext
3425         extStructCount = 0
3426         extStructVar = 'NULL'
3427         extStructNames = 'NULL'
3428         if value.extstructs:
3429             structs = value.extstructs.split(',')
3430             checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
3431             extStructCount = 'ARRAY_SIZE(allowedStructs)'
3432             extStructVar = 'allowedStructs'
3433             extStructNames = '"' + ', '.join(structs) + '"'
3434         checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion);\n'.format(
3435             funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, **postProcSpec))
3436         return checkExpr
3437     #
3438     # Generate the pointer check string
3439     def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
3440         checkExpr = []
3441         if lenValue:
3442             # This is an array with a pointer to a count value
3443             if lenValue.ispointer:
3444                 # If count and array parameters are optional, there will be no validation
3445                 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
3446                     # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
3447                     checkExpr.append('skipCall |= validate_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
3448                         funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3449             # This is an array with an integer count value
3450             else:
3451                 # If count and array parameters are optional, there will be no validation
3452                 if valueRequired == 'true' or lenValueRequired == 'true':
3453                     # Arrays of strings receive special processing
3454                     validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
3455                     checkExpr.append('skipCall |= {}(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
3456                         validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
3457             if checkExpr:
3458                 if lenValue and ('->' in lenValue.name):
3459                     # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
3460                     checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
3461         # This is an individual struct that is not allowed to be NULL
3462         elif not value.isoptional:
3463             # Function pointers need a reinterpret_cast to void*
3464             if value.type[:4] == 'PFN_':
3465                 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
3466             else:
3467                 checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
3468         return checkExpr
3469     #
3470     # Process struct member validation code, performing name suibstitution if required
3471     def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
3472         # Build format specifier list
3473         kwargs = {}
3474         if '{postProcPrefix}' in line:
3475             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3476             if type(memberDisplayNamePrefix) is tuple:
3477                 kwargs['postProcPrefix'] = 'ParameterName('
3478             else:
3479                 kwargs['postProcPrefix'] = postProcSpec['ppp']
3480         if '{postProcSuffix}' in line:
3481             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3482             if type(memberDisplayNamePrefix) is tuple:
3483                 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3484             else:
3485                 kwargs['postProcSuffix'] = postProcSpec['pps']
3486         if '{postProcInsert}' in line:
3487             # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
3488             if type(memberDisplayNamePrefix) is tuple:
3489                 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
3490             else:
3491                 kwargs['postProcInsert'] = postProcSpec['ppi']
3492         if '{funcName}' in line:
3493             kwargs['funcName'] = funcName
3494         if '{valuePrefix}' in line:
3495             kwargs['valuePrefix'] = memberNamePrefix
3496         if '{displayNamePrefix}' in line:
3497             # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
3498             if type(memberDisplayNamePrefix) is tuple:
3499                 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
3500             else:
3501                 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
3502
3503         if kwargs:
3504             # Need to escape the C++ curly braces
3505             if 'IndexVector' in line:
3506                 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
3507                 line = line.replace(' }),', ' }}),')
3508             return line.format(**kwargs)
3509         return line
3510     #
3511     # Process struct validation code for inclusion in function or parent struct validation code
3512     def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
3513         for line in lines:
3514             if output:
3515                 output[-1] += '\n'
3516             if type(line) is list:
3517                 for sub in line:
3518                     output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
3519             else:
3520                 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
3521         return output
3522     #
3523     # Process struct pointer/array validation code, perfoeming name substitution if required
3524     def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
3525         expr = []
3526         expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
3527         expr.append('{')
3528         indent = self.incIndent(None)
3529         if lenValue:
3530             # Need to process all elements in the array
3531             indexName = lenValue.name.replace('Count', 'Index')
3532             expr[-1] += '\n'
3533             expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
3534             expr.append(indent + '{')
3535             indent = self.incIndent(indent)
3536             # Prefix for value name to display in error message
3537             memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
3538             memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
3539         else:
3540             memberNamePrefix = '{}{}->'.format(prefix, value.name)
3541             memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
3542         #
3543         # Expand the struct validation lines
3544         expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
3545         #
3546         if lenValue:
3547             # Close if and for scopes
3548             indent = self.decIndent(indent)
3549             expr.append(indent + '}\n')
3550         expr.append('}\n')
3551         return expr
3552     #
3553     # Generate the parameter checking code
3554     def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
3555         lines = []    # Generated lines of code
3556         unused = []   # Unused variable names
3557         for value in values:
3558             usedLines = []
3559             lenParam = None
3560             #
3561             # 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.
3562             postProcSpec = {}
3563             postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
3564             postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
3565             postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
3566             #
3567             # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
3568             valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
3569             #
3570             # Check for NULL pointers, ignore the inout count parameters that
3571             # will be validated with their associated array
3572             if (value.ispointer or value.isstaticarray) and not value.iscount:
3573                 #
3574                 # Parameters for function argument generation
3575                 req = 'true'    # Paramerter cannot be NULL
3576                 cpReq = 'true'  # Count pointer cannot be NULL
3577                 cvReq = 'true'  # Count value cannot be 0
3578                 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
3579                 #
3580                 # Generate required/optional parameter strings for the pointer and count values
3581                 if value.isoptional:
3582                     req = 'false'
3583                 if value.len:
3584                     # The parameter is an array with an explicit count parameter
3585                     lenParam = self.getLenParam(values, value.len)
3586                     lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
3587                     if lenParam.ispointer:
3588                         # Count parameters that are pointers are inout
3589                         if type(lenParam.isoptional) is list:
3590                             if lenParam.isoptional[0]:
3591                                 cpReq = 'false'
3592                             if lenParam.isoptional[1]:
3593                                 cvReq = 'false'
3594                         else:
3595                             if lenParam.isoptional:
3596                                 cpReq = 'false'
3597                     else:
3598                         if lenParam.isoptional:
3599                             cvReq = 'false'
3600                 #
3601                 # The parameter will not be processes when tagged as 'noautovalidity'
3602                 # For the pointer to struct case, the struct pointer will not be validated, but any
3603                 # members not tagged as 'noatuvalidity' will be validated
3604                 if value.noautovalidity:
3605                     # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3606                     self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3607                 else:
3608                     #
3609                     # If this is a pointer to a struct with an sType field, verify the type
3610                     if value.type in self.structTypes:
3611                         usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3612                     # 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
3613                     elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
3614                         usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3615                     elif value.type in self.flags and value.isconst:
3616                         usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3617                     elif value.isbool and value.isconst:
3618                         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))
3619                     elif value.israngedenum and value.isconst:
3620                         enumRange = self.enumRanges[value.type]
3621                         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))
3622                     elif value.name == 'pNext':
3623                         # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
3624                         if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
3625                             usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec)
3626                     else:
3627                         usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
3628                     #
3629                     # If this is a pointer to a struct (input), see if it contains members that need to be checked
3630                     if value.type in self.validatedStructs and value.isconst:
3631                         usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
3632             # Non-pointer types
3633             else:
3634                 #
3635                 # The parameter will not be processes when tagged as 'noautovalidity'
3636                 # For the struct case, the struct type will not be validated, but any
3637                 # members not tagged as 'noatuvalidity' will be validated
3638                 if value.noautovalidity:
3639                     # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
3640                     self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
3641                 else:
3642                     if value.type in self.structTypes:
3643                         stype = self.structTypes[value.type]
3644                         usedLines.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false);\n'.format(
3645                             funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value, **postProcSpec))
3646                     elif value.type in self.handleTypes:
3647                         if not self.isHandleOptional(value, None):
3648                             usedLines.append('skipCall |= validate_required_handle(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
3649                     elif value.type in self.flags:
3650                         flagBitsName = value.type.replace('Flags', 'FlagBits')
3651                         if not flagBitsName in self.flagBits:
3652                             usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", {ppp}"{}"{pps}, {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix, **postProcSpec))
3653                         else:
3654                             flagsRequired = 'false' if value.isoptional else 'true'
3655                             allFlagsName = 'All' + flagBitsName
3656                             usedLines.append('skipCall |= validate_flags(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix, **postProcSpec))
3657                     elif value.isbool:
3658                         usedLines.append('skipCall |= validate_bool32(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
3659                     elif value.israngedenum:
3660                         enumRange = self.enumRanges[value.type]
3661                         usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name, **postProcSpec))
3662                     #
3663                     # If this is a struct, see if it contains members that need to be checked
3664                     if value.type in self.validatedStructs:
3665                         memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
3666                         memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
3667                         usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
3668             #
3669             # Append the parameter check to the function body for the current command
3670             if usedLines:
3671                 # Apply special conditional checks
3672                 if value.condition:
3673                     usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
3674                 lines += usedLines
3675             elif not value.iscount:
3676                 # If no expression was generated for this value, it is unreferenced by the validation function, unless
3677                 # it is an array count, which is indirectly referenced for array valiadation.
3678                 unused.append(value.name)
3679         return lines, unused
3680     #
3681     # Generate the struct member check code from the captured data
3682     def processStructMemberData(self):
3683         indent = self.incIndent(None)
3684         for struct in self.structMembers:
3685             #
3686             # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
3687             lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
3688             if lines:
3689                 self.validatedStructs[struct.name] = lines
3690     #
3691     # Generate the command param check code from the captured data
3692     def processCmdData(self):
3693         indent = self.incIndent(None)
3694         for command in self.commands:
3695             # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
3696             startIndex = 0 if command.name == 'vkCreateInstance' else 1
3697             lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
3698             if lines:
3699                 cmdDef = self.getCmdDef(command) + '\n'
3700                 cmdDef += '{\n'
3701                 # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
3702                 # processed by parameter_validation (except for vkCreateInstance, which does not have a
3703                 # handle as its first parameter)
3704                 if unused:
3705                     for name in unused:
3706                         cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
3707                     if len(unused) > 0:
3708                         cmdDef += '\n'
3709                 cmdDef += indent + 'bool skipCall = false;\n'
3710                 for line in lines:
3711                     cmdDef += '\n'
3712                     if type(line) is list:
3713                         for sub in line:
3714                             cmdDef += indent + sub
3715                     else:
3716                         cmdDef += indent + line
3717                 cmdDef += '\n'
3718                 cmdDef += indent + 'return skipCall;\n'
3719                 cmdDef += '}\n'
3720                 self.appendSection('command', cmdDef)